API 16 support prep
This commit is contained in:
parent
089304da2d
commit
35958d87fd
20 changed files with 143 additions and 91 deletions
|
@ -56,8 +56,7 @@ android {
|
|||
// minSdkVersion constraints:
|
||||
// - Flutter & other plugins: 16
|
||||
// - google_maps_flutter v2.1.1: 20
|
||||
// - Aves native: 19
|
||||
minSdkVersion 19
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 31
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
|
@ -149,7 +148,7 @@ dependencies {
|
|||
// forked, built by JitPack, cf https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
|
||||
implementation 'com.github.deckerst:Android-TiffBitmapFactory:876e53870a'
|
||||
// forked, built by JitPack, cf https://jitpack.io/p/deckerst/pixymeta-android
|
||||
implementation 'com.github.deckerst:pixymeta-android:a86b1b8e4c'
|
||||
implementation 'com.github.deckerst:pixymeta-android:706bd73d6e'
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
|
||||
kapt 'androidx.annotation:annotation:1.3.0'
|
||||
|
|
|
@ -23,7 +23,6 @@ import io.flutter.embedding.engine.FlutterEngine
|
|||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.*
|
||||
|
||||
class AnalysisService : MethodChannel.MethodCallHandler, Service() {
|
||||
private var backgroundFlutterEngine: FlutterEngine? = null
|
||||
|
@ -141,11 +140,12 @@ class AnalysisService : MethodChannel.MethodCallHandler, Service() {
|
|||
getString(R.string.analysis_notification_action_stop),
|
||||
stopServiceIntent
|
||||
).build()
|
||||
val icon = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) R.drawable.ic_notification else R.mipmap.ic_launcher_round
|
||||
return NotificationCompat.Builder(this, CHANNEL_ANALYSIS)
|
||||
.setContentTitle(title ?: getText(R.string.analysis_notification_default_title))
|
||||
.setContentText(message)
|
||||
.setBadgeIconType(NotificationCompat.BADGE_ICON_NONE)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setSmallIcon(icon)
|
||||
.setContentIntent(openAppIntent)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.addAction(stopAction)
|
||||
|
|
|
@ -24,11 +24,13 @@ class AccessibilityHandler(private val activity: Activity) : MethodCallHandler {
|
|||
|
||||
private fun areAnimationsRemoved(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
var removed = false
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
try {
|
||||
removed = Settings.Global.getFloat(activity.contentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE) == 0f
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get settings with error=${e.message}", null)
|
||||
}
|
||||
}
|
||||
result.success(removed)
|
||||
}
|
||||
|
||||
|
|
|
@ -62,7 +62,14 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
fun addPackageDetails(intent: Intent) {
|
||||
// apps tend to use their name in English when creating directories
|
||||
// so we get their names in English as well as the current locale
|
||||
val englishConfig = Configuration().apply { setLocale(Locale.ENGLISH) }
|
||||
val englishConfig = Configuration().apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
setLocale(Locale.ENGLISH)
|
||||
} else {
|
||||
@Suppress("deprecation")
|
||||
locale = Locale.ENGLISH
|
||||
}
|
||||
}
|
||||
|
||||
val pm = context.packageManager
|
||||
for (resolveInfo in pm.queryIntentActivities(intent, 0)) {
|
||||
|
|
|
@ -20,15 +20,21 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
private fun getCapabilities(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(hashMapOf(
|
||||
"canGrantDirectoryAccess" to (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP),
|
||||
val sdkInt = Build.VERSION.SDK_INT
|
||||
result.success(
|
||||
hashMapOf(
|
||||
"canGrantDirectoryAccess" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
||||
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
|
||||
"canPrint" to (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT),
|
||||
"canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT),
|
||||
"canRenderEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
||||
// as of google_maps_flutter v2.1.1, minSDK is 20 because of default PlatformView usage,
|
||||
// but using hybrid composition would make it usable on API 19 too,
|
||||
// cf https://github.com/flutter/flutter/issues/23728
|
||||
"canRenderGoogleMaps" to (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH),
|
||||
))
|
||||
"canRenderGoogleMaps" to (sdkInt >= Build.VERSION_CODES.KITKAT_WATCH),
|
||||
"hasFilePicker" to (sdkInt >= Build.VERSION_CODES.KITKAT),
|
||||
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getDefaultTimeZone(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
|
|
|
@ -584,7 +584,9 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
|
|||
|
||||
var flags = (metadataMap[KEY_FLAGS] ?: 0) as Int
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION) { metadataMap[KEY_ROTATION_DEGREES] = it }
|
||||
}
|
||||
if (!metadataMap.containsKey(KEY_DATE_MILLIS)) {
|
||||
retriever.getSafeDateMillis(MediaMetadataRetriever.METADATA_KEY_DATE) { metadataMap[KEY_DATE_MILLIS] = it }
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package deckers.thibault.aves.channel.streams
|
|||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
|
@ -32,12 +33,13 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
|
||||
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||
if (update()) {
|
||||
success(
|
||||
hashMapOf(
|
||||
val settings: FieldMap = hashMapOf(
|
||||
Settings.System.ACCELEROMETER_ROTATION to accelerometerRotation,
|
||||
Settings.Global.TRANSITION_ANIMATION_SCALE to transitionAnimationScale,
|
||||
)
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
settings[Settings.Global.TRANSITION_ANIMATION_SCALE] = transitionAnimationScale
|
||||
}
|
||||
success(settings)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,12 +51,13 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
accelerometerRotation = newAccelerometerRotation
|
||||
changed = true
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
val newTransitionAnimationScale = Settings.Global.getFloat(context.contentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE)
|
||||
if (transitionAnimationScale != newTransitionAnimationScale) {
|
||||
transitionAnimationScale = newTransitionAnimationScale
|
||||
changed = true
|
||||
}
|
||||
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get settings with error=${e.message}", null)
|
||||
}
|
||||
|
|
|
@ -27,11 +27,13 @@ object MediaMetadataRetrieverHelper {
|
|||
MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS to "Number of Tracks",
|
||||
MediaMetadataRetriever.METADATA_KEY_TITLE to "Title",
|
||||
MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT to "Video Height",
|
||||
MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION to "Video Rotation",
|
||||
MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH to "Video Width",
|
||||
MediaMetadataRetriever.METADATA_KEY_WRITER to "Writer",
|
||||
MediaMetadataRetriever.METADATA_KEY_YEAR to "Year",
|
||||
).apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
put(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION, "Video Rotation")
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
put(MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE, "Capture Framerate")
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.graphics.BitmapFactory
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import com.drew.imaging.ImageMetadataReader
|
||||
import com.drew.metadata.avi.AviDirectory
|
||||
|
@ -135,10 +136,12 @@ class SourceEntry {
|
|||
try {
|
||||
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH) { width = it }
|
||||
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT) { height = it }
|
||||
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION) { sourceRotationDegrees = it }
|
||||
retriever.getSafeLong(MediaMetadataRetriever.METADATA_KEY_DURATION) { durationMillis = it }
|
||||
retriever.getSafeDateMillis(MediaMetadataRetriever.METADATA_KEY_DATE) { sourceDateTakenMillis = it }
|
||||
retriever.getSafeString(MediaMetadataRetriever.METADATA_KEY_TITLE) { title = it }
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION) { sourceRotationDegrees = it }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// ignore
|
||||
} finally {
|
||||
|
|
|
@ -142,39 +142,6 @@ object PermissionManager {
|
|||
}
|
||||
}
|
||||
|
||||
fun getRestrictedDirectories(context: Context): List<Map<String, String>> {
|
||||
val dirs = ArrayList<Map<String, String>>()
|
||||
val sdkInt = Build.VERSION.SDK_INT
|
||||
|
||||
if (sdkInt >= Build.VERSION_CODES.R) {
|
||||
// cf https://developer.android.com/about/versions/11/privacy/storage#directory-access
|
||||
val volumePaths = StorageUtils.getVolumePaths(context)
|
||||
dirs.addAll(volumePaths.map {
|
||||
hashMapOf(
|
||||
"volumePath" to it,
|
||||
"relativeDir" to "",
|
||||
)
|
||||
})
|
||||
dirs.addAll(volumePaths.map {
|
||||
hashMapOf(
|
||||
"volumePath" to it,
|
||||
"relativeDir" to Environment.DIRECTORY_DOWNLOADS,
|
||||
)
|
||||
})
|
||||
} else if (sdkInt == Build.VERSION_CODES.KITKAT || sdkInt == Build.VERSION_CODES.KITKAT_WATCH) {
|
||||
// no SD card volume access on KitKat
|
||||
val primaryVolume = StorageUtils.getPrimaryVolumePath(context)
|
||||
val nonPrimaryVolumes = StorageUtils.getVolumePaths(context).filter { it != primaryVolume }
|
||||
dirs.addAll(nonPrimaryVolumes.map {
|
||||
hashMapOf(
|
||||
"volumePath" to it,
|
||||
"relativeDir" to "",
|
||||
)
|
||||
})
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
fun canInsertByMediaStore(directories: List<FieldMap>): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
directories.all {
|
||||
|
@ -217,14 +184,46 @@ object PermissionManager {
|
|||
// from API 30 / Android 11 / R, any storage requires access permission
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||
accessibleDirs.addAll(StorageUtils.getVolumePaths(context))
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
|
||||
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q
|
||||
) {
|
||||
} else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||
accessibleDirs.add(StorageUtils.getPrimaryVolumePath(context))
|
||||
}
|
||||
return accessibleDirs
|
||||
}
|
||||
|
||||
fun getRestrictedDirectories(context: Context): List<Map<String, String>> {
|
||||
val dirs = ArrayList<Map<String, String>>()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// cf https://developer.android.com/about/versions/11/privacy/storage#directory-access
|
||||
val volumePaths = StorageUtils.getVolumePaths(context)
|
||||
dirs.addAll(volumePaths.map {
|
||||
hashMapOf(
|
||||
"volumePath" to it,
|
||||
"relativeDir" to "",
|
||||
)
|
||||
})
|
||||
dirs.addAll(volumePaths.map {
|
||||
hashMapOf(
|
||||
"volumePath" to it,
|
||||
"relativeDir" to Environment.DIRECTORY_DOWNLOADS,
|
||||
)
|
||||
})
|
||||
} else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT
|
||||
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT_WATCH) {
|
||||
// removable storage requires access permission, at the file level
|
||||
// without directory access, we consider the whole volume restricted
|
||||
val primaryVolume = StorageUtils.getPrimaryVolumePath(context)
|
||||
val nonPrimaryVolumes = StorageUtils.getVolumePaths(context).filter { it != primaryVolume }
|
||||
dirs.addAll(nonPrimaryVolumes.map {
|
||||
hashMapOf(
|
||||
"volumePath" to it,
|
||||
"relativeDir" to "",
|
||||
)
|
||||
})
|
||||
}
|
||||
return dirs
|
||||
}
|
||||
|
||||
// As of Android R, `MediaStore.getDocumentUri` fails if any of the persisted
|
||||
// URI permissions we hold points to a folder that no longer exists,
|
||||
// so we should remove these obsolete URIs before proceeding.
|
||||
|
|
|
@ -5,7 +5,8 @@ final Device device = Device._private();
|
|||
|
||||
class Device {
|
||||
late final String _userAgent;
|
||||
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderGoogleMaps;
|
||||
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderEmojis, _canRenderGoogleMaps;
|
||||
late final bool _hasFilePicker, _showPinShortcutFeedback;
|
||||
|
||||
String get userAgent => _userAgent;
|
||||
|
||||
|
@ -15,8 +16,15 @@ class Device {
|
|||
|
||||
bool get canPrint => _canPrint;
|
||||
|
||||
bool get canRenderEmojis => _canRenderEmojis;
|
||||
|
||||
bool get canRenderGoogleMaps => _canRenderGoogleMaps;
|
||||
|
||||
// TODO TLAD toggle settings > import/export, about > bug report > save
|
||||
bool get hasFilePicker => _hasFilePicker;
|
||||
|
||||
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
||||
|
||||
Device._private();
|
||||
|
||||
Future<void> init() async {
|
||||
|
@ -27,6 +35,9 @@ class Device {
|
|||
_canGrantDirectoryAccess = capabilities['canGrantDirectoryAccess'] ?? false;
|
||||
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
||||
_canPrint = capabilities['canPrint'] ?? false;
|
||||
_canRenderEmojis = capabilities['canRenderEmojis'] ?? false;
|
||||
_canRenderGoogleMaps = capabilities['canRenderGoogleMaps'] ?? false;
|
||||
_hasFilePicker = capabilities['hasFilePicker'] ?? false;
|
||||
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ abstract class CollectionFilter extends Equatable implements Comparable<Collecti
|
|||
];
|
||||
|
||||
static CollectionFilter? fromJson(String jsonString) {
|
||||
if (jsonString.isEmpty) return null;
|
||||
|
||||
try {
|
||||
final jsonMap = jsonDecode(jsonString);
|
||||
if (jsonMap is Map<String, dynamic>) {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:aves/model/device.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
|
@ -58,6 +59,7 @@ class LocationFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true, bool embossed = false}) {
|
||||
if (_countryCode != null && device.canRenderEmojis) {
|
||||
final flag = countryCodeToFlag(_countryCode);
|
||||
// as of Flutter v1.22.3, emoji shadows are rendered as colorful duplicates,
|
||||
// not filled with the shadow color as expected, so we remove them
|
||||
|
@ -68,6 +70,7 @@ class LocationFilter extends CollectionFilter {
|
|||
textScaleFactor: 1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
return Icon(_location.isEmpty ? AIcons.locationOff : AIcons.location, size: size);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ import 'package:aves/model/source/media_store_source.dart';
|
|||
import 'package:aves/model/source/source_state.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:fijkplayer/fijkplayer.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
|
||||
class AnalysisService {
|
||||
|
|
|
@ -159,7 +159,7 @@ class PlatformMediaFileService implements MediaFileService {
|
|||
int? pageId,
|
||||
int? expectedContentLength,
|
||||
BytesReceivedCallback? onBytesReceived,
|
||||
}) {
|
||||
}) async {
|
||||
try {
|
||||
final completer = Completer<Uint8List>.sync();
|
||||
final sink = OutputBuffer();
|
||||
|
@ -191,11 +191,12 @@ class PlatformMediaFileService implements MediaFileService {
|
|||
},
|
||||
cancelOnError: true,
|
||||
);
|
||||
return completer.future;
|
||||
// `await` here, so that `completeError` will be caught below
|
||||
return await completer.future;
|
||||
} on PlatformException catch (e, stack) {
|
||||
reportService.recordError(e, stack);
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return Future.sync(() => Uint8List(0));
|
||||
return Uint8List(0);
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -172,7 +172,8 @@ class PlatformStorageService implements StorageService {
|
|||
},
|
||||
cancelOnError: true,
|
||||
);
|
||||
return completer.future;
|
||||
// `await` here, so that `completeError` will be caught below
|
||||
return await completer.future;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
|
@ -196,7 +197,8 @@ class PlatformStorageService implements StorageService {
|
|||
},
|
||||
cancelOnError: true,
|
||||
);
|
||||
return completer.future;
|
||||
// `await` here, so that `completeError` will be caught below
|
||||
return await completer.future;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
|
@ -220,7 +222,8 @@ class PlatformStorageService implements StorageService {
|
|||
},
|
||||
cancelOnError: true,
|
||||
);
|
||||
return completer.future;
|
||||
// `await` here, so that `completeError` will be caught below
|
||||
return await completer.future;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
|
@ -247,7 +250,8 @@ class PlatformStorageService implements StorageService {
|
|||
},
|
||||
cancelOnError: true,
|
||||
);
|
||||
return completer.future;
|
||||
// `await` here, so that `completeError` will be caught below
|
||||
return await completer.future;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
|
|
|
@ -626,6 +626,9 @@ class EntrySetActionDelegate with EntryEditorMixin, FeedbackMixin, PermissionAwa
|
|||
final name = result.item2;
|
||||
if (name.isEmpty) return;
|
||||
|
||||
unawaited(androidAppService.pinToHomeScreen(name, coverEntry, filters: filters));
|
||||
await androidAppService.pinToHomeScreen(name, coverEntry, filters: filters);
|
||||
if (!device.showPinShortcutFeedback) {
|
||||
showFeedback(context, context.l10n.genericSuccessFeedback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:convert';
|
|||
import 'package:aves/app_mode.dart';
|
||||
import 'package:aves/model/actions/entry_actions.dart';
|
||||
import 'package:aves/model/actions/move_type.dart';
|
||||
import 'package:aves/model/device.dart';
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/highlight.dart';
|
||||
|
@ -124,7 +125,10 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
final name = result.item2;
|
||||
if (name.isEmpty) return;
|
||||
|
||||
unawaited(androidAppService.pinToHomeScreen(name, entry, uri: entry.uri));
|
||||
await androidAppService.pinToHomeScreen(name, entry, uri: entry.uri);
|
||||
if (!device.showPinShortcutFeedback) {
|
||||
showFeedback(context, context.l10n.genericSuccessFeedback);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _flip(BuildContext context, AvesEntry entry) async {
|
||||
|
|
|
@ -88,6 +88,7 @@ class ViewerTopOverlay extends StatelessWidget {
|
|||
case EntryAction.rotateScreen:
|
||||
return settings.isRotationLocked;
|
||||
case EntryAction.addShortcut:
|
||||
return device.canPinShortcut;
|
||||
case EntryAction.copyToClipboard:
|
||||
case EntryAction.edit:
|
||||
case EntryAction.info:
|
||||
|
|
|
@ -1025,7 +1025,7 @@ packages:
|
|||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: d644fedd9cb79a45b1b92788880e81b846a69d9b
|
||||
resolved-ref: fba50f0e380d8cbd6a5bbda32f97a9c5e4d033e2
|
||||
url: "git://github.com/deckerst/aves_streams_channel.git"
|
||||
source: git
|
||||
version: "0.3.0"
|
||||
|
@ -1203,7 +1203,7 @@ packages:
|
|||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.3.1"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
Loading…
Reference in a new issue