diff --git a/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppAdapterHandler.java b/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppAdapterHandler.java index 4fdd70d8b..2e0ee78f6 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppAdapterHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppAdapterHandler.java @@ -38,8 +38,6 @@ import deckers.thibault.aves.utils.Utils; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel; -import static com.bumptech.glide.request.RequestOptions.centerCropTransform; - public class AppAdapterHandler implements MethodChannel.MethodCallHandler { private static final String LOG_TAG = Utils.createLogTag(AppAdapterHandler.class); @@ -184,7 +182,7 @@ public class AppAdapterHandler implements MethodChannel.MethodCallHandler { FutureTarget target = Glide.with(context) .asBitmap() .apply(options) - .apply(centerCropTransform()) + .centerCrop() .load(uri) .signature(signature) .submit(size, size); diff --git a/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppShortcutHandler.java b/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppShortcutHandler.java index b4a4a3457..08c7071e0 100644 --- a/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppShortcutHandler.java +++ b/android/app/src/main/java/deckers/thibault/aves/channel/calls/AppShortcutHandler.java @@ -2,6 +2,8 @@ package deckers.thibault.aves.channel.calls; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.text.TextUtils; import androidx.annotation.NonNull; @@ -10,6 +12,9 @@ import androidx.core.content.pm.ShortcutInfoCompat; import androidx.core.content.pm.ShortcutManagerCompat; import androidx.core.graphics.drawable.IconCompat; +import com.bumptech.glide.load.engine.bitmap_recycle.LruBitmapPool; +import com.bumptech.glide.load.resource.bitmap.TransformationUtils; + import java.util.List; import deckers.thibault.aves.MainActivity; @@ -35,8 +40,9 @@ public class AppShortcutHandler implements MethodChannel.MethodCallHandler { } case "pin": { String label = call.argument("label"); + byte[] iconBytes = call.argument("iconBytes"); List filters = call.argument("filters"); - pin(label, filters); + new Thread(() -> pin(label, iconBytes, filters)).start(); result.success(null); break; } @@ -46,17 +52,28 @@ public class AppShortcutHandler implements MethodChannel.MethodCallHandler { } } - private void pin(String label, @Nullable List filters) { + private void pin(String label, byte[] iconBytes, @Nullable List filters) { if (!ShortcutManagerCompat.isRequestPinShortcutSupported(context) || filters == null) { return; } + IconCompat icon; + if (iconBytes != null && iconBytes.length > 0) { + Bitmap bitmap = BitmapFactory.decodeByteArray(iconBytes, 0, iconBytes.length); + bitmap = TransformationUtils.centerCrop(new LruBitmapPool(2 << 24), bitmap, 256, 256); + icon = IconCompat.createWithBitmap(bitmap); + } else { + icon = IconCompat.createWithResource(context, R.mipmap.ic_shortcut_collection); + } + + Intent intent = new Intent(Intent.ACTION_MAIN, null, context, MainActivity.class) + .putExtra("page", "/collection") + .putExtra("filters", filters.toArray(new String[0])); + ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(context, "collection-" + TextUtils.join("-", filters)) .setShortLabel(label) - .setIcon(IconCompat.createWithResource(context, R.mipmap.ic_shortcut_collection)) - .setIntent(new Intent(Intent.ACTION_MAIN, null, context, MainActivity.class) - .putExtra("page", "/collection") - .putExtra("filters", filters.toArray(new String[0]))) + .setIcon(icon) + .setIntent(intent) .build(); ShortcutManagerCompat.requestPinShortcut(context, shortcut, null); diff --git a/lib/services/app_shortcut_service.dart b/lib/services/app_shortcut_service.dart index 5d4c244b7..6369048f1 100644 --- a/lib/services/app_shortcut_service.dart +++ b/lib/services/app_shortcut_service.dart @@ -1,4 +1,6 @@ import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/image_entry.dart'; +import 'package:aves/services/image_file_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; @@ -22,10 +24,12 @@ class AppShortcutService { return false; } - static Future pin(String label, Set filters) async { + static Future pin(String label, ImageEntry iconEntry, Set filters) async { + final iconBytes = iconEntry != null ? await ImageFileService.getThumbnail(iconEntry, 256, 256) : null; try { await platform.invokeMethod('pin', { 'label': label, + 'iconBytes': iconBytes, 'filters': filters.map((filter) => filter.toJson()).toList(), }); } on PlatformException catch (e) { diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index e29302f42..8436686b6 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -356,7 +356,8 @@ class _CollectionAppBarState extends State with SingleTickerPr ); if (name == null || name.isEmpty) return; - unawaited(AppShortcutService.pin(name, collection.filters)); + final iconEntry = collection.sortedEntries.isNotEmpty ? collection.sortedEntries.first : null; + unawaited(AppShortcutService.pin(name, iconEntry, collection.filters)); } void _goToSearch() { diff --git a/lib/widgets/collection/grid/header_generic.dart b/lib/widgets/collection/grid/header_generic.dart index e267e2be4..7c0fda47a 100644 --- a/lib/widgets/collection/grid/header_generic.dart +++ b/lib/widgets/collection/grid/header_generic.dart @@ -87,8 +87,7 @@ class SectionHeader extends StatelessWidget { // force a higher first line to match leading icon/selector dimension style: TextStyle(height: 2.3 * textScaleFactor), ), // 23 hair spaces match a width of 40.0 - if (hasTrailing) - TextSpan(text: '\u200A' * 17), + if (hasTrailing) TextSpan(text: '\u200A' * 17), TextSpan( text: text, style: Constants.titleTextStyle, diff --git a/lib/widgets/common/action_delegates/add_shortcut_dialog.dart b/lib/widgets/common/action_delegates/add_shortcut_dialog.dart index 1b353f519..7274c273e 100644 --- a/lib/widgets/common/action_delegates/add_shortcut_dialog.dart +++ b/lib/widgets/common/action_delegates/add_shortcut_dialog.dart @@ -43,7 +43,7 @@ class _AddShortcutDialogState extends State { labelText: 'Shortcut label', ), autofocus: true, - maxLength: 10, + maxLength: 25, onChanged: (_) => _validate(), onSubmitted: (_) => _submit(context), ), diff --git a/lib/widgets/common/image_providers/thumbnail_provider.dart b/lib/widgets/common/image_providers/thumbnail_provider.dart index b3f1875c7..7d0e58e3e 100644 --- a/lib/widgets/common/image_providers/thumbnail_provider.dart +++ b/lib/widgets/common/image_providers/thumbnail_provider.dart @@ -66,6 +66,7 @@ class ThumbnailProviderKey { final ImageEntry entry; final double extent; final double scale; + // do not access `contentId` via `entry` for hashCode and equality purposes // as an entry is not constant and its contentId can change final int contentId; diff --git a/lib/widgets/fullscreen/info/maps/scale_layer.dart b/lib/widgets/fullscreen/info/maps/scale_layer.dart index 70bf1ad4a..ac92d838d 100644 --- a/lib/widgets/fullscreen/info/maps/scale_layer.dart +++ b/lib/widgets/fullscreen/info/maps/scale_layer.dart @@ -75,7 +75,12 @@ class ScaleLayer extends StatelessWidget { builder: (context, snapshot) { final center = map.center; final latitude = center.latitude.abs(); - final level = map.zoom.round() + (latitude > 80 ? 4 : latitude > 60 ? 3 : 2); + final level = map.zoom.round() + + (latitude > 80 + ? 4 + : latitude > 60 + ? 3 + : 2); final distance = scale[max(0, min(20, level))].toDouble(); final start = map.project(center); final targetPoint = util.calculateEndingGlobalCoordinates(center, 90, distance);