moved region decoding responsibility to platform side

This commit is contained in:
Thibault Deckers 2024-01-18 00:07:21 +01:00
parent 426264d186
commit 82e4a6b22a
7 changed files with 70 additions and 23 deletions

View file

@ -12,6 +12,7 @@ import androidx.core.content.pm.ShortcutManagerCompat
import com.google.android.material.color.DynamicColors
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.MimeTypes
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
@ -55,6 +56,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
"canUseCrypto" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
"hasGeocoder" to Geocoder.isPresent(),
"isDynamicColorAvailable" to DynamicColors.isDynamicColorAvailable(),
"regionDecodableMimeTypes" to MimeTypes.supportedByBitmapRegionDecoder,
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
)

View file

@ -163,4 +163,13 @@ object MimeTypes {
}
val TIFF_EXTENSION_PATTERN = Regex(".*\\.tiff?", RegexOption.IGNORE_CASE)
val supportedByBitmapRegionDecoder: List<String> = listOf(
// Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported"
// but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below,
// and it actually fails to decode GIF, DNG and animated WEBP. Other formats were not tested.
HEIC, HEIF, JPEG, PNG, WEBP, ARW, CR2, NEF, NRW, ORF, PEF, RAF, RW2, SRW,
// custom support
TIFF,
)
}

View file

@ -20,28 +20,6 @@ class AppSupport {
static bool canDecode(String mimeType) => !undecodableImages.contains(mimeType);
// Android's `BitmapRegionDecoder` documentation states that "only the JPEG and PNG formats are supported"
// but in practice (tested on API 25, 27, 29), it successfully decodes the formats listed below,
// and it actually fails to decode GIF, DNG and animated WEBP. Other formats were not tested.
static bool _supportedByBitmapRegionDecoder(String mimeType) => [
MimeTypes.heic,
MimeTypes.heif,
MimeTypes.jpeg,
MimeTypes.png,
MimeTypes.webp,
MimeTypes.arw,
MimeTypes.cr2,
MimeTypes.nef,
MimeTypes.nrw,
MimeTypes.orf,
MimeTypes.pef,
MimeTypes.raf,
MimeTypes.rw2,
MimeTypes.srw,
].contains(mimeType);
static bool canDecodeRegion(String mimeType) => _supportedByBitmapRegionDecoder(mimeType) || mimeType == MimeTypes.tiff;
// `exifinterface` v1.3.3 declared support for DNG, but it strips non-standard Exif tags when saving attributes,
// and DNG requires DNG-specific tags saved along standard Exif. So it was actually breaking DNG files.
static bool canEditExif(String mimeType) {

View file

@ -12,6 +12,7 @@ class Device {
late final bool _canAuthenticateUser, _canGrantDirectoryAccess, _canPinShortcut;
late final bool _canRenderFlagEmojis, _canRenderSubdivisionFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper, _canUseCrypto;
late final bool _hasGeocoder, _isDynamicColorAvailable, _isTelevision, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode, _supportPictureInPicture;
late final List<String> _regionDecodableMimeTypes;
String get packageName => _packageName;
@ -49,6 +50,8 @@ class Device {
bool get supportPictureInPicture => _supportPictureInPicture;
bool canDecodeRegion(String mimeType) => _regionDecodableMimeTypes.contains(mimeType);
Device._private();
Future<void> init() async {
@ -82,6 +85,7 @@ class Device {
_canUseCrypto = capabilities['canUseCrypto'] ?? false;
_hasGeocoder = capabilities['hasGeocoder'] ?? false;
_isDynamicColorAvailable = capabilities['isDynamicColorAvailable'] ?? false;
_regionDecodableMimeTypes = (capabilities['regionDecodableMimeTypes'] ?? []).cast<String>();
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
_supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false;
}

View file

@ -2,6 +2,7 @@ import 'dart:io';
import 'dart:ui';
import 'package:aves/model/app/support.dart';
import 'package:aves/model/device.dart';
import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/trash.dart';
@ -127,7 +128,7 @@ extension ExtraAvesEntryProps on AvesEntry {
bool get canDecode => AppSupport.canDecode(mimeType);
bool get canDecodeRegion => AppSupport.canDecodeRegion(mimeType) && !isAnimated;
bool get canDecodeRegion => device.canDecodeRegion(mimeType) && !isAnimated;
bool get canEditExif => AppSupport.canEditExif(mimeType);

View file

@ -19,6 +19,7 @@ import 'package:aves/widgets/debug/app_debug_action.dart';
import 'package:aves/widgets/debug/cache.dart';
import 'package:aves/widgets/debug/colors.dart';
import 'package:aves/widgets/debug/database.dart';
import 'package:aves/widgets/debug/device.dart';
import 'package:aves/widgets/debug/general.dart';
import 'package:aves/widgets/debug/media_store_scan_dialog.dart';
import 'package:aves/widgets/debug/report.dart';
@ -75,6 +76,7 @@ class AppDebugPage extends StatelessWidget {
DebugCacheSection(),
DebugColorSection(),
DebugAppDatabaseSection(),
DebugDeviceSection(),
DebugErrorReportingSection(),
DebugSettingsSection(),
DebugStorageSection(),

View file

@ -0,0 +1,51 @@
import 'package:aves/model/device.dart';
import 'package:aves/widgets/common/identity/aves_expansion_tile.dart';
import 'package:aves/widgets/viewer/info/common.dart';
import 'package:flutter/material.dart';
class DebugDeviceSection extends StatefulWidget {
const DebugDeviceSection({super.key});
@override
State<DebugDeviceSection> createState() => _DebugDeviceSectionState();
}
class _DebugDeviceSectionState extends State<DebugDeviceSection> with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
super.build(context);
return AvesExpansionTile(
title: 'Device',
children: [
Padding(
padding: const EdgeInsets.only(left: 8, right: 8, bottom: 8),
child: InfoRowGroup(
info: {
'packageName': device.packageName,
'packageVersion': device.packageVersion,
'userAgent': device.userAgent,
'canAuthenticateUser': '${device.canAuthenticateUser}',
'canGrantDirectoryAccess': '${device.canGrantDirectoryAccess}',
'canPinShortcut': '${device.canPinShortcut}',
'canRenderFlagEmojis': '${device.canRenderFlagEmojis}',
'canRenderSubdivisionFlagEmojis': '${device.canRenderSubdivisionFlagEmojis}',
'canRequestManageMedia': '${device.canRequestManageMedia}',
'canSetLockScreenWallpaper': '${device.canSetLockScreenWallpaper}',
'canUseCrypto': '${device.canUseCrypto}',
'canUseVaults': '${device.canUseVaults}',
'hasGeocoder': '${device.hasGeocoder}',
'isDynamicColorAvailable': '${device.isDynamicColorAvailable}',
'isTelevision': '${device.isTelevision}',
'showPinShortcutFeedback': '${device.showPinShortcutFeedback}',
'supportEdgeToEdgeUIMode': '${device.supportEdgeToEdgeUIMode}',
'supportPictureInPicture': '${device.supportPictureInPicture}',
},
),
),
],
);
}
@override
bool get wantKeepAlive => true;
}