Compare commits

..

3 commits

Author SHA1 Message Date
20c693004a 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
2025-01-17 21:59:06 +02:00
2519513638 Change flutter version + add manual-update 2025-01-17 20:03:47 +02:00
13cb97b4b8
Update README.md 2024-07-15 22:00:26 +03:00
15 changed files with 250 additions and 24 deletions

2
.fvmrc
View file

@ -1,3 +1,3 @@
{
"flutter": "3.22.2"
"flutter": "3.24.4"
}

View file

@ -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
- shorebird login

View file

@ -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 {

View file

@ -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. -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"

View file

@ -1,5 +1,55 @@
package org.fnives.flutter.experiment_shorebird
import android.app.Activity
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Intent
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity()
class MainActivity : FlutterActivity() {
private val channel = "MainChannel"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
channel
).setMethodCallHandler { call, result ->
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)
}
}

View file

@ -0,0 +1,7 @@
package org.fnives.flutter.experiment_shorebird
enum class Methods(val methodName: String) {
RESTART("restart");
}

View file

@ -1,3 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
android.enableJetifier=true

View file

@ -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

View file

@ -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"

View file

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"

View file

@ -1 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"

View file

@ -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<StatefulWidget> createState() => _ShorebirdPatchState();
}
enum PatchState {
loading,
noUpdate,
updateReady;
}
enum MainChannelMethods {
restart(methodName: "restart");
final String methodName;
const MainChannelMethods({required this.methodName});
}
class _ShorebirdPatchState extends State<ShorebirdPatch> {
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<void> 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<MyHomePage> {
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
actions: const [ShorebirdPatch()],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
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:',

View file

@ -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"

View file

@ -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.

View file

@ -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