#638 widget: option to update on tap

This commit is contained in:
Thibault Deckers 2023-05-14 18:40:33 +02:00
parent 56b67c47db
commit 00346ebb31
8 changed files with 112 additions and 41 deletions

View file

@ -9,6 +9,7 @@ All notable changes to this project will be documented in this file.
- option to set the Tags page as home
- support for animated PNG
- Info: added day filter with item date
- Widget: option to update image on tap
### Changed

View file

@ -18,6 +18,7 @@ import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.FlutterUtils
import deckers.thibault.aves.utils.LogUtils
import io.flutter.FlutterInjector
@ -40,11 +41,11 @@ class HomeWidgetProvider : AppWidgetProvider() {
val widgetInfo = appWidgetManager.getAppWidgetOptions(widgetId)
defaultScope.launch {
val backgroundBytes = getBytes(context, widgetId, widgetInfo, drawEntryImage = false)
updateWidgetImage(context, appWidgetManager, widgetId, widgetInfo, backgroundBytes)
val backgroundProps = getProps(context, widgetId, widgetInfo, drawEntryImage = false)
updateWidgetImage(context, appWidgetManager, widgetId, widgetInfo, backgroundProps)
val imageBytes = getBytes(context, widgetId, widgetInfo, drawEntryImage = true, reuseEntry = false)
updateWidgetImage(context, appWidgetManager, widgetId, widgetInfo, imageBytes)
val imageProps = getProps(context, widgetId, widgetInfo, drawEntryImage = true, reuseEntry = false)
updateWidgetImage(context, appWidgetManager, widgetId, widgetInfo, imageProps)
}
}
}
@ -59,8 +60,8 @@ class HomeWidgetProvider : AppWidgetProvider() {
}
imageByteFetchJob = defaultScope.launch {
delay(500)
val imageBytes = getBytes(context, widgetId, widgetInfo, drawEntryImage = true, reuseEntry = true)
updateWidgetImage(context, appWidgetManager, widgetId, widgetInfo, imageBytes)
val imageProps = getProps(context, widgetId, widgetInfo, drawEntryImage = true, reuseEntry = true)
updateWidgetImage(context, appWidgetManager, widgetId, widgetInfo, imageProps)
}
}
@ -76,13 +77,13 @@ class HomeWidgetProvider : AppWidgetProvider() {
return Pair(widthPx, heightPx)
}
private suspend fun getBytes(
private suspend fun getProps(
context: Context,
widgetId: Int,
widgetInfo: Bundle,
drawEntryImage: Boolean,
reuseEntry: Boolean = false,
): ByteArray? {
): FieldMap? {
val (widthPx, heightPx) = getWidgetSizePx(context, widgetInfo)
if (widthPx == 0 || heightPx == 0) return null
@ -90,7 +91,7 @@ class HomeWidgetProvider : AppWidgetProvider() {
val messenger = flutterEngine!!.dartExecutor
val channel = MethodChannel(messenger, WIDGET_DRAW_CHANNEL)
try {
val bytes = suspendCoroutine<Any?> { cont ->
val props = suspendCoroutine<Any?> { cont ->
defaultScope.launch {
FlutterUtils.runOnUiThread {
channel.invokeMethod("drawWidget", hashMapOf(
@ -116,7 +117,8 @@ class HomeWidgetProvider : AppWidgetProvider() {
}
}
}
if (bytes is ByteArray) return bytes
@Suppress("unchecked_cast")
return props as FieldMap?
} catch (e: Exception) {
Log.e(LOG_TAG, "failed to draw widget for widgetId=$widgetId widthPx=$widthPx heightPx=$heightPx", e)
}
@ -128,9 +130,16 @@ class HomeWidgetProvider : AppWidgetProvider() {
appWidgetManager: AppWidgetManager,
widgetId: Int,
widgetInfo: Bundle,
bytes: ByteArray?,
props: FieldMap?,
) {
bytes ?: return
props ?: return
val bytes = props["bytes"] as ByteArray?
val updateOnTap = props["updateOnTap"] as Boolean?
if (bytes == null || updateOnTap == null) {
Log.e(LOG_TAG, "missing arguments")
return
}
val (widthPx, heightPx) = getWidgetSizePx(context, widgetInfo)
if (widthPx == 0 || heightPx == 0) return
@ -139,24 +148,11 @@ class HomeWidgetProvider : AppWidgetProvider() {
val bitmap = Bitmap.createBitmap(widthPx, heightPx, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(bytes))
// set a unique URI to prevent the intent (and its extras) from being shared by different widgets
val intent = Intent(MainActivity.INTENT_ACTION_WIDGET_OPEN, Uri.parse("widget://$widgetId"), context, MainActivity::class.java)
.putExtra(MainActivity.EXTRA_KEY_WIDGET_ID, widgetId)
val activity = PendingIntent.getActivity(
context,
0,
intent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
)
val pendingIntent = if (updateOnTap) buildUpdateIntent(context, widgetId) else buildOpenAppIntent(context, widgetId)
val views = RemoteViews(context.packageName, R.layout.app_widget).apply {
setImageViewBitmap(R.id.widget_img, bitmap)
setOnClickPendingIntent(R.id.widget_img, activity)
setOnClickPendingIntent(R.id.widget_img, pendingIntent)
}
appWidgetManager.updateAppWidget(widgetId, views)
@ -166,6 +162,39 @@ class HomeWidgetProvider : AppWidgetProvider() {
}
}
private fun buildUpdateIntent(context: Context, widgetId: Int): PendingIntent {
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, Uri.parse("widget://$widgetId"), context, HomeWidgetProvider::class.java)
.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(widgetId))
return PendingIntent.getBroadcast(
context,
0,
intent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
)
}
private fun buildOpenAppIntent(context: Context, widgetId: Int): PendingIntent {
// set a unique URI to prevent the intent (and its extras) from being shared by different widgets
val intent = Intent(MainActivity.INTENT_ACTION_WIDGET_OPEN, Uri.parse("widget://$widgetId"), context, MainActivity::class.java)
.putExtra(MainActivity.EXTRA_KEY_WIDGET_ID, widgetId)
return PendingIntent.getActivity(
context,
0,
intent,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
)
}
companion object {
private val LOG_TAG = LogUtils.createTag<HomeWidgetProvider>()
private const val WIDGET_DART_ENTRYPOINT = "widgetMain"

View file

@ -281,6 +281,7 @@
"widgetOpenPageHome": "Open home",
"widgetOpenPageCollection": "Open collection",
"widgetOpenPageViewer": "Open viewer",
"widgetTapUpdateWidget": "Update widget",
"storageVolumeDescriptionFallbackPrimary": "Internal storage",
"storageVolumeDescriptionFallbackNonPrimary": "SD card",

View file

@ -275,6 +275,8 @@ extension ExtraWidgetOpenPageView on WidgetOpenPage {
return context.l10n.widgetOpenPageCollection;
case WidgetOpenPage.viewer:
return context.l10n.widgetOpenPageViewer;
case WidgetOpenPage.updateWidget:
return context.l10n.widgetTapUpdateWidget;
}
}
}

View file

@ -34,7 +34,7 @@ void widgetMainCommon(AppFlavor flavor) async {
});
}
Future<Uint8List> _drawWidget(dynamic args) async {
Future<Map<String, dynamic>> _drawWidget(dynamic args) async {
final widgetId = args['widgetId'] as int;
final widthPx = args['widthPx'] as int;
final heightPx = args['heightPx'] as int;
@ -47,12 +47,16 @@ Future<Uint8List> _drawWidget(dynamic args) async {
entry: entry,
devicePixelRatio: devicePixelRatio,
);
return painter.drawWidget(
final bytes = await painter.drawWidget(
widthPx: widthPx,
heightPx: heightPx,
outline: settings.getWidgetOutline(widgetId),
shape: settings.getWidgetShape(widgetId),
);
return {
'bytes': bytes,
'updateOnTap': settings.getWidgetOpenPage(widgetId) == WidgetOpenPage.updateWidget,
};
}
Future<AvesEntry?> _getWidgetEntry(int widgetId, bool reuseEntry) async {

View file

@ -113,6 +113,7 @@ class _HomePageState extends State<HomePage> {
final page = settings.getWidgetOpenPage(widgetId);
switch (page) {
case WidgetOpenPage.home:
case WidgetOpenPage.updateWidget:
break;
case WidgetOpenPage.collection:
_initialFilters = settings.getWidgetCollectionFilters(widgetId);

View file

@ -44,6 +44,6 @@ enum ViewerTransition { slide, parallax, fade, zoomIn, none }
enum WidgetDisplayedItem { random, mostRecent }
enum WidgetOpenPage { home, collection, viewer }
enum WidgetOpenPage { home, collection, viewer, updateWidget }
enum WidgetShape { rrect, circle, heart }

View file

@ -178,6 +178,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -765,6 +766,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -1245,6 +1247,7 @@
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"exportEntryDialogQuality",
"settingsAskEverytime",
"settingsVideoPlaybackTile",
@ -1267,6 +1270,7 @@
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"exportEntryDialogQuality",
"settingsAskEverytime",
"settingsVideoPlaybackTile",
@ -1284,7 +1288,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"es": [
@ -1295,7 +1300,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"eu": [
@ -1306,7 +1312,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"fa": [
@ -1382,6 +1389,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"rootDirectoryDescription",
"otherDirectoryDescription",
"restrictedAccessDialogMessage",
@ -1820,7 +1828,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"gl": [
@ -1891,6 +1900,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -2555,6 +2565,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -3199,6 +3210,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -3674,7 +3686,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"id": [
@ -3685,7 +3698,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"it": [
@ -3696,7 +3710,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"ja": [
@ -3722,6 +3737,7 @@
"subtitlePositionBottom",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"vaultBinUsageDialogMessage",
"exportEntryDialogQuality",
"exportEntryDialogWriteMetadata",
@ -3757,7 +3773,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"lt": [
@ -3791,6 +3808,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
"configureVaultDialogTitle",
@ -4036,6 +4054,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -4521,6 +4540,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"patternDialogEnter",
"patternDialogConfirm",
"exportEntryDialogQuality",
@ -4567,6 +4587,7 @@
"vaultLockTypePattern",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
"configureVaultDialogTitle",
@ -4653,6 +4674,7 @@
"videoResumptionModeAlways",
"wallpaperTargetHome",
"wallpaperTargetHomeLock",
"widgetTapUpdateWidget",
"setCoverDialogCustom",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
@ -5145,6 +5167,7 @@
"widgetOpenPageHome",
"widgetOpenPageCollection",
"widgetOpenPageViewer",
"widgetTapUpdateWidget",
"storageVolumeDescriptionFallbackPrimary",
"storageVolumeDescriptionFallbackNonPrimary",
"rootDirectoryDescription",
@ -5570,7 +5593,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"pt": [
@ -5581,7 +5605,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"ro": [
@ -5597,6 +5622,7 @@
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"exportEntryDialogQuality",
"settingsAskEverytime",
"settingsVideoPlaybackTile",
@ -5619,6 +5645,7 @@
"maxBrightnessAlways",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"exportEntryDialogQuality",
"statePageTitle",
"stateEmpty",
@ -5668,6 +5695,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"otherDirectoryDescription",
"storageAccessDialogMessage",
"restrictedAccessDialogMessage",
@ -6121,6 +6149,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
"configureVaultDialogTitle",
@ -6501,6 +6530,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
"configureVaultDialogTitle",
@ -6547,7 +6577,8 @@
"editorTransformRotate",
"cropAspectRatioFree",
"cropAspectRatioOriginal",
"cropAspectRatioSquare"
"cropAspectRatioSquare",
"widgetTapUpdateWidget"
],
"zh": [
@ -6575,6 +6606,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
"configureVaultDialogTitle",
@ -6649,6 +6681,7 @@
"settingsVideoEnablePip",
"videoResumptionModeNever",
"videoResumptionModeAlways",
"widgetTapUpdateWidget",
"newVaultWarningDialogMessage",
"newVaultDialogTitle",
"configureVaultDialogTitle",