#1110 fixed widget setup race
This commit is contained in:
parent
0bbed0ebda
commit
3bad4b6814
5 changed files with 71 additions and 34 deletions
|
@ -8,6 +8,10 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
- Viewer: display more items in tag/copy/move quick action choosers
|
||||
|
||||
### Fixed
|
||||
|
||||
- multiple widget setup after device reboot
|
||||
|
||||
## <a id="v1.11.8"></a>[v1.11.8] - 2024-07-19
|
||||
|
||||
### Added
|
||||
|
|
|
@ -11,12 +11,18 @@ import android.graphics.Bitmap
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.util.SizeF
|
||||
import android.widget.RemoteViews
|
||||
import app.loup.streams_channel.StreamsChannel
|
||||
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
|
||||
import deckers.thibault.aves.channel.calls.*
|
||||
import deckers.thibault.aves.channel.calls.DeviceHandler
|
||||
import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
|
||||
import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
|
||||
import deckers.thibault.aves.channel.calls.MediaStoreHandler
|
||||
import deckers.thibault.aves.channel.calls.StorageHandler
|
||||
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
|
||||
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
|
@ -26,8 +32,14 @@ import io.flutter.FlutterInjector
|
|||
import io.flutter.embedding.engine.FlutterEngine
|
||||
import io.flutter.embedding.engine.dart.DartExecutor
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import java.nio.ByteBuffer
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.resumeWithException
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
@ -108,37 +120,25 @@ class HomeWidgetProvider : AppWidgetProvider() {
|
|||
|
||||
val isNightModeOn = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
|
||||
val params = hashMapOf(
|
||||
"widgetId" to widgetId,
|
||||
"sizesDip" to sizesDip,
|
||||
"devicePixelRatio" to getDevicePixelRatio(),
|
||||
"drawEntryImage" to drawEntryImage,
|
||||
"reuseEntry" to reuseEntry,
|
||||
"isSystemThemeDark" to isNightModeOn,
|
||||
).apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
put("cornerRadiusPx", context.resources.getDimension(android.R.dimen.system_app_widget_background_radius))
|
||||
}
|
||||
}
|
||||
|
||||
initFlutterEngine(context)
|
||||
val messenger = flutterEngine!!.dartExecutor
|
||||
val channel = MethodChannel(messenger, WIDGET_DRAW_CHANNEL)
|
||||
try {
|
||||
val props = suspendCoroutine<Any?> { cont ->
|
||||
val props = suspendCoroutine { cont ->
|
||||
defaultScope.launch {
|
||||
FlutterUtils.runOnUiThread {
|
||||
channel.invokeMethod("drawWidget", hashMapOf(
|
||||
"widgetId" to widgetId,
|
||||
"sizesDip" to sizesDip,
|
||||
"devicePixelRatio" to getDevicePixelRatio(),
|
||||
"drawEntryImage" to drawEntryImage,
|
||||
"reuseEntry" to reuseEntry,
|
||||
"isSystemThemeDark" to isNightModeOn,
|
||||
).apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
put("cornerRadiusPx", context.resources.getDimension(android.R.dimen.system_app_widget_background_radius))
|
||||
}
|
||||
}, object : MethodChannel.Result {
|
||||
override fun success(result: Any?) {
|
||||
cont.resume(result)
|
||||
}
|
||||
|
||||
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
|
||||
cont.resumeWithException(Exception("$errorCode: $errorMessage\n$errorDetails"))
|
||||
}
|
||||
|
||||
override fun notImplemented() {
|
||||
cont.resumeWithException(Exception("not implemented"))
|
||||
}
|
||||
})
|
||||
tryDrawWidget(params, cont, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,6 +150,30 @@ class HomeWidgetProvider : AppWidgetProvider() {
|
|||
return null
|
||||
}
|
||||
|
||||
private fun tryDrawWidget(params: HashMap<String, Any>, cont: Continuation<Any?>, drawRetry: Int) {
|
||||
val messenger = flutterEngine!!.dartExecutor
|
||||
val channel = MethodChannel(messenger, WIDGET_DRAW_CHANNEL)
|
||||
channel.invokeMethod("drawWidget", params, object : MethodChannel.Result {
|
||||
override fun success(result: Any?) {
|
||||
cont.resume(result)
|
||||
}
|
||||
|
||||
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
|
||||
cont.resumeWithException(Exception("$errorCode: $errorMessage\n$errorDetails"))
|
||||
}
|
||||
|
||||
override fun notImplemented() {
|
||||
if (drawRetry > DRAW_RETRY_MAX) {
|
||||
cont.resumeWithException(Exception("not implemented"))
|
||||
} else {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
tryDrawWidget(params, cont, drawRetry + 1)
|
||||
}, 2000L)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateWidgetImage(
|
||||
context: Context,
|
||||
appWidgetManager: AppWidgetManager,
|
||||
|
@ -271,6 +295,7 @@ class HomeWidgetProvider : AppWidgetProvider() {
|
|||
private val LOG_TAG = LogUtils.createTag<HomeWidgetProvider>()
|
||||
private const val WIDGET_DART_ENTRYPOINT = "widgetMain"
|
||||
private const val WIDGET_DRAW_CHANNEL = "deckers.thibault/aves/widget_draw"
|
||||
private const val DRAW_RETRY_MAX = 5
|
||||
|
||||
private var flutterEngine: FlutterEngine? = null
|
||||
private var imageByteFetchJob: Job? = null
|
||||
|
|
|
@ -7,6 +7,8 @@ import 'package:flutter/foundation.dart';
|
|||
|
||||
final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
|
||||
|
||||
enum _State { uninitialized, initializing, initialized }
|
||||
|
||||
class AndroidFileUtils {
|
||||
// cf https://developer.android.com/reference/android/content/ContentResolver#SCHEME_CONTENT
|
||||
static const contentScheme = 'content';
|
||||
|
@ -27,13 +29,19 @@ class AndroidFileUtils {
|
|||
late final String dcimPath, downloadPath, moviesPath, picturesPath, avesVideoCapturesPath;
|
||||
late final Set<String> videoCapturesPaths;
|
||||
Set<StorageVolume> storageVolumes = {};
|
||||
bool _initialized = false;
|
||||
_State _initialized = _State.uninitialized;
|
||||
|
||||
AndroidFileUtils._private();
|
||||
|
||||
Future<void> init() async {
|
||||
if (_initialized) return;
|
||||
if (_initialized == _State.uninitialized) {
|
||||
_initialized = _State.initializing;
|
||||
await _doInit();
|
||||
_initialized = _State.initialized;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _doInit() async {
|
||||
separator = pContext.separator;
|
||||
await _initStorageVolumes();
|
||||
vaultRoot = await storageService.getVaultRoot();
|
||||
|
@ -50,8 +58,6 @@ class AndroidFileUtils {
|
|||
// from Aves
|
||||
avesVideoCapturesPath,
|
||||
};
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
Future<void> _initStorageVolumes() async {
|
||||
|
|
|
@ -18,11 +18,13 @@ import 'package:flutter/services.dart';
|
|||
const _widgetDrawChannel = MethodChannel('deckers.thibault/aves/widget_draw');
|
||||
|
||||
void widgetMainCommon(AppFlavor flavor) async {
|
||||
debugPrint('Widget main start');
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
initPlatformServices();
|
||||
await settings.init(monitorPlatformSettings: false);
|
||||
await reportService.init();
|
||||
|
||||
debugPrint('Widget channel method handling setup');
|
||||
_widgetDrawChannel.setMethodCallHandler((call) async {
|
||||
// widget settings may be modified in a different process after channel setup
|
||||
await settings.reload();
|
||||
|
|
|
@ -36,7 +36,7 @@ class HomeWidgetPainter {
|
|||
final widthPx = sizeDip.width * devicePixelRatio;
|
||||
final heightPx = sizeDip.height * devicePixelRatio;
|
||||
final widgetSizePx = Size(widthPx, heightPx);
|
||||
debugPrint('draw widget for $sizeDip dp ($widgetSizePx px), entry=$entry');
|
||||
debugPrint('Draw widget for ${sizeDip.width}x${sizeDip.height} dp (${widgetSizePx.width}x${widgetSizePx.height} px), entry=$entry');
|
||||
final ui.Image? entryImage;
|
||||
if (entry != null) {
|
||||
final extent = shape.extentPx(widgetSizePx, entry!) / devicePixelRatio;
|
||||
|
|
Loading…
Reference in a new issue