diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt index 95f14d79f..0a2dfb4ee 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt @@ -53,18 +53,16 @@ class MainActivity : FlutterActivity() { // dart -> platform -> dart MethodChannel(messenger, AppAdapterHandler.CHANNEL).setMethodCallHandler(AppAdapterHandler(this)) - MethodChannel(messenger, AppShortcutHandler.CHANNEL).setMethodCallHandler(AppShortcutHandler(this)) MethodChannel(messenger, DebugHandler.CHANNEL).setMethodCallHandler(DebugHandler(this)) MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler()) MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this)) - MethodChannel(messenger, ImageFileHandler.CHANNEL).setMethodCallHandler(ImageFileHandler(this)) MethodChannel(messenger, GeocodingHandler.CHANNEL).setMethodCallHandler(GeocodingHandler(this)) MethodChannel(messenger, GlobalSearchHandler.CHANNEL).setMethodCallHandler(GlobalSearchHandler(this)) + MethodChannel(messenger, MediaFileHandler.CHANNEL).setMethodCallHandler(MediaFileHandler(this)) MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(this)) MethodChannel(messenger, MetadataEditHandler.CHANNEL).setMethodCallHandler(MetadataEditHandler(this)) MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this)) MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(this)) - MethodChannel(messenger, TimeHandler.CHANNEL).setMethodCallHandler(TimeHandler()) MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(WindowHandler(this)) // result streaming: dart -> platform ->->-> dart diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt index 3b1decf34..50d2be701 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt @@ -4,17 +4,25 @@ import android.content.* import android.content.pm.ApplicationInfo import android.content.res.Configuration import android.content.res.Resources +import android.graphics.BitmapFactory import android.net.Uri +import android.os.Build import android.os.Handler import android.os.Looper import android.util.Log import androidx.core.content.FileProvider +import androidx.core.content.pm.ShortcutInfoCompat +import androidx.core.content.pm.ShortcutManagerCompat +import androidx.core.graphics.drawable.IconCompat import com.bumptech.glide.Glide import com.bumptech.glide.load.DecodeFormat import com.bumptech.glide.request.RequestOptions +import deckers.thibault.aves.MainActivity +import deckers.thibault.aves.R import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import deckers.thibault.aves.channel.calls.Coresult.Companion.safeSuspend import deckers.thibault.aves.model.FieldMap +import deckers.thibault.aves.utils.BitmapUtils import deckers.thibault.aves.utils.BitmapUtils.getBytes import deckers.thibault.aves.utils.LogUtils import io.flutter.plugin.common.MethodCall @@ -39,6 +47,8 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { "openMap" -> safe(call, result, ::openMap) "setAs" -> safe(call, result, ::setAs) "share" -> safe(call, result, ::share) + "canPin" -> safe(call, result, ::canPin) + "pin" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::pin) } else -> result.notImplemented() } } @@ -307,6 +317,64 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler { } } + // shortcuts + + private fun isPinSupported() = ShortcutManagerCompat.isRequestPinShortcutSupported(context) + + private fun canPin(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { + result.success(isPinSupported()) + } + + private fun pin(call: MethodCall, result: MethodChannel.Result) { + val label = call.argument("label") + val iconBytes = call.argument("iconBytes") + val filters = call.argument>("filters") + if (label == null || filters == null) { + result.error("pin-args", "failed because of missing arguments", null) + return + } + + if (!isPinSupported()) { + result.error("pin-unsupported", "failed because the launcher does not support pinning shortcuts", null) + return + } + + var icon: IconCompat? = null + if (iconBytes?.isNotEmpty() == true) { + var bitmap = BitmapFactory.decodeByteArray(iconBytes, 0, iconBytes.size) + bitmap = BitmapUtils.centerSquareCrop(context, bitmap, 256) + if (bitmap != null) { + // adaptive, so the bitmap is used as background and covers the whole icon + icon = IconCompat.createWithAdaptiveBitmap(bitmap) + } + } + if (icon == null) { + // shortcut adaptive icons are placed in `mipmap`, not `drawable`, + // so that foreground is rendered at the intended scale + val supportAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O + + icon = IconCompat.createWithResource(context, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_collection else R.drawable.ic_shortcut_collection) + } + + val intent = Intent(Intent.ACTION_MAIN, null, context, MainActivity::class.java) + .putExtra("page", "/collection") + .putExtra("filters", filters.toTypedArray()) + // on API 25, `String[]` or `ArrayList` extras are null when using the shortcut + // so we use a joined `String` as fallback + .putExtra("filtersString", filters.joinToString(MainActivity.EXTRA_STRING_ARRAY_SEPARATOR)) + + // multiple shortcuts sharing the same ID cannot be created with different labels or icons + // so we provide a unique ID for each one, and let the user manage duplicates (i.e. same filter set), if any + val shortcut = ShortcutInfoCompat.Builder(context, UUID.randomUUID().toString()) + .setShortLabel(label) + .setIcon(icon) + .setIntent(intent) + .build() + ShortcutManagerCompat.requestPinShortcut(context, shortcut, null) + + result.success(true) + } + companion object { private val LOG_TAG = LogUtils.createTag() const val CHANNEL = "deckers.thibault/aves/app" diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppShortcutHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppShortcutHandler.kt deleted file mode 100644 index 452d35213..000000000 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppShortcutHandler.kt +++ /dev/null @@ -1,90 +0,0 @@ -package deckers.thibault.aves.channel.calls - -import android.content.Context -import android.content.Intent -import android.graphics.BitmapFactory -import android.os.Build -import androidx.core.content.pm.ShortcutInfoCompat -import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.graphics.drawable.IconCompat -import deckers.thibault.aves.MainActivity -import deckers.thibault.aves.R -import deckers.thibault.aves.channel.calls.Coresult.Companion.safe -import deckers.thibault.aves.utils.BitmapUtils.centerSquareCrop -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import java.util.* - -class AppShortcutHandler(private val context: Context) : MethodCallHandler { - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when (call.method) { - "canPin" -> safe(call, result, ::canPin) - "pin" -> GlobalScope.launch(Dispatchers.IO) { safe(call, result, ::pin) } - else -> result.notImplemented() - } - } - - private fun isSupported() = ShortcutManagerCompat.isRequestPinShortcutSupported(context) - - private fun canPin(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { - result.success(isSupported()) - } - - private fun pin(call: MethodCall, result: MethodChannel.Result) { - val label = call.argument("label") - val iconBytes = call.argument("iconBytes") - val filters = call.argument>("filters") - if (label == null || filters == null) { - result.error("pin-args", "failed because of missing arguments", null) - return - } - - if (!isSupported()) { - result.error("pin-unsupported", "failed because the launcher does not support pinning shortcuts", null) - return - } - - var icon: IconCompat? = null - if (iconBytes?.isNotEmpty() == true) { - var bitmap = BitmapFactory.decodeByteArray(iconBytes, 0, iconBytes.size) - bitmap = centerSquareCrop(context, bitmap, 256) - if (bitmap != null) { - // adaptive, so the bitmap is used as background and covers the whole icon - icon = IconCompat.createWithAdaptiveBitmap(bitmap) - } - } - if (icon == null) { - // shortcut adaptive icons are placed in `mipmap`, not `drawable`, - // so that foreground is rendered at the intended scale - val supportAdaptiveIcon = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O - - icon = IconCompat.createWithResource(context, if (supportAdaptiveIcon) R.mipmap.ic_shortcut_collection else R.drawable.ic_shortcut_collection) - } - - val intent = Intent(Intent.ACTION_MAIN, null, context, MainActivity::class.java) - .putExtra("page", "/collection") - .putExtra("filters", filters.toTypedArray()) - // on API 25, `String[]` or `ArrayList` extras are null when using the shortcut - // so we use a joined `String` as fallback - .putExtra("filtersString", filters.joinToString(MainActivity.EXTRA_STRING_ARRAY_SEPARATOR)) - - // multiple shortcuts sharing the same ID cannot be created with different labels or icons - // so we provide a unique ID for each one, and let the user manage duplicates (i.e. same filter set), if any - val shortcut = ShortcutInfoCompat.Builder(context, UUID.randomUUID().toString()) - .setShortLabel(label) - .setIcon(icon) - .setIntent(intent) - .build() - ShortcutManagerCompat.requestPinShortcut(context, shortcut, null) - - result.success(true) - } - - companion object { - const val CHANNEL = "deckers.thibault/aves/shortcut" - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt index ac795ac44..3f1dda8a1 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt @@ -5,15 +5,21 @@ import deckers.thibault.aves.channel.calls.Coresult.Companion.safe import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import java.util.* class DeviceHandler : MethodCallHandler { override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { when (call.method) { + "getDefaultTimeZone" -> safe(call, result, ::getDefaultTimeZone) "getPerformanceClass" -> safe(call, result, ::getPerformanceClass) else -> result.notImplemented() } } + private fun getDefaultTimeZone(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { + result.success(TimeZone.getDefault().id) + } + private fun getPerformanceClass(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { result.success(Build.VERSION.MEDIA_PERFORMANCE_CLASS) diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFileHandler.kt similarity index 98% rename from android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt rename to android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFileHandler.kt index 0858fcd9d..7fe3715fc 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/ImageFileHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaFileHandler.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import kotlin.math.roundToInt -class ImageFileHandler(private val activity: Activity) : MethodCallHandler { +class MediaFileHandler(private val activity: Activity) : MethodCallHandler { private val density = activity.resources.displayMetrics.density private val regionFetcher = RegionFetcher(activity) @@ -196,6 +196,6 @@ class ImageFileHandler(private val activity: Activity) : MethodCallHandler { } companion object { - const val CHANNEL = "deckers.thibault/aves/image" + const val CHANNEL = "deckers.thibault/aves/media_file" } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaStoreHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaStoreHandler.kt index 9ac2992a0..9de8a687b 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaStoreHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MediaStoreHandler.kt @@ -38,6 +38,6 @@ class MediaStoreHandler(private val activity: Activity) : MethodCallHandler { } companion object { - const val CHANNEL = "deckers.thibault/aves/mediastore" + const val CHANNEL = "deckers.thibault/aves/media_store" } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/TimeHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/TimeHandler.kt deleted file mode 100644 index 6d4736dc6..000000000 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/TimeHandler.kt +++ /dev/null @@ -1,24 +0,0 @@ -package deckers.thibault.aves.channel.calls - -import deckers.thibault.aves.channel.calls.Coresult.Companion.safe -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import java.util.* - -class TimeHandler : MethodCallHandler { - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - when (call.method) { - "getDefaultTimeZone" -> safe(call, result, ::getDefaultTimeZone) - else -> result.notImplemented() - } - } - - private fun getDefaultTimeZone(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) { - result.success(TimeZone.getDefault().id) - } - - companion object { - const val CHANNEL = "deckers.thibault/aves/time" - } -} \ No newline at end of file diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageByteStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageByteStreamHandler.kt index 66f81b146..84742cb21 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageByteStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageByteStreamHandler.kt @@ -187,7 +187,7 @@ class ImageByteStreamHandler(private val activity: Activity, private val argumen companion object { private val LOG_TAG = LogUtils.createTag() - const val CHANNEL = "deckers.thibault/aves/image_byte_stream" + const val CHANNEL = "deckers.thibault/aves/media_byte_stream" private const val BUFFER_SIZE = 2 shl 17 // 256kB diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageOpStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageOpStreamHandler.kt index 7170a3105..9786cd59e 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageOpStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ImageOpStreamHandler.kt @@ -177,6 +177,6 @@ class ImageOpStreamHandler(private val activity: Activity, private val arguments companion object { private val LOG_TAG = LogUtils.createTag() - const val CHANNEL = "deckers.thibault/aves/image_op_stream" + const val CHANNEL = "deckers.thibault/aves/media_op_stream" } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreChangeStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreChangeStreamHandler.kt index 432d0e04b..7fdcddffc 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreChangeStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreChangeStreamHandler.kt @@ -59,6 +59,6 @@ class MediaStoreChangeStreamHandler(private val context: Context) : EventChannel companion object { private val LOG_TAG = LogUtils.createTag() - const val CHANNEL = "deckers.thibault/aves/mediastorechange" + const val CHANNEL = "deckers.thibault/aves/media_store_change" } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt index dc5a0caa8..027f26506 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/MediaStoreStreamHandler.kt @@ -62,6 +62,6 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E companion object { private val LOG_TAG = LogUtils.createTag() - const val CHANNEL = "deckers.thibault/aves/mediastorestream" + const val CHANNEL = "deckers.thibault/aves/media_store_stream" } } \ No newline at end of file diff --git a/lib/image_providers/region_provider.dart b/lib/image_providers/region_provider.dart index f36ee71ef..e583467f7 100644 --- a/lib/image_providers/region_provider.dart +++ b/lib/image_providers/region_provider.dart @@ -33,7 +33,7 @@ class RegionProvider extends ImageProvider { final mimeType = key.mimeType; final pageId = key.pageId; try { - final bytes = await imageFileService.getRegion( + final bytes = await mediaFileService.getRegion( uri, mimeType, key.rotationDegrees, @@ -56,11 +56,11 @@ class RegionProvider extends ImageProvider { @override void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, RegionProviderKey key, ImageErrorListener handleError) { - imageFileService.resumeLoading(key); + mediaFileService.resumeLoading(key); super.resolveStreamForKey(configuration, stream, key, handleError); } - void pause() => imageFileService.cancelRegion(key); + void pause() => mediaFileService.cancelRegion(key); } @immutable diff --git a/lib/image_providers/thumbnail_provider.dart b/lib/image_providers/thumbnail_provider.dart index 2dd61c259..57fe937bd 100644 --- a/lib/image_providers/thumbnail_provider.dart +++ b/lib/image_providers/thumbnail_provider.dart @@ -35,7 +35,7 @@ class ThumbnailProvider extends ImageProvider { final mimeType = key.mimeType; final pageId = key.pageId; try { - final bytes = await imageFileService.getThumbnail( + final bytes = await mediaFileService.getThumbnail( uri: uri, mimeType: mimeType, pageId: pageId, @@ -57,11 +57,11 @@ class ThumbnailProvider extends ImageProvider { @override void resolveStreamForKey(ImageConfiguration configuration, ImageStream stream, ThumbnailProviderKey key, ImageErrorListener handleError) { - imageFileService.resumeLoading(key); + mediaFileService.resumeLoading(key); super.resolveStreamForKey(configuration, stream, key, handleError); } - void pause() => imageFileService.cancelThumbnail(key); + void pause() => mediaFileService.cancelThumbnail(key); } @immutable diff --git a/lib/image_providers/uri_image_provider.dart b/lib/image_providers/uri_image_provider.dart index 5271e74d3..416d407f1 100644 --- a/lib/image_providers/uri_image_provider.dart +++ b/lib/image_providers/uri_image_provider.dart @@ -49,7 +49,7 @@ class UriImage extends ImageProvider with EquatableMixin { assert(key == this); try { - final bytes = await imageFileService.getImage( + final bytes = await mediaFileService.getImage( uri, mimeType, rotationDegrees, diff --git a/lib/model/entry.dart b/lib/model/entry.dart index 387cfe66e..0ec3f87ae 100644 --- a/lib/model/entry.dart +++ b/lib/model/entry.dart @@ -615,7 +615,7 @@ class AvesEntry { Future delete() { final completer = Completer(); - imageFileService.delete([this]).listen( + mediaFileService.delete([this]).listen( (event) => completer.complete(event.success), onError: completer.completeError, onDone: () { diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart index 41e32038f..fa2484190 100644 --- a/lib/model/settings/settings.dart +++ b/lib/model/settings/settings.dart @@ -11,7 +11,6 @@ import 'package:aves/model/settings/enums.dart'; import 'package:aves/model/settings/map_style.dart'; import 'package:aves/model/settings/screen_on.dart'; import 'package:aves/model/source/enums.dart'; -import 'package:aves/services/device_service.dart'; import 'package:aves/services/common/services.dart'; import 'package:collection/collection.dart'; import 'package:firebase_core/firebase_core.dart'; @@ -128,7 +127,7 @@ class Settings extends ChangeNotifier { Future setContextualDefaults() async { // performance - final performanceClass = await DeviceService.getPerformanceClass(); + final performanceClass = await deviceService.getPerformanceClass(); enableOverlayBlurEffect = performanceClass >= 30; // availability diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index b097f6896..78672a7a1 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -159,7 +159,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM Future renameEntry(AvesEntry entry, String newName, {required bool persist}) async { if (newName == entry.filenameWithoutExtension) return true; - final newFields = await imageFileService.rename(entry, '$newName${entry.extension}'); + final newFields = await mediaFileService.rename(entry, '$newName${entry.extension}'); if (newFields.isEmpty) return false; await _moveEntry(entry, newFields, persist: persist); diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 6f1e7c1ae..ac9e97ab6 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -25,7 +25,7 @@ class MediaStoreSource extends CollectionSource { await metadataDb.init(); await favourites.init(); await covers.init(); - final currentTimeZone = await timeService.getDefaultTimeZone(); + final currentTimeZone = await deviceService.getDefaultTimeZone(); if (currentTimeZone != null) { final catalogTimeZone = settings.catalogTimeZone; if (currentTimeZone != catalogTimeZone) { @@ -153,7 +153,7 @@ class MediaStoreSource extends CollectionSource { for (final kv in uriByContentId.entries) { final contentId = kv.key; final uri = kv.value; - final sourceEntry = await imageFileService.getEntry(uri, null); + final sourceEntry = await mediaFileService.getEntry(uri, null); if (sourceEntry != null) { final existingEntry = allEntries.firstWhereOrNull((entry) => entry.contentId == contentId); // compare paths because some apps move files without updating their `last modified date` diff --git a/lib/services/android_app_service.dart b/lib/services/android_app_service.dart index 32e930efc..a8883adf8 100644 --- a/lib/services/android_app_service.dart +++ b/lib/services/android_app_service.dart @@ -1,10 +1,12 @@ import 'dart:typed_data'; import 'package:aves/model/entry.dart'; +import 'package:aves/model/filters/filters.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/math_utils.dart'; import 'package:collection/collection.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:latlong2/latlong.dart'; @@ -136,4 +138,49 @@ class AndroidAppService { } return false; } + + // app shortcuts + + // this ability will not change over the lifetime of the app + static bool? _canPin; + + static Future canPinToHomeScreen() async { + if (_canPin != null) return SynchronousFuture(_canPin!); + + try { + final result = await platform.invokeMethod('canPin'); + if (result != null) { + _canPin = result; + return result; + } + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + return false; + } + + static Future pinToHomeScreen(String label, AvesEntry? entry, Set filters) async { + Uint8List? iconBytes; + if (entry != null) { + final size = entry.isVideo ? 0.0 : 256.0; + iconBytes = await mediaFileService.getThumbnail( + uri: entry.uri, + mimeType: entry.mimeType, + pageId: entry.pageId, + rotationDegrees: entry.rotationDegrees, + isFlipped: entry.isFlipped, + dateModifiedSecs: entry.dateModifiedSecs, + extent: size, + ); + } + try { + await platform.invokeMethod('pin', { + 'label': label, + 'iconBytes': iconBytes, + 'filters': filters.map((filter) => filter.toJson()).toList(), + }); + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + } } diff --git a/lib/services/app_shortcut_service.dart b/lib/services/app_shortcut_service.dart deleted file mode 100644 index a1f0574c6..000000000 --- a/lib/services/app_shortcut_service.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'dart:typed_data'; - -import 'package:aves/model/entry.dart'; -import 'package:aves/model/filters/filters.dart'; -import 'package:aves/services/common/services.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -class AppShortcutService { - static const platform = MethodChannel('deckers.thibault/aves/shortcut'); - - // this ability will not change over the lifetime of the app - static bool? _canPin; - - static Future canPin() async { - if (_canPin != null) return SynchronousFuture(_canPin!); - - try { - final result = await platform.invokeMethod('canPin'); - if (result != null) { - _canPin = result; - return result; - } - } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); - } - return false; - } - - static Future pin(String label, AvesEntry? entry, Set filters) async { - Uint8List? iconBytes; - if (entry != null) { - final size = entry.isVideo ? 0.0 : 256.0; - iconBytes = await imageFileService.getThumbnail( - uri: entry.uri, - mimeType: entry.mimeType, - pageId: entry.pageId, - rotationDegrees: entry.rotationDegrees, - isFlipped: entry.isFlipped, - dateModifiedSecs: entry.dateModifiedSecs, - extent: size, - ); - } - try { - await platform.invokeMethod('pin', { - 'label': label, - 'iconBytes': iconBytes, - 'filters': filters.map((filter) => filter.toJson()).toList(), - }); - } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); - } - } -} diff --git a/lib/services/common/services.dart b/lib/services/common/services.dart index 40e045aec..a97b32bf8 100644 --- a/lib/services/common/services.dart +++ b/lib/services/common/services.dart @@ -1,13 +1,13 @@ import 'package:aves/model/availability.dart'; import 'package:aves/model/metadata_db.dart'; -import 'package:aves/services/embedded_data_service.dart'; -import 'package:aves/services/image_file_service.dart'; -import 'package:aves/services/media_store_service.dart'; +import 'package:aves/services/device_service.dart'; +import 'package:aves/services/media/embedded_data_service.dart'; +import 'package:aves/services/media/media_file_service.dart'; +import 'package:aves/services/media/media_store_service.dart'; import 'package:aves/services/metadata/metadata_edit_service.dart'; import 'package:aves/services/metadata/metadata_fetch_service.dart'; import 'package:aves/services/report_service.dart'; import 'package:aves/services/storage_service.dart'; -import 'package:aves/services/time_service.dart'; import 'package:aves/services/window_service.dart'; import 'package:get_it/get_it.dart'; import 'package:path/path.dart' as p; @@ -18,14 +18,14 @@ final p.Context pContext = getIt(); final AvesAvailability availability = getIt(); final MetadataDb metadataDb = getIt(); +final DeviceService deviceService = getIt(); final EmbeddedDataService embeddedDataService = getIt(); -final ImageFileService imageFileService = getIt(); +final MediaFileService mediaFileService = getIt(); final MediaStoreService mediaStoreService = getIt(); final MetadataEditService metadataEditService = getIt(); final MetadataFetchService metadataFetchService = getIt(); final ReportService reportService = getIt(); final StorageService storageService = getIt(); -final TimeService timeService = getIt(); final WindowService windowService = getIt(); void initPlatformServices() { @@ -33,13 +33,13 @@ void initPlatformServices() { getIt.registerLazySingleton(() => LiveAvesAvailability()); getIt.registerLazySingleton(() => SqfliteMetadataDb()); + getIt.registerLazySingleton(() => PlatformDeviceService()); getIt.registerLazySingleton(() => PlatformEmbeddedDataService()); - getIt.registerLazySingleton(() => PlatformImageFileService()); + getIt.registerLazySingleton(() => PlatformMediaFileService()); getIt.registerLazySingleton(() => PlatformMediaStoreService()); getIt.registerLazySingleton(() => PlatformMetadataEditService()); getIt.registerLazySingleton(() => PlatformMetadataFetchService()); getIt.registerLazySingleton(() => CrashlyticsReportService()); getIt.registerLazySingleton(() => PlatformStorageService()); - getIt.registerLazySingleton(() => PlatformTimeService()); getIt.registerLazySingleton(() => PlatformWindowService()); } diff --git a/lib/services/device_service.dart b/lib/services/device_service.dart index f375f73df..cacbc5882 100644 --- a/lib/services/device_service.dart +++ b/lib/services/device_service.dart @@ -1,10 +1,27 @@ import 'package:aves/services/common/services.dart'; import 'package:flutter/services.dart'; -class DeviceService { +abstract class DeviceService { + Future getDefaultTimeZone(); + + Future getPerformanceClass(); +} + +class PlatformDeviceService implements DeviceService { static const platform = MethodChannel('deckers.thibault/aves/device'); - static Future getPerformanceClass() async { + @override + Future getDefaultTimeZone() async { + try { + return await platform.invokeMethod('getDefaultTimeZone'); + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + return null; + } + + @override + Future getPerformanceClass() async { try { await platform.invokeMethod('getPerformanceClass'); final result = await platform.invokeMethod('getPerformanceClass'); diff --git a/lib/services/embedded_data_service.dart b/lib/services/media/embedded_data_service.dart similarity index 100% rename from lib/services/embedded_data_service.dart rename to lib/services/media/embedded_data_service.dart diff --git a/lib/services/image_file_service.dart b/lib/services/media/media_file_service.dart similarity index 97% rename from lib/services/image_file_service.dart rename to lib/services/media/media_file_service.dart index d07454375..7301795f9 100644 --- a/lib/services/image_file_service.dart +++ b/lib/services/media/media_file_service.dart @@ -13,7 +13,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:streams_channel/streams_channel.dart'; -abstract class ImageFileService { +abstract class MediaFileService { Future getEntry(String uri, String? mimeType); Future getSvg( @@ -92,10 +92,10 @@ abstract class ImageFileService { Future> rename(AvesEntry entry, String newName); } -class PlatformImageFileService implements ImageFileService { - static const platform = MethodChannel('deckers.thibault/aves/image'); - static final StreamsChannel _byteStreamChannel = StreamsChannel('deckers.thibault/aves/image_byte_stream'); - static final StreamsChannel _opStreamChannel = StreamsChannel('deckers.thibault/aves/image_op_stream'); +class PlatformMediaFileService implements MediaFileService { + static const platform = MethodChannel('deckers.thibault/aves/media_file'); + static final StreamsChannel _byteStreamChannel = StreamsChannel('deckers.thibault/aves/media_byte_stream'); + static final StreamsChannel _opStreamChannel = StreamsChannel('deckers.thibault/aves/media_op_stream'); static const double thumbnailDefaultSize = 64.0; static Map _toPlatformEntryMap(AvesEntry entry) { diff --git a/lib/services/media_store_service.dart b/lib/services/media/media_store_service.dart similarity index 77% rename from lib/services/media_store_service.dart rename to lib/services/media/media_store_service.dart index 323d8ccbe..67ccb7b5a 100644 --- a/lib/services/media_store_service.dart +++ b/lib/services/media/media_store_service.dart @@ -12,11 +12,14 @@ abstract class MediaStoreService { // knownEntries: map of contentId -> dateModifiedSecs Stream getEntries(Map knownEntries); + + // returns media URI + Future scanFile(String path, String mimeType); } class PlatformMediaStoreService implements MediaStoreService { - static const platform = MethodChannel('deckers.thibault/aves/mediastore'); - static final StreamsChannel _streamChannel = StreamsChannel('deckers.thibault/aves/mediastorestream'); + static const platform = MethodChannel('deckers.thibault/aves/media_store'); + static final StreamsChannel _streamChannel = StreamsChannel('deckers.thibault/aves/media_store_stream'); @override Future> checkObsoleteContentIds(List knownContentIds) async { @@ -55,4 +58,19 @@ class PlatformMediaStoreService implements MediaStoreService { return Stream.error(e); } } + + // returns media URI + @override + Future scanFile(String path, String mimeType) async { + try { + final result = await platform.invokeMethod('scanFile', { + 'path': path, + 'mimeType': mimeType, + }); + if (result != null) return Uri.tryParse(result); + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + return null; + } } diff --git a/lib/services/metadata/svg_metadata_service.dart b/lib/services/metadata/svg_metadata_service.dart index 0d34d863c..94710cb10 100644 --- a/lib/services/metadata/svg_metadata_service.dart +++ b/lib/services/metadata/svg_metadata_service.dart @@ -18,7 +18,7 @@ class SvgMetadataService { static Future getSize(AvesEntry entry) async { try { - final data = await imageFileService.getSvg(entry.uri, entry.mimeType); + final data = await mediaFileService.getSvg(entry.uri, entry.mimeType); final document = XmlDocument.parse(utf8.decode(data)); final root = document.rootElement; @@ -64,7 +64,7 @@ class SvgMetadataService { } try { - final data = await imageFileService.getSvg(entry.uri, entry.mimeType); + final data = await mediaFileService.getSvg(entry.uri, entry.mimeType); final document = XmlDocument.parse(utf8.decode(data)); final root = document.rootElement; diff --git a/lib/services/storage_service.dart b/lib/services/storage_service.dart index 977239d57..680a1b3dd 100644 --- a/lib/services/storage_service.dart +++ b/lib/services/storage_service.dart @@ -4,7 +4,6 @@ import 'dart:typed_data'; import 'package:aves/services/common/output_buffer.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:streams_channel/streams_channel.dart'; @@ -27,9 +26,6 @@ abstract class StorageService { // returns number of deleted directories Future deleteEmptyDirectories(Iterable dirPaths); - // returns media URI - Future scanFile(String path, String mimeType); - // return whether operation succeeded (`null` if user cancelled) Future createFile(String name, String mimeType, Uint8List bytes); @@ -154,22 +150,6 @@ class PlatformStorageService implements StorageService { return 0; } - // returns media URI - @override - Future scanFile(String path, String mimeType) async { - debugPrint('scanFile with path=$path, mimeType=$mimeType'); - try { - final result = await platform.invokeMethod('scanFile', { - 'path': path, - 'mimeType': mimeType, - }); - if (result != null) return Uri.tryParse(result); - } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); - } - return null; - } - @override Future createFile(String name, String mimeType, Uint8List bytes) async { try { diff --git a/lib/services/time_service.dart b/lib/services/time_service.dart deleted file mode 100644 index 2c38166a3..000000000 --- a/lib/services/time_service.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:aves/services/common/services.dart'; -import 'package:flutter/services.dart'; - -abstract class TimeService { - Future getDefaultTimeZone(); -} - -class PlatformTimeService implements TimeService { - static const platform = MethodChannel('deckers.thibault/aves/time'); - - @override - Future getDefaultTimeZone() async { - try { - return await platform.invokeMethod('getDefaultTimeZone'); - } on PlatformException catch (e, stack) { - await reportService.recordError(e, stack); - } - return null; - } -} diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 5321e632b..5a27e93ba 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -41,7 +41,7 @@ class _AvesAppState extends State { // observers are not registered when using the same list object with different items // the list itself needs to be reassigned List _navigatorObservers = []; - final EventChannel _mediaStoreChangeChannel = const EventChannel('deckers.thibault/aves/mediastorechange'); + final EventChannel _mediaStoreChangeChannel = const EventChannel('deckers.thibault/aves/media_store_change'); final EventChannel _newIntentChannel = const EventChannel('deckers.thibault/aves/intent'); final EventChannel _errorChannel = const EventChannel('deckers.thibault/aves/error'); final GlobalKey _navigatorKey = GlobalKey(debugLabel: 'app-navigator'); diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index 68d93af84..9ee8527c5 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -9,7 +9,7 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/enums.dart'; -import 'package:aves/services/app_shortcut_service.dart'; +import 'package:aves/services/android_app_service.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/widgets/collection/entry_set_action_delegate.dart'; import 'package:aves/widgets/collection/filter_bar.dart'; @@ -61,7 +61,7 @@ class _CollectionAppBarState extends State with SingleTickerPr vsync: this, ); _isSelectingNotifier.addListener(_onActivityChange); - _canAddShortcutsLoader = AppShortcutService.canPin(); + _canAddShortcutsLoader = AndroidAppService.canPinToHomeScreen(); _registerWidget(widget); WidgetsBinding.instance!.addPostFrameCallback((_) => _onFilterChanged()); } @@ -370,7 +370,7 @@ class _CollectionAppBarState extends State with SingleTickerPr final name = result.item2; if (name.isEmpty) return; - unawaited(AppShortcutService.pin(name, coverEntry, filters)); + unawaited(AndroidAppService.pinToHomeScreen(name, coverEntry, filters)); } void _goToSearch() { diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index 3b33d1b4c..968d67f80 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -122,7 +122,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware source.pauseMonitoring(); showOpReport( context: context, - opStream: imageFileService.move(todoEntries, copy: copy, destinationAlbum: destinationAlbum), + opStream: mediaFileService.move(todoEntries, copy: copy, destinationAlbum: destinationAlbum), itemCount: todoCount, onDone: (processed) async { final movedOps = processed.where((e) => e.success).toSet(); @@ -222,7 +222,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware source.pauseMonitoring(); showOpReport( context: context, - opStream: imageFileService.delete(selectedItems), + opStream: mediaFileService.delete(selectedItems), itemCount: todoCount, onDone: (processed) async { final deletedUris = processed.where((event) => event.success).map((event) => event.uri).toSet(); diff --git a/lib/widgets/common/thumbnail/image.dart b/lib/widgets/common/thumbnail/image.dart index 1b37ffc75..8eadc9c3a 100644 --- a/lib/widgets/common/thumbnail/image.dart +++ b/lib/widgets/common/thumbnail/image.dart @@ -155,7 +155,7 @@ class _ThumbnailImageState extends State { if (widget.cancellableNotifier?.value ?? false) { final key = await _currentProviderStream?.provider.provider.obtainKey(ImageConfiguration.empty); if (key is ThumbnailProviderKey) { - imageFileService.cancelThumbnail(key); + mediaFileService.cancelThumbnail(key); } } } diff --git a/lib/widgets/debug/cache.dart b/lib/widgets/debug/cache.dart index 0c4eda72d..90a08dd43 100644 --- a/lib/widgets/debug/cache.dart +++ b/lib/widgets/debug/cache.dart @@ -45,7 +45,7 @@ class _DebugCacheSectionState extends State with AutomaticKee ), const SizedBox(width: 8), ElevatedButton( - onPressed: imageFileService.clearSizedThumbnailDiskCache, + onPressed: mediaFileService.clearSizedThumbnailDiskCache, child: const Text('Clear'), ), ], diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index 393b11c96..a2f0bb183 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -167,7 +167,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate { source.pauseMonitoring(); showOpReport( context: context, - opStream: imageFileService.delete(todoEntries), + opStream: mediaFileService.delete(todoEntries), itemCount: todoCount, onDone: (processed) async { final deletedUris = processed.where((event) => event.success).map((event) => event.uri).toSet(); @@ -226,7 +226,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate { source.pauseMonitoring(); showOpReport( context: context, - opStream: imageFileService.move(todoEntries, copy: false, destinationAlbum: destinationAlbum), + opStream: mediaFileService.move(todoEntries, copy: false, destinationAlbum: destinationAlbum), itemCount: todoCount, onDone: (processed) async { final movedOps = processed.where((e) => e.success).toSet(); diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index 4a59f04c6..81d9eea44 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -125,7 +125,7 @@ class _HomePageState extends State { } Future _initViewerEntry({required String uri, required String? mimeType}) async { - final entry = await imageFileService.getEntry(uri, mimeType); + final entry = await mediaFileService.getEntry(uri, mimeType); if (entry != null) { // cataloguing is essential for coordinates and video rotation await entry.catalog(background: false, persist: false); diff --git a/lib/widgets/viewer/entry_action_delegate.dart b/lib/widgets/viewer/entry_action_delegate.dart index 49f797c4b..9a8660d64 100644 --- a/lib/widgets/viewer/entry_action_delegate.dart +++ b/lib/widgets/viewer/entry_action_delegate.dart @@ -204,7 +204,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix showOpReport( context: context, // TODO TLAD [SVG] export separately from raster images (sending bytes, like frame captures) - opStream: imageFileService.export( + opStream: mediaFileService.export( selection, mimeType: MimeTypes.jpeg, destinationAlbum: destinationAlbum, @@ -286,7 +286,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix MaterialPageRoute( settings: const RouteSettings(name: SourceViewerPage.routeName), builder: (context) => SourceViewerPage( - loader: () => imageFileService.getSvg(entry.uri, entry.mimeType).then(utf8.decode), + loader: () => mediaFileService.getSvg(entry.uri, entry.mimeType).then(utf8.decode), ), ), ); diff --git a/lib/widgets/viewer/printer.dart b/lib/widgets/viewer/printer.dart index 25c3c33fd..46a0dff56 100644 --- a/lib/widgets/viewer/printer.dart +++ b/lib/widgets/viewer/printer.dart @@ -74,7 +74,7 @@ class EntryPrinter with FeedbackMixin { Future _buildPageImage(AvesEntry entry) async { if (entry.isSvg) { - final bytes = await imageFileService.getSvg(entry.uri, entry.mimeType); + final bytes = await mediaFileService.getSvg(entry.uri, entry.mimeType); if (bytes.isNotEmpty) { return pdf.SvgImage(svg: utf8.decode(bytes)); } diff --git a/lib/widgets/viewer/video_action_delegate.dart b/lib/widgets/viewer/video_action_delegate.dart index b2e7a9b75..9a1b4048d 100644 --- a/lib/widgets/viewer/video_action_delegate.dart +++ b/lib/widgets/viewer/video_action_delegate.dart @@ -85,7 +85,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix } }; - final newFields = await imageFileService.captureFrame( + final newFields = await mediaFileService.captureFrame( entry, desiredName: '${entry.bestTitle}_${'$positionMillis'.padLeft(8, '0')}', exif: exif, diff --git a/test/fake/time_service.dart b/test/fake/device_service.dart similarity index 59% rename from test/fake/time_service.dart rename to test/fake/device_service.dart index 5e7eddee2..a2efd54c1 100644 --- a/test/fake/time_service.dart +++ b/test/fake/device_service.dart @@ -1,8 +1,8 @@ -import 'package:aves/services/time_service.dart'; +import 'package:aves/services/device_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; -class FakeTimeService extends Fake implements TimeService { +class FakeDeviceService extends Fake implements DeviceService { @override Future getDefaultTimeZone() => SynchronousFuture(''); } diff --git a/test/fake/image_file_service.dart b/test/fake/media_file_service.dart similarity index 84% rename from test/fake/image_file_service.dart rename to test/fake/media_file_service.dart index 88d6803e0..5d2933b66 100644 --- a/test/fake/image_file_service.dart +++ b/test/fake/media_file_service.dart @@ -1,11 +1,11 @@ import 'package:aves/model/entry.dart'; -import 'package:aves/services/image_file_service.dart'; +import 'package:aves/services/media/media_file_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'media_store_service.dart'; -class FakeImageFileService extends Fake implements ImageFileService { +class FakeMediaFileService extends Fake implements MediaFileService { @override Future> rename(AvesEntry entry, String newName) { final contentId = FakeMediaStoreService.nextContentId; diff --git a/test/fake/media_store_service.dart b/test/fake/media_store_service.dart index 2476a8ca0..8702860dd 100644 --- a/test/fake/media_store_service.dart +++ b/test/fake/media_store_service.dart @@ -1,7 +1,7 @@ import 'package:aves/model/entry.dart'; import 'package:aves/ref/mime_types.dart'; import 'package:aves/services/common/image_op_events.dart'; -import 'package:aves/services/media_store_service.dart'; +import 'package:aves/services/media/media_store_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/test/model/collection_source_test.dart b/test/model/collection_source_test.dart index c44cbaad4..2e5d01b55 100644 --- a/test/model/collection_source_test.dart +++ b/test/model/collection_source_test.dart @@ -8,12 +8,12 @@ import 'package:aves/model/metadata_db.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/enums.dart'; import 'package:aves/model/source/media_store_source.dart'; -import 'package:aves/services/image_file_service.dart'; -import 'package:aves/services/media_store_service.dart'; -import 'package:aves/services/metadata/metadata_fetch_service.dart'; import 'package:aves/services/common/services.dart'; +import 'package:aves/services/device_service.dart'; +import 'package:aves/services/media/media_file_service.dart'; +import 'package:aves/services/media/media_store_service.dart'; +import 'package:aves/services/metadata/metadata_fetch_service.dart'; import 'package:aves/services/storage_service.dart'; -import 'package:aves/services/time_service.dart'; import 'package:aves/services/window_service.dart'; import 'package:aves/utils/android_file_utils.dart'; import 'package:flutter/widgets.dart'; @@ -21,12 +21,12 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:path/path.dart' as p; import '../fake/availability.dart'; -import '../fake/image_file_service.dart'; +import '../fake/device_service.dart'; +import '../fake/media_file_service.dart'; import '../fake/media_store_service.dart'; import '../fake/metadata_db.dart'; import '../fake/metadata_fetch_service.dart'; import '../fake/storage_service.dart'; -import '../fake/time_service.dart'; import '../fake/window_service.dart'; void main() { @@ -40,11 +40,11 @@ void main() { getIt.registerLazySingleton(() => FakeAvesAvailability()); getIt.registerLazySingleton(() => FakeMetadataDb()); - getIt.registerLazySingleton(() => FakeImageFileService()); + getIt.registerLazySingleton(() => FakeDeviceService()); + getIt.registerLazySingleton(() => FakeMediaFileService()); getIt.registerLazySingleton(() => FakeMediaStoreService()); getIt.registerLazySingleton(() => FakeMetadataFetchService()); getIt.registerLazySingleton(() => FakeStorageService()); - getIt.registerLazySingleton(() => FakeTimeService()); getIt.registerLazySingleton(() => FakeWindowService()); await settings.init(); diff --git a/test_driver/app.dart b/test_driver/app.dart index 2eab39143..5aae278c7 100644 --- a/test_driver/app.dart +++ b/test_driver/app.dart @@ -4,7 +4,7 @@ import 'package:aves/main.dart' as app; import 'package:aves/model/settings/enums.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; -import 'package:aves/services/storage_service.dart'; +import 'package:aves/services/media/media_store_service.dart'; import 'package:aves/services/window_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/src/widgets/media_query.dart'; @@ -19,9 +19,9 @@ void main() { // scan files copied from test assets // we do it via the app instead of broadcasting via ADB // because `MEDIA_SCANNER_SCAN_FILE` intent got deprecated in API 29 - final storageService = PlatformStorageService(); - storageService.scanFile(p.join(targetPicturesDir, 'aves_logo.svg'), 'image/svg+xml'); - storageService.scanFile(p.join(targetPicturesDir, 'ipse.jpg'), 'image/jpeg'); + final mediaStoreService = PlatformMediaStoreService(); + mediaStoreService.scanFile(p.join(targetPicturesDir, 'aves_logo.svg'), 'image/svg+xml'); + mediaStoreService.scanFile(p.join(targetPicturesDir, 'ipse.jpg'), 'image/jpeg'); configureAndLaunch(); }