From 13cb97b4b816304f3b256b4a9b34727d5a20de51 Mon Sep 17 00:00:00 2001 From: Gergely Hegedis Date: Mon, 15 Jul 2024 22:00:26 +0300 Subject: [PATCH 1/3] Update README.md --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fba0172..31d0989 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,10 @@ java -jar bundletool-all-1.17.0.jar build-apks --bundle=my_app.aab --output=my_a ``` Download via: + ![QR Code for APK](download_qr.jpg) -First launch you should see an incorrect text. Second launch it should update to the correct patched version. +First launch you should see an incorrect text. Second launch it should update to the correct patched version. (After swiping away from recents) ## Local development @@ -29,4 +30,4 @@ Steps: alias f="fvm flutter" alias flutter="fvm flutter" ``` - - shorebird login \ No newline at end of file + - shorebird login From 25195136381b289501257bf4dff78766de802b4c Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 17 Jan 2025 20:03:47 +0200 Subject: [PATCH 2/3] Change flutter version + add manual-update --- .fvmrc | 2 +- android/app/build.gradle | 8 +- android/gradle.properties | 2 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- android/settings.gradle | 2 +- ios/Flutter/Debug.xcconfig | 1 + ios/Flutter/Release.xcconfig | 1 + lib/main.dart | 75 +++++++++++++++++++ pubspec.lock | 71 ++++++++++++++---- pubspec.yaml | 4 +- shorebird.yaml | 2 +- 11 files changed, 151 insertions(+), 21 deletions(-) diff --git a/.fvmrc b/.fvmrc index 8f59eb5..ff263c6 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.22.2" + "flutter": "3.24.4" } \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 3f89347..38cff25 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -37,8 +37,12 @@ android { ndkVersion = flutter.ndkVersion compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" } defaultConfig { diff --git a/android/gradle.properties b/android/gradle.properties index 3b5b324..b4dddc5 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,3 +1,3 @@ org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true -android.enableJetifier=true +android.enableJetifier=true \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index e1ca574..09523c0 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 536165d..afd9255 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.7.10" apply false + id "org.jetbrains.kotlin.android" version '1.9.20' apply false } include ":app" diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index 592ceee..ec97fc6 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 592ceee..c4855bf 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/lib/main.dart b/lib/main.dart index b706416..70ff854 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,82 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:shorebird_code_push/shorebird_code_push.dart'; +import 'package:restart_app/restart_app.dart'; + void main() { runApp(const MyApp()); } +class ShorebirdPatch extends StatefulWidget { + @override + State createState() => _ShorebirdPatchState(); +} + +enum PatchState { + loading, + noUpdate, + updateReady; +} + +class _ShorebirdPatchState extends State { + final shorebird = ShorebirdCodePush(); + late Timer timer; + PatchState patchState = PatchState.noUpdate; + + @override + void initState() { + timer = Timer.periodic(const Duration(minutes: 5), (timer) async { + if (!shorebird.isShorebirdAvailable()) { + timer.cancel(); + return; + } + if (!await shorebird.isNewPatchAvailableForDownload()) { + setState(() { + patchState = PatchState.noUpdate; + }); + return; + } + final download = shorebird.downloadUpdateIfAvailable(); + setState(() { + patchState = PatchState.loading; + }); + await download; + if (await shorebird.isNewPatchReadyToInstall()) { + setState(() { + patchState = PatchState.updateReady; + }); + return; + } + }); + super.initState(); + } + + @override + void dispose() { + timer.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + switch (patchState) { + case PatchState.loading: + return const SizedBox.square(dimension: 48, child: Center(child: CircularProgressIndicator())); + case PatchState.noUpdate: + return const SizedBox.shrink(); + case PatchState.updateReady: + return InkWell( + child: const SizedBox.square(dimension: 48, child: Icon(Icons.update, size: 24)), + onTap: () { + Restart.restartApp(); + }, + ); + } + } +} + class MyApp extends StatelessWidget { const MyApp({super.key}); @@ -22,6 +95,7 @@ class MyApp extends StatelessWidget { class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); + final String title; @override @@ -43,6 +117,7 @@ class _MyHomePageState extends State { appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), + actions: [ShorebirdPatch()], ), body: Center( child: Column( diff --git a/pubspec.lock b/pubspec.lock index ed4160b..cdd29f8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -57,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" flutter: dependency: "direct main" description: flutter @@ -75,22 +83,27 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -119,18 +132,18 @@ packages: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.15.0" path: dependency: transitive description: @@ -139,6 +152,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.0" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + restart_app: + dependency: "direct main" + description: + name: restart_app + sha256: "00d5ec3e9de871cedbe552fc41e615b042b5ec654385e090e0983f6d02f655ed" + url: "https://pub.dev" + source: hosted + version: "1.3.2" + shorebird_code_push: + dependency: "direct main" + description: + name: shorebird_code_push + sha256: "77511427c51906dd39d3bb1e6b0a8c49777975f9b0a5d073e35c9087a8a36bb6" + url: "https://pub.dev" + source: hosted + version: "1.1.6" sky_engine: dependency: transitive description: flutter @@ -188,10 +225,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.2" vector_math: dependency: transitive description: @@ -204,10 +241,18 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.2.5" + web: + dependency: transitive + description: + name: web + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" sdks: - dart: ">=3.4.3 <4.0.0" + dart: ">=3.5.1 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index cf8d593..96810d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.0+10 +version: 1.0.1+1 environment: sdk: '>=3.4.3 <4.0.0' @@ -30,6 +30,8 @@ environment: dependencies: flutter: sdk: flutter + shorebird_code_push: ^1.1.4 + restart_app: ^1.2.1 # The following adds the Cupertino Icons font to your application. diff --git a/shorebird.yaml b/shorebird.yaml index a806036..7af012a 100644 --- a/shorebird.yaml +++ b/shorebird.yaml @@ -11,4 +11,4 @@ app_id: ee6ee5c7-8fb0-49a1-bdfd-eb326daf5ae5 # If auto_update: false, you will need to use package:shorebird_code_push to trigger updates. # https://pub.dev/packages/shorebird_code_push # Uncomment the following line to disable automatic updates. -# auto_update: false +auto_update: false From 20c693004a9e09b0daa30fc0b636a327cbff9031 Mon Sep 17 00:00:00 2001 From: Gergely Hegedus Date: Fri, 17 Jan 2025 21:54:22 +0200 Subject: [PATCH 3/3] Make manual shorebird update actually work Restarting just the activity doesn't seem to work. To work around this, I finished the process and started the activity via alarm manager. This is inspired from ProcessPhoenix --- android/app/src/main/AndroidManifest.xml | 7 ++ .../experiment_shorebird/MainActivity.kt | 52 +++++++++++- .../flutter/experiment_shorebird/Methods.kt | 7 ++ lib/main.dart | 83 +++++++++++++------ pubspec.yaml | 2 +- 5 files changed, 123 insertions(+), 28 deletions(-) create mode 100644 android/app/src/main/kotlin/org/fnives/flutter/experiment_shorebird/Methods.kt diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4e84368..d448357 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -17,6 +17,13 @@ the Android process has started. This theme is visible to the user while the Flutter UI initializes. After that, this theme continues to determine the Window background behind the Flutter UI. --> + + + + + + + + when (call.method) { + Methods.RESTART.methodName -> { + val pendingIntent = PendingIntent.getActivity( + context, + 4201, + if (isHomeApp()) Intent(intent.action).apply { + flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + intent.categories.forEach { + addCategory(it) + } + } else Intent(this, this::class.java).apply { + flags = Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + }, + PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + val alarmManager = context.getSystemService(ALARM_SERVICE) as AlarmManager + alarmManager[AlarmManager.RTC, System.currentTimeMillis() + 100] = + pendingIntent + if (context is Activity) { + (context as Activity).finishAndRemoveTask() + } + Runtime.getRuntime().exit(0) + result.success(Unit) + } + } + } + } + + private fun isHomeApp(): Boolean { + val intent = Intent(Intent.ACTION_MAIN) + intent.addCategory(Intent.CATEGORY_HOME) + val res = packageManager.resolveActivity(intent, 0) + return res?.activityInfo != null && (packageName == res.activityInfo.packageName) + } +} diff --git a/android/app/src/main/kotlin/org/fnives/flutter/experiment_shorebird/Methods.kt b/android/app/src/main/kotlin/org/fnives/flutter/experiment_shorebird/Methods.kt new file mode 100644 index 0000000..7f934ff --- /dev/null +++ b/android/app/src/main/kotlin/org/fnives/flutter/experiment_shorebird/Methods.kt @@ -0,0 +1,7 @@ +package org.fnives.flutter.experiment_shorebird + +enum class Methods(val methodName: String) { + RESTART("restart"); + + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 70ff854..2fbbeef 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,15 +1,16 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:shorebird_code_push/shorebird_code_push.dart'; -import 'package:restart_app/restart_app.dart'; - void main() { runApp(const MyApp()); } class ShorebirdPatch extends StatefulWidget { + const ShorebirdPatch({super.key}); + @override State createState() => _ShorebirdPatchState(); } @@ -20,39 +21,65 @@ enum PatchState { updateReady; } +enum MainChannelMethods { + restart(methodName: "restart"); + + final String methodName; + + const MainChannelMethods({required this.methodName}); +} + class _ShorebirdPatchState extends State { final shorebird = ShorebirdCodePush(); late Timer timer; PatchState patchState = PatchState.noUpdate; + final methodChannel = const MethodChannel("MainChannel"); @override void initState() { - timer = Timer.periodic(const Duration(minutes: 5), (timer) async { - if (!shorebird.isShorebirdAvailable()) { - timer.cancel(); - return; - } - if (!await shorebird.isNewPatchAvailableForDownload()) { - setState(() { - patchState = PatchState.noUpdate; - }); - return; - } - final download = shorebird.downloadUpdateIfAvailable(); - setState(() { - patchState = PatchState.loading; - }); - await download; - if (await shorebird.isNewPatchReadyToInstall()) { - setState(() { - patchState = PatchState.updateReady; - }); - return; - } + timer = Timer.periodic(const Duration(minutes: 1), (timer) async { + checkForPatch(timer); }); + checkForPatch(timer); super.initState(); } + Future checkForPatch(Timer timer) async { + print('timer activated'); + if (await shorebird.isNewPatchReadyToInstall()) { + print('timer await shorebird.isNewPatchReadyToInstall()'); + setState(() { + patchState = PatchState.updateReady; + }); + return; + } + if (!shorebird.isShorebirdAvailable()) { + print('timer !shorebird.isShorebirdAvailable()'); + timer.cancel(); + return; + } + if (!await shorebird.isNewPatchAvailableForDownload()) { + print('timer !await shorebird.isNewPatchAvailableForDownload()'); + setState(() { + patchState = PatchState.noUpdate; + }); + return; + } + final download = shorebird.downloadUpdateIfAvailable(); + setState(() { + patchState = PatchState.loading; + }); + await download; + if (await shorebird.isNewPatchReadyToInstall()) { + print('timer await shorebird.isNewPatchReadyToInstall()'); + setState(() { + patchState = PatchState.updateReady; + }); + return; + } + print('timer end'); + } + @override void dispose() { timer.cancel(); @@ -70,7 +97,7 @@ class _ShorebirdPatchState extends State { return InkWell( child: const SizedBox.square(dimension: 48, child: Icon(Icons.update, size: 24)), onTap: () { - Restart.restartApp(); + methodChannel.invokeMethod(MainChannelMethods.restart.methodName); }, ); } @@ -117,12 +144,16 @@ class _MyHomePageState extends State { appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), - actions: [ShorebirdPatch()], + actions: const [ShorebirdPatch()], ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ + const Text( + 'This is a release 1.9.1', + // 'You have times:', + ), const Text( 'You have pushed the button this many times:', // 'You have times:', diff --git a/pubspec.yaml b/pubspec.yaml index 96810d2..153908d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.1+1 +version: 1.9.1+191 environment: sdk: '>=3.4.3 <4.0.0'