UI elements using the new structs #8
@@ -42,20 +42,24 @@ jobs:
 | 
				
			|||||||
    - run: flutter pub get
 | 
					    - run: flutter pub get
 | 
				
			||||||
      working-directory: ./frontend
 | 
					      working-directory: ./frontend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - run: flutter build apk --release --split-per-abi
 | 
					    - name: Add required secrets
 | 
				
			||||||
 | 
					      run: |
 | 
				
			||||||
 | 
					        echo ${{ secrets.ANDROID_SECRETS_BASE64 }} | base64 -d > ./android/secrets.properties
 | 
				
			||||||
      working-directory: ./frontend
 | 
					      working-directory: ./frontend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    - name: Release APK
 | 
					    - name: Sanity check
 | 
				
			||||||
      uses: https://gitea.com/akkuman/gitea-release-action@v1
 | 
					      run: |
 | 
				
			||||||
 | 
					        ls
 | 
				
			||||||
 | 
					        ls -lah android
 | 
				
			||||||
 | 
					      working-directory: ./frontend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - run: flutter build apk --release --split-per-abi --build-number=${{ gitea.run_number }}
 | 
				
			||||||
 | 
					      working-directory: ./frontend
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - name: Upload APKs to artifacts
 | 
				
			||||||
 | 
					      uses: https://gitea.com/actions/upload-artifact@v3
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        files: ./frontend/build/app/outputs/flutter-apk/*.apk
 | 
					        name: app-release
 | 
				
			||||||
        name: Testing release
 | 
					        path: frontend/build/app/outputs/flutter-apk/
 | 
				
			||||||
        release_name: testing
 | 
					        if-no-files-found: error
 | 
				
			||||||
        tag: testing
 | 
					        retention-days: 15
 | 
				
			||||||
        tag_name: testing
 | 
					 | 
				
			||||||
        release_body: "This is a testing release."
 | 
					 | 
				
			||||||
        prerelease: true
 | 
					 | 
				
			||||||
        token: ${{ secrets.GITEA_TOKEN }}
 | 
					 | 
				
			||||||
      env:
 | 
					 | 
				
			||||||
        NODE_OPTIONS: '--experimental-fetch'
 | 
					 | 
				
			||||||
      
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								frontend/android/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								frontend/android/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -4,6 +4,7 @@ gradle-wrapper.jar
 | 
				
			|||||||
/gradlew
 | 
					/gradlew
 | 
				
			||||||
/gradlew.bat
 | 
					/gradlew.bat
 | 
				
			||||||
/local.properties
 | 
					/local.properties
 | 
				
			||||||
 | 
					/secrets.properties
 | 
				
			||||||
GeneratedPluginRegistrant.java
 | 
					GeneratedPluginRegistrant.java
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Remember to never publicly share your keystore.
 | 
					# Remember to never publicly share your keystore.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										48
									
								
								frontend/android/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								frontend/android/README.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					## Android Setup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Keystore setup
 | 
				
			||||||
 | 
					```bash
 | 
				
			||||||
 | 
					keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias release
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					- This is required to store local credentials securely (not used for now).
 | 
				
			||||||
 | 
					- But necesseary in order to restrict the particular api key to a particular app (through the sha1 of the associated keystore).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Building and secret credentials
 | 
				
			||||||
 | 
					Following the guide under [https://developers.google.com/maps/flutter-package/config#android_1](https://developers.google.com/maps/flutter-package/config#android_1).
 | 
				
			||||||
 | 
					- Add the following to `android/build.gradle`:
 | 
				
			||||||
 | 
					    ```gradle
 | 
				
			||||||
 | 
					    buildscript {
 | 
				
			||||||
 | 
					        dependencies {
 | 
				
			||||||
 | 
					            classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					- Add the following to `android/app/build.gradle`:
 | 
				
			||||||
 | 
					    ```gradle
 | 
				
			||||||
 | 
					    plugins {
 | 
				
			||||||
 | 
					        // ...
 | 
				
			||||||
 | 
					        id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					- Add the credentials to `android/secrets.properties`:
 | 
				
			||||||
 | 
					    ```properties
 | 
				
			||||||
 | 
					    MAPS_API_KEY=YOUR_API_KEY
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					- Reference the credentials in `android/app/src/main/AndroidManifest.xml`:
 | 
				
			||||||
 | 
					    ```xml
 | 
				
			||||||
 | 
					    <meta-data
 | 
				
			||||||
 | 
					        android:name="com.google.android.geo.API_KEY"
 | 
				
			||||||
 | 
					        android:value="${MAPS_API_KEY}" />
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Using the credentials in CI
 | 
				
			||||||
 | 
					- Add the base64 encoded credentials to the repository secrets (e.g. `ANDROID_SECRETS`).
 | 
				
			||||||
 | 
					    ```bash
 | 
				
			||||||
 | 
					    base64 -i android/secrets.properties
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					- Use the following in the CI script:
 | 
				
			||||||
 | 
					    ```bash
 | 
				
			||||||
 | 
					    echo {{ secrets.ANDROID_SECRETS }} | base64 -d > android/secrets.properties
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
@@ -2,14 +2,18 @@ plugins {
 | 
				
			|||||||
    id "com.android.application"
 | 
					    id "com.android.application"
 | 
				
			||||||
    id "kotlin-android"
 | 
					    id "kotlin-android"
 | 
				
			||||||
    id "dev.flutter.flutter-gradle-plugin"
 | 
					    id "dev.flutter.flutter-gradle-plugin"
 | 
				
			||||||
 | 
					    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def localProperties = new Properties()
 | 
					def localProperties = new Properties()
 | 
				
			||||||
def localPropertiesFile = rootProject.file('local.properties')
 | 
					def localPropertiesFile = rootProject.file('secrets.properties')
 | 
				
			||||||
if (localPropertiesFile.exists()) {
 | 
					if (localPropertiesFile.exists()) {
 | 
				
			||||||
    localPropertiesFile.withReader('UTF-8') { reader ->
 | 
					    localPropertiesFile.withReader('UTF-8') { reader ->
 | 
				
			||||||
        localProperties.load(reader)
 | 
					        localProperties.load(reader)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					} else {
 | 
				
			||||||
 | 
					    throw new GradleException("Secrets file secrets.properties not found")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
 | 
					def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
 | 
				
			||||||
@@ -52,6 +56,9 @@ android {
 | 
				
			|||||||
        targetSdkVersion flutter.targetSdkVersion
 | 
					        targetSdkVersion flutter.targetSdkVersion
 | 
				
			||||||
        versionCode flutterVersionCode.toInteger()
 | 
					        versionCode flutterVersionCode.toInteger()
 | 
				
			||||||
        versionName flutterVersionName
 | 
					        versionName flutterVersionName
 | 
				
			||||||
 | 
					        // Placeholders of keys that are replaced by the build system.
 | 
				
			||||||
 | 
					        manifestPlaceholders += [MAPS_API_KEY: "some value"]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buildTypes {
 | 
					    buildTypes {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,7 @@
 | 
				
			|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 | 
					<manifest xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <!-- Required to fetch data from the internet. -->
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.INTERNET"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <application
 | 
					    <application
 | 
				
			||||||
        android:label="fast_network_navigation"
 | 
					        android:label="fast_network_navigation"
 | 
				
			||||||
        android:name="${applicationName}"
 | 
					        android:name="${applicationName}"
 | 
				
			||||||
@@ -32,11 +35,10 @@
 | 
				
			|||||||
        />
 | 
					        />
 | 
				
			||||||
        <meta-data
 | 
					        <meta-data
 | 
				
			||||||
            android:name="com.google.android.geo.API_KEY"
 | 
					            android:name="com.google.android.geo.API_KEY"
 | 
				
			||||||
            android:value="AIzaSyCeWk_D2xvfOHLidvV56EZeQCUybypEntw"
 | 
					            android:value="${MAPS_API_KEY}"
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    </application>
 | 
					    </application>
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    <!-- Required to query activities that can process text, see:
 | 
					    <!-- Required to query activities that can process text, see:
 | 
				
			||||||
         https://developer.android.com/training/package-visibility?hl=en and
 | 
					         https://developer.android.com/training/package-visibility?hl=en and
 | 
				
			||||||
         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
 | 
					         https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
 | 
				
			||||||
@@ -48,7 +50,4 @@
 | 
				
			|||||||
            <data android:mimeType="text/plain"/>
 | 
					            <data android:mimeType="text/plain"/>
 | 
				
			||||||
        </intent>
 | 
					        </intent>
 | 
				
			||||||
    </queries>
 | 
					    </queries>
 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- Required to fetch data from the internet. -->
 | 
					 | 
				
			||||||
    <uses-permission android:name="android.permission.INTERNET"/>
 | 
					 | 
				
			||||||
</manifest>
 | 
					</manifest>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,3 +16,14 @@ subprojects {
 | 
				
			|||||||
tasks.register("clean", Delete) {
 | 
					tasks.register("clean", Delete) {
 | 
				
			||||||
    delete rootProject.buildDir
 | 
					    delete rootProject.buildDir
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					buildscript {
 | 
				
			||||||
 | 
					    repositories {
 | 
				
			||||||
 | 
					        google()
 | 
				
			||||||
 | 
					        mavenCentral()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies {
 | 
				
			||||||
 | 
					        classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										1
									
								
								frontend/android/fallback.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								frontend/android/fallback.properties
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					MAPS_API_KEY=Key
 | 
				
			||||||
@@ -30,38 +30,38 @@ class _LandmarkCardState extends State<LandmarkCard> {
 | 
				
			|||||||
                bottomLeft: Radius.circular(15.0),
 | 
					                bottomLeft: Radius.circular(15.0),
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
              image: DecorationImage(
 | 
					              image: DecorationImage(
 | 
				
			||||||
                image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg'),
 | 
					                image: NetworkImage(widget.landmark.imageURL),
 | 
				
			||||||
                fit: BoxFit.cover,
 | 
					                fit: BoxFit.cover,
 | 
				
			||||||
              ),
 | 
					              ),
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
          Padding(
 | 
					          Padding(
 | 
				
			||||||
            padding: EdgeInsets.all(10),
 | 
					            padding: EdgeInsets.all(10),
 | 
				
			||||||
            child: Expanded(
 | 
					            
 | 
				
			||||||
              child: Column(
 | 
					            child: Column(
 | 
				
			||||||
                crossAxisAlignment: CrossAxisAlignment.start,
 | 
					              crossAxisAlignment: CrossAxisAlignment.start,
 | 
				
			||||||
                children: [
 | 
					              children: [
 | 
				
			||||||
                  Row(
 | 
					                Row(
 | 
				
			||||||
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
					                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
 | 
				
			||||||
                    children: [
 | 
					                  children: [
 | 
				
			||||||
                      Text(
 | 
					                    Text(
 | 
				
			||||||
                        widget.landmark.name,
 | 
					                      widget.landmark.name,
 | 
				
			||||||
                        style: TextStyle(
 | 
					                      style: TextStyle(
 | 
				
			||||||
                          fontSize: 18,
 | 
					                        fontSize: 18,
 | 
				
			||||||
                          fontWeight: FontWeight.bold,
 | 
					                        fontWeight: FontWeight.bold,
 | 
				
			||||||
                        ),
 | 
					 | 
				
			||||||
                      ),
 | 
					                      ),
 | 
				
			||||||
                    ],
 | 
					                    ),
 | 
				
			||||||
                  ),
 | 
					                  ],
 | 
				
			||||||
                  SizedBox(height: 5),
 | 
					                ),
 | 
				
			||||||
                  Text(
 | 
					                SizedBox(height: 5),
 | 
				
			||||||
                    "${widget.landmark.name} (${widget.landmark.type.name})",
 | 
					                Text(
 | 
				
			||||||
                    style: TextStyle(fontSize: 14),
 | 
					                  "${widget.landmark.name} (${widget.landmark.type.name})",
 | 
				
			||||||
                  ),
 | 
					                  style: TextStyle(fontSize: 14),
 | 
				
			||||||
                ],
 | 
					                ),
 | 
				
			||||||
              ),
 | 
					              ],
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
          ),
 | 
					          ),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
          // Align(
 | 
					          // Align(
 | 
				
			||||||
          //   alignment: Alignment.topRight,
 | 
					          //   alignment: Alignment.topRight,
 | 
				
			||||||
          //   child: Icon(Icons.push_pin, color: theme.primaryColor),
 | 
					          //   child: Icon(Icons.push_pin, color: theme.primaryColor),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,6 +2,7 @@ class Landmark {
 | 
				
			|||||||
  final String name;
 | 
					  final String name;
 | 
				
			||||||
  final List location;
 | 
					  final List location;
 | 
				
			||||||
  final LandmarkType type;
 | 
					  final LandmarkType type;
 | 
				
			||||||
 | 
					  final String imageURL;
 | 
				
			||||||
  // final String description;
 | 
					  // final String description;
 | 
				
			||||||
  // final Duration duration;
 | 
					  // final Duration duration;
 | 
				
			||||||
  // final bool visited;
 | 
					  // final bool visited;
 | 
				
			||||||
@@ -10,6 +11,7 @@ class Landmark {
 | 
				
			|||||||
    required this.name,
 | 
					    required this.name,
 | 
				
			||||||
    required this.location,
 | 
					    required this.location,
 | 
				
			||||||
    required this.type,
 | 
					    required this.type,
 | 
				
			||||||
 | 
					    this.imageURL = 'https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg',
 | 
				
			||||||
    // required this.description,
 | 
					    // required this.description,
 | 
				
			||||||
    // required this.duration,
 | 
					    // required this.duration,
 | 
				
			||||||
    // required this.visited,
 | 
					    // required this.visited,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,14 +10,39 @@ Future<List<Landmark>> fetchLandmarks() async {
 | 
				
			|||||||
    // If the server did return a 200 OK response,
 | 
					    // If the server did return a 200 OK response,
 | 
				
			||||||
    // then parse the JSON.
 | 
					    // then parse the JSON.
 | 
				
			||||||
    List<Landmark> landmarks = [
 | 
					    List<Landmark> landmarks = [
 | 
				
			||||||
      Landmark(name: "Landmark 1", location: [48.85, 2.35], type: LandmarkType(name: "Type 1")),
 | 
					      // 48°51′29.6″N 2°17′40.2″E
 | 
				
			||||||
      Landmark(name: "Landmark 2", location: [48.86, 2.36], type: LandmarkType(name: "Type 2")),
 | 
					      Landmark(
 | 
				
			||||||
      Landmark(name: "Landmark 3", location: [48.75, 2.3], type: LandmarkType(name: "Type 3")),
 | 
					        name: "Eiffel Tower",
 | 
				
			||||||
      Landmark(name: "Landmark 4", location: [48.9, 2.4], type: LandmarkType(name: "Type 4")),
 | 
					        location: [48.51296, 2.17402],
 | 
				
			||||||
      Landmark(name: "Landmark 5", location: [48.91, 2.45], type: LandmarkType(name: "Type 5")),
 | 
					        type: LandmarkType(name: "Tower"),
 | 
				
			||||||
 | 
					        imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      Landmark(
 | 
				
			||||||
 | 
					        name: "Notre Dame Cathedral",
 | 
				
			||||||
 | 
					        location: [48.8530, 2.3498],
 | 
				
			||||||
 | 
					        type: LandmarkType(name: "Monument"),
 | 
				
			||||||
 | 
					        imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Notre-Dame_de_Paris%2C_4_October_2017.jpg/440px-Notre-Dame_de_Paris%2C_4_October_2017.jpg"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      Landmark(
 | 
				
			||||||
 | 
					        name: "Louvre palace",
 | 
				
			||||||
 | 
					        location: [48.8606, 2.3376],
 | 
				
			||||||
 | 
					        type: LandmarkType(name: "Museum"),
 | 
				
			||||||
 | 
					        imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Louvre_Museum_Wikimedia_Commons.jpg/540px-Louvre_Museum_Wikimedia_Commons.jpg"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					      Landmark(
 | 
				
			||||||
 | 
					        name: "Pont-des-arts",
 | 
				
			||||||
 | 
					        location: [48.5130, 2.2015],
 | 
				
			||||||
 | 
					        type: LandmarkType(name: "Bridge"),
 | 
				
			||||||
 | 
					        imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg/560px-Pont_des_Arts%2C_6e_Arrondissement%2C_Paris_%28HDR%29_20140320_1.jpg"),
 | 
				
			||||||
 | 
					      Landmark(
 | 
				
			||||||
 | 
					        name: "Panthéon",
 | 
				
			||||||
 | 
					        location: [48.5046, 2.2046],
 | 
				
			||||||
 | 
					        type: LandmarkType(name: "Monument"),
 | 
				
			||||||
 | 
					        imageURL: "https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/Pantheon_of_Paris_007.JPG/1280px-Pantheon_of_Paris_007.JPG"
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
    // sleep 10 seconds
 | 
					    // sleep 10 seconds
 | 
				
			||||||
    await Future.delayed(Duration(seconds: 10));
 | 
					    await Future.delayed(Duration(seconds: 5));
 | 
				
			||||||
    return landmarks;
 | 
					    return landmarks;
 | 
				
			||||||
  // } else {
 | 
					  // } else {
 | 
				
			||||||
  //   // If the server did not return a 200 OK response,
 | 
					  //   // If the server did not return a 200 OK response,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user