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/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 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/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/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..2fbbeef 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,109 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shorebird_code_push/shorebird_code_push.dart'; void main() { runApp(const MyApp()); } +class ShorebirdPatch extends StatefulWidget { + const ShorebirdPatch({super.key}); + + @override + State createState() => _ShorebirdPatchState(); +} + +enum PatchState { + loading, + noUpdate, + 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: 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(); + 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: () { + methodChannel.invokeMethod(MainChannelMethods.restart.methodName); + }, + ); + } + } +} + class MyApp extends StatelessWidget { const MyApp({super.key}); @@ -22,6 +122,7 @@ class MyApp extends StatelessWidget { class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); + final String title; @override @@ -43,11 +144,16 @@ class _MyHomePageState extends State { appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text(widget.title), + 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.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..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.0+10 +version: 1.9.1+191 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