From 8d1677d78b46fe5820ef7dd0746ec2acace13121 Mon Sep 17 00:00:00 2001 From: Remy Moll Date: Mon, 3 Jun 2024 17:36:13 +0200 Subject: [PATCH] cleaner ci --- .gitea/workflows/frontend_build-android.yaml | 26 +++---- frontend/android/.gitignore | 1 + frontend/android/README.md | 48 +++++++++++++ frontend/android/app/build.gradle | 7 +- .../android/app/src/main/AndroidManifest.xml | 2 +- frontend/android/build.gradle | 11 +++ frontend/android/fallback.properties | 1 + frontend/lib/modules/landmark_card.dart | 72 +++++++++---------- frontend/lib/structs/landmark.dart | 2 + frontend/lib/utils/get_landmarks.dart | 37 ++++++++-- 10 files changed, 150 insertions(+), 57 deletions(-) create mode 100644 frontend/android/README.md create mode 100644 frontend/android/fallback.properties diff --git a/.gitea/workflows/frontend_build-android.yaml b/.gitea/workflows/frontend_build-android.yaml index a1e2d7e..16c22fb 100644 --- a/.gitea/workflows/frontend_build-android.yaml +++ b/.gitea/workflows/frontend_build-android.yaml @@ -42,20 +42,20 @@ jobs: - run: flutter pub get 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 - - name: Release APK - uses: https://gitea.com/akkuman/gitea-release-action@v1 + - 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: - files: ./frontend/build/app/outputs/flutter-apk/*.apk - name: Testing release - release_name: testing - tag: testing - tag_name: testing - release_body: "This is a testing release." - prerelease: true - token: ${{ secrets.GITEA_TOKEN }} - env: - NODE_OPTIONS: '--experimental-fetch' + name: app-release + path: frontend/build/app/outputs/flutter-apk/ + if-no-files-found: error + retention-days: 15 + diff --git a/frontend/android/.gitignore b/frontend/android/.gitignore index 6f56801..3592eab 100644 --- a/frontend/android/.gitignore +++ b/frontend/android/.gitignore @@ -4,6 +4,7 @@ gradle-wrapper.jar /gradlew /gradlew.bat /local.properties +/secrets.properties GeneratedPluginRegistrant.java # Remember to never publicly share your keystore. diff --git a/frontend/android/README.md b/frontend/android/README.md new file mode 100644 index 0000000..1e3354e --- /dev/null +++ b/frontend/android/README.md @@ -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 + + ``` + + +### 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 + ``` \ No newline at end of file diff --git a/frontend/android/app/build.gradle b/frontend/android/app/build.gradle index f6b499e..a2e13a5 100644 --- a/frontend/android/app/build.gradle +++ b/frontend/android/app/build.gradle @@ -2,10 +2,12 @@ plugins { id "com.android.application" id "kotlin-android" id "dev.flutter.flutter-gradle-plugin" + id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' + } def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') +def localPropertiesFile = rootProject.file('secrets.properties') if (localPropertiesFile.exists()) { localPropertiesFile.withReader('UTF-8') { reader -> localProperties.load(reader) @@ -52,6 +54,9 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + // Placeholders of keys that are replaced by the build system. + manifestPlaceholders += [MAPS_API_KEY: "some value"] + } buildTypes { diff --git a/frontend/android/app/src/main/AndroidManifest.xml b/frontend/android/app/src/main/AndroidManifest.xml index b38e92b..b6c8a2a 100644 --- a/frontend/android/app/src/main/AndroidManifest.xml +++ b/frontend/android/app/src/main/AndroidManifest.xml @@ -32,7 +32,7 @@ /> diff --git a/frontend/android/build.gradle b/frontend/android/build.gradle index bc157bd..98e49ef 100644 --- a/frontend/android/build.gradle +++ b/frontend/android/build.gradle @@ -16,3 +16,14 @@ subprojects { tasks.register("clean", Delete) { delete rootProject.buildDir } + +buildscript { + repositories { + google() + mavenCentral() + } + + dependencies { + classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1" + } +} \ No newline at end of file diff --git a/frontend/android/fallback.properties b/frontend/android/fallback.properties new file mode 100644 index 0000000..f7ee9e9 --- /dev/null +++ b/frontend/android/fallback.properties @@ -0,0 +1 @@ +MAPS_API_KEY=Key \ No newline at end of file diff --git a/frontend/lib/modules/landmark_card.dart b/frontend/lib/modules/landmark_card.dart index 8b42b7a..7caf76b 100644 --- a/frontend/lib/modules/landmark_card.dart +++ b/frontend/lib/modules/landmark_card.dart @@ -21,47 +21,47 @@ class _LandmarkCardState extends State { ), child: Row( children: [ - Container( - width: 160, - height: 160, - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(15.0), - bottomLeft: Radius.circular(15.0), - ), - image: DecorationImage( - image: NetworkImage('https://upload.wikimedia.org/wikipedia/commons/thumb/a/a8/Tour_Eiffel_Wikimedia_Commons.jpg/1037px-Tour_Eiffel_Wikimedia_Commons.jpg'), - fit: BoxFit.cover, - ), - ), - ), + // Container( + // width: 160, + // height: 160, + // decoration: BoxDecoration( + // borderRadius: BorderRadius.only( + // topLeft: Radius.circular(15.0), + // bottomLeft: Radius.circular(15.0), + // ), + // image: DecorationImage( + // image: NetworkImage(widget.landmark.imageURL), + // fit: BoxFit.cover, + // ), + // ), + // ), Padding( padding: EdgeInsets.all(10), - child: Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.landmark.name, - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), + + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.landmark.name, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, ), - ], - ), - SizedBox(height: 5), - Text( - "${widget.landmark.name} (${widget.landmark.type.name})", - style: TextStyle(fontSize: 14), - ), - ], - ), + ), + ], + ), + SizedBox(height: 5), + Text( + "${widget.landmark.name} (${widget.landmark.type.name})", + style: TextStyle(fontSize: 14), + ), + ], ), ), + // Align( // alignment: Alignment.topRight, // child: Icon(Icons.push_pin, color: theme.primaryColor), diff --git a/frontend/lib/structs/landmark.dart b/frontend/lib/structs/landmark.dart index 14d1b5b..1e9bf26 100644 --- a/frontend/lib/structs/landmark.dart +++ b/frontend/lib/structs/landmark.dart @@ -2,6 +2,7 @@ class Landmark { final String name; final List location; final LandmarkType type; + final String imageURL; // final String description; // final Duration duration; // final bool visited; @@ -10,6 +11,7 @@ class Landmark { required this.name, required this.location, 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.duration, // required this.visited, diff --git a/frontend/lib/utils/get_landmarks.dart b/frontend/lib/utils/get_landmarks.dart index 5390849..53e7314 100644 --- a/frontend/lib/utils/get_landmarks.dart +++ b/frontend/lib/utils/get_landmarks.dart @@ -10,14 +10,39 @@ Future> fetchLandmarks() async { // If the server did return a 200 OK response, // then parse the JSON. List landmarks = [ - Landmark(name: "Landmark 1", location: [48.85, 2.35], type: LandmarkType(name: "Type 1")), - Landmark(name: "Landmark 2", location: [48.86, 2.36], type: LandmarkType(name: "Type 2")), - Landmark(name: "Landmark 3", location: [48.75, 2.3], type: LandmarkType(name: "Type 3")), - Landmark(name: "Landmark 4", location: [48.9, 2.4], type: LandmarkType(name: "Type 4")), - Landmark(name: "Landmark 5", location: [48.91, 2.45], type: LandmarkType(name: "Type 5")), + // 48°51′29.6″N 2°17′40.2″E + Landmark( + name: "Eiffel Tower", + location: [48.51296, 2.17402], + 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 - await Future.delayed(Duration(seconds: 10)); + await Future.delayed(Duration(seconds: 5)); return landmarks; // } else { // // If the server did not return a 200 OK response,