Merge branch 'develop'
This commit is contained in:
commit
a02131c7c8
89 changed files with 747 additions and 391 deletions
2
.flutter
2
.flutter
|
@ -1 +1 @@
|
|||
Subproject commit abb292a07e20d696c4568099f918f6c5f330e6b0
|
||||
Subproject commit ba393198430278b6595976de84fe170f553cc728
|
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
|||
name: Build and release artifacts.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-java@v3
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
@ -75,7 +75,7 @@ jobs:
|
|||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload app bundle
|
||||
uses: actions/upload-artifact@v3
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: appbundle
|
||||
path: outputs/app-play-release.aab
|
||||
|
@ -85,15 +85,15 @@ jobs:
|
|||
needs: [ build ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Get appbundle from artifacts.
|
||||
uses: actions/download-artifact@v3
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: appbundle
|
||||
|
||||
- name: Release app to beta channel.
|
||||
uses: r0adkll/upload-google-play@v1.1.1
|
||||
uses: r0adkll/upload-google-play@v1.1.3
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAYSTORE_ACCOUNT_KEY }}
|
||||
packageName: deckers.thibault.aves
|
||||
|
|
16
CHANGELOG.md
16
CHANGELOG.md
|
@ -4,6 +4,22 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
## <a id="v1.10.6"></a>[v1.10.6] - 2024-03-11
|
||||
|
||||
### Added
|
||||
|
||||
- Cataloguing: detect/filter HDR videos
|
||||
|
||||
### Changed
|
||||
|
||||
- check Media Store changes when resuming app
|
||||
- disabling animations also applies to pop up menus
|
||||
- upgraded Flutter to stable v3.19.3
|
||||
|
||||
### Fixed
|
||||
|
||||
- engine leak from analysis worker
|
||||
|
||||
## <a id="v1.10.5"></a>[v1.10.5] - 2024-02-22
|
||||
|
||||
### Added
|
||||
|
|
|
@ -44,11 +44,22 @@ class AnalysisWorker(context: Context, parameters: WorkerParameters) : Coroutine
|
|||
workCont = cont
|
||||
onStart()
|
||||
}
|
||||
dispose()
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private suspend fun dispose() {
|
||||
Log.i(LOG_TAG, "Clean analysis worker $id")
|
||||
flutterEngine?.let {
|
||||
FlutterUtils.runOnUiThread {
|
||||
it.destroy()
|
||||
}
|
||||
flutterEngine = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun onStart() {
|
||||
Log.i(LOG_TAG, "Start analysis worker")
|
||||
Log.i(LOG_TAG, "Start analysis worker $id")
|
||||
runBlocking {
|
||||
FlutterUtils.initFlutterEngine(applicationContext, SHARED_PREFERENCES_KEY, CALLBACK_HANDLE_KEY) {
|
||||
flutterEngine = it
|
||||
|
|
|
@ -175,6 +175,18 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mediaStoreChangeStreamHandler.onAppResume()
|
||||
settingsChangeStreamHandler.onAppResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
mediaStoreChangeStreamHandler.onAppPause()
|
||||
settingsChangeStreamHandler.onAppPause()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
Log.i(LOG_TAG, "onStop")
|
||||
super.onStop()
|
||||
|
|
|
@ -15,8 +15,12 @@ import android.util.Log
|
|||
import androidx.exifinterface.media.ExifInterface
|
||||
import com.drew.metadata.file.FileTypeDirectory
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import deckers.thibault.aves.metadata.*
|
||||
import deckers.thibault.aves.metadata.ExifInterfaceHelper
|
||||
import deckers.thibault.aves.metadata.MediaMetadataRetrieverHelper
|
||||
import deckers.thibault.aves.metadata.Metadata
|
||||
import deckers.thibault.aves.metadata.Mp4ParserHelper
|
||||
import deckers.thibault.aves.metadata.Mp4ParserHelper.dumpBoxes
|
||||
import deckers.thibault.aves.metadata.PixyMetaHelper
|
||||
import deckers.thibault.aves.metadata.metadataextractor.Helper
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
|
|
|
@ -12,7 +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 deckers.thibault.aves.utils.MemoryUtils
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
|
@ -35,6 +35,8 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
||||
"isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled)
|
||||
"requestMediaManagePermission" -> safe(call, result, ::requestMediaManagePermission)
|
||||
"getAvailableHeapSize" -> safe(call, result, ::getAvailableHeapSize)
|
||||
"requestGarbageCollection" -> safe(call, result, ::requestGarbageCollection)
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +125,15 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
result.success(true)
|
||||
}
|
||||
|
||||
private fun getAvailableHeapSize(@Suppress("unused_parameter") methodCall: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(MemoryUtils.getAvailableHeapSize())
|
||||
}
|
||||
|
||||
private fun requestGarbageCollection(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
Runtime.getRuntime().gc()
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL = "deckers.thibault/aves/device"
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ package deckers.thibault.aves.channel.calls
|
|||
import android.content.Context
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import deckers.thibault.aves.model.provider.MediaStoreImageProvider
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
@ -20,13 +22,15 @@ class MediaStoreHandler(private val context: Context) : MethodCallHandler {
|
|||
when (call.method) {
|
||||
"checkObsoleteContentIds" -> ioScope.launch { safe(call, result, ::checkObsoleteContentIds) }
|
||||
"checkObsoletePaths" -> ioScope.launch { safe(call, result, ::checkObsoletePaths) }
|
||||
"getChangedUris" -> ioScope.launch { safe(call, result, ::getChangedUris) }
|
||||
"getGeneration" -> ioScope.launch { safe(call, result, ::getGeneration) }
|
||||
"scanFile" -> ioScope.launch { safe(call, result, ::scanFile) }
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkObsoleteContentIds(call: MethodCall, result: MethodChannel.Result) {
|
||||
val knownContentIds = call.argument<List<Int?>>("knownContentIds")
|
||||
val knownContentIds = call.argument<List<Number?>>("knownContentIds")?.map { it?.toLong() }
|
||||
if (knownContentIds == null) {
|
||||
result.error("checkObsoleteContentIds-args", "missing arguments", null)
|
||||
return
|
||||
|
@ -35,7 +39,7 @@ class MediaStoreHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
private fun checkObsoletePaths(call: MethodCall, result: MethodChannel.Result) {
|
||||
val knownPathById = call.argument<Map<Int?, String?>>("knownPathById")
|
||||
val knownPathById = call.argument<Map<Number?, String?>>("knownPathById")?.mapKeys { it.key?.toLong() }
|
||||
if (knownPathById == null) {
|
||||
result.error("checkObsoletePaths-args", "missing arguments", null)
|
||||
return
|
||||
|
@ -43,6 +47,25 @@ class MediaStoreHandler(private val context: Context) : MethodCallHandler {
|
|||
result.success(MediaStoreImageProvider().checkObsoletePaths(context, knownPathById))
|
||||
}
|
||||
|
||||
private fun getChangedUris(call: MethodCall, result: MethodChannel.Result) {
|
||||
val sinceGeneration = call.argument<Int>("sinceGeneration")
|
||||
if (sinceGeneration == null) {
|
||||
result.error("getChangedUris-args", "missing arguments", null)
|
||||
return
|
||||
}
|
||||
val uris = MediaStoreImageProvider().getChangedUris(context, sinceGeneration)
|
||||
result.success(uris)
|
||||
}
|
||||
|
||||
private fun getGeneration(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
val generation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
MediaStore.getGeneration(context, MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
result.success(generation)
|
||||
}
|
||||
|
||||
private fun scanFile(call: MethodCall, result: MethodChannel.Result) {
|
||||
val path = call.argument<String>("path")
|
||||
val mimeType = call.argument<String>("mimeType")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.content.Context
|
||||
import android.media.MediaFormat
|
||||
import android.media.MediaMetadataRetriever
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
|
@ -559,7 +560,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
|
|||
|
||||
// identification of embedded gain map
|
||||
if (xmpMeta.hasHdrGainMap()) {
|
||||
flags = flags or MASK_HAS_HDR_GAIN_MAP
|
||||
flags = flags or MASK_IS_HDR
|
||||
}
|
||||
} catch (e: XMPException) {
|
||||
Log.w(LOG_TAG, "failed to read XMP directory for uri=$uri", e)
|
||||
|
@ -797,6 +798,14 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
retriever.getSafeInt(MediaMetadataRetriever.METADATA_KEY_COLOR_TRANSFER) {
|
||||
if (it == MediaFormat.COLOR_TRANSFER_ST2084 || it == MediaFormat.COLOR_TRANSFER_HLG) {
|
||||
flags = flags or MASK_IS_HDR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metadataMap[KEY_FLAGS] = flags
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get catalog metadata by MediaMetadataRetriever for uri=$uri", e)
|
||||
|
@ -1300,7 +1309,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
|
|||
private const val MASK_IS_360 = 1 shl 3
|
||||
private const val MASK_IS_MULTIPAGE = 1 shl 4
|
||||
private const val MASK_IS_MOTION_PHOTO = 1 shl 5
|
||||
private const val MASK_HAS_HDR_GAIN_MAP = 1 shl 6
|
||||
private const val MASK_IS_HDR = 1 shl 6 // for images: embedded HDR gainmap, for videos: HDR color transfer
|
||||
private const val XMP_SUBJECTS_SEPARATOR = ";"
|
||||
|
||||
// overlay metadata
|
||||
|
|
|
@ -135,7 +135,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
val trashDirs = context.getExternalFilesDirs(null).mapNotNull { StorageUtils.trashDirFor(context, it.path) }
|
||||
val trashItemPaths = trashDirs.flatMap { dir -> dir.listFiles()?.map { file -> file.path } ?: listOf() }
|
||||
val trashItemPaths = trashDirs.flatMap { dir -> dir.listFiles()?.mapNotNull { file -> file?.path } ?: listOf() }
|
||||
val untrackedPaths = trashItemPaths.filterNot(knownPaths::contains).toList()
|
||||
|
||||
result.success(untrackedPaths)
|
||||
|
@ -150,7 +150,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
val vaultDir = File(StorageUtils.getVaultRoot(context), vault)
|
||||
val vaultItemPaths = vaultDir.listFiles()?.map { file -> file.path } ?: listOf()
|
||||
val vaultItemPaths = vaultDir.listFiles()?.mapNotNull { file -> file?.path } ?: listOf()
|
||||
val untrackedPaths = vaultItemPaths.filterNot(knownPaths::contains).toList()
|
||||
|
||||
result.success(untrackedPaths)
|
||||
|
|
|
@ -30,12 +30,26 @@ class MediaStoreChangeStreamHandler(private val context: Context) : EventChannel
|
|||
}
|
||||
|
||||
init {
|
||||
onAppResume()
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
onAppPause()
|
||||
}
|
||||
|
||||
fun onAppResume() {
|
||||
Log.i(LOG_TAG, "start listening to Media Store")
|
||||
context.contentResolver.apply {
|
||||
registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver)
|
||||
registerContentObserver(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, contentObserver)
|
||||
}
|
||||
}
|
||||
|
||||
fun onAppPause() {
|
||||
Log.i(LOG_TAG, "stop listening to Media Store")
|
||||
context.contentResolver.unregisterContentObserver(contentObserver)
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any?, eventSink: EventSink) {
|
||||
this.eventSink = eventSink
|
||||
handler = Handler(Looper.getMainLooper())
|
||||
|
@ -45,10 +59,6 @@ class MediaStoreChangeStreamHandler(private val context: Context) : EventChannel
|
|||
Log.i(LOG_TAG, "onCancel arguments=$arguments")
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
context.contentResolver.unregisterContentObserver(contentObserver)
|
||||
}
|
||||
|
||||
private fun success(uri: String?) {
|
||||
handler?.post {
|
||||
try {
|
||||
|
|
|
@ -19,13 +19,12 @@ class MediaStoreStreamHandler(private val context: Context, arguments: Any?) : E
|
|||
private lateinit var eventSink: EventSink
|
||||
private lateinit var handler: Handler
|
||||
|
||||
private var knownEntries: Map<Int?, Int?>? = null
|
||||
private var knownEntries: Map<Long?, Int?>? = null
|
||||
private var directory: String? = null
|
||||
|
||||
init {
|
||||
if (arguments is Map<*, *>) {
|
||||
@Suppress("unchecked_cast")
|
||||
knownEntries = arguments["knownEntries"] as Map<Int?, Int?>?
|
||||
knownEntries = (arguments["knownEntries"] as? Map<*, *>?)?.map { (it.key as Number?)?.toLong() to it.value as Int? }?.toMap()
|
||||
directory = arguments["directory"] as String?
|
||||
}
|
||||
}
|
||||
|
|
|
@ -62,9 +62,21 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
}
|
||||
|
||||
init {
|
||||
context.contentResolver.apply {
|
||||
registerContentObserver(Settings.System.CONTENT_URI, true, contentObserver)
|
||||
}
|
||||
onAppResume()
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
onAppPause()
|
||||
}
|
||||
|
||||
fun onAppResume() {
|
||||
Log.i(LOG_TAG, "start listening to system settings")
|
||||
context.contentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, contentObserver)
|
||||
}
|
||||
|
||||
fun onAppPause() {
|
||||
Log.i(LOG_TAG, "stop listening to system settings")
|
||||
context.contentResolver.unregisterContentObserver(contentObserver)
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any?, eventSink: EventSink) {
|
||||
|
@ -76,10 +88,6 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
Log.i(LOG_TAG, "onCancel arguments=$arguments")
|
||||
}
|
||||
|
||||
fun dispose() {
|
||||
context.contentResolver.unregisterContentObserver(contentObserver)
|
||||
}
|
||||
|
||||
private fun success(settings: FieldMap) {
|
||||
handler?.post {
|
||||
try {
|
||||
|
|
|
@ -166,7 +166,7 @@ object Helper {
|
|||
|
||||
// This seems to cover all known Exif and Xmp date strings
|
||||
// Note that " : : : : " is a valid date string according to the Exif spec (which means 'unknown date'): http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/datetimeoriginal.html
|
||||
private val datePatterns = arrayOf(
|
||||
private val dateFormats = arrayOf(
|
||||
"yyyy:MM:dd HH:mm:ss",
|
||||
"yyyy:MM:dd HH:mm",
|
||||
"yyyy-MM-dd HH:mm:ss",
|
||||
|
@ -179,7 +179,7 @@ object Helper {
|
|||
"yyyy-MM",
|
||||
"yyyyMMdd", // as used in IPTC data
|
||||
"yyyy"
|
||||
)
|
||||
).map { SimpleDateFormat(it, Locale.ROOT) }.toTypedArray()
|
||||
private val subsecondPattern = Pattern.compile("(\\d\\d:\\d\\d:\\d\\d)(\\.\\d+)")
|
||||
private val timeZonePattern = Pattern.compile("(Z|[+-]\\d\\d:\\d\\d|[+-]\\d\\d\\d\\d)$")
|
||||
private val calendar: Calendar = GregorianCalendar()
|
||||
|
@ -210,11 +210,10 @@ object Helper {
|
|||
effectiveTimeZone = TimeZone.getTimeZone("GMT" + timeZoneMatcher.group().replace("Z".toRegex(), ""))
|
||||
dateString = timeZoneMatcher.replaceAll("")
|
||||
}
|
||||
for (datePattern in datePatterns) {
|
||||
for (dateFormat in dateFormats) {
|
||||
try {
|
||||
val parsed = SimpleDateFormat(datePattern, Locale.ROOT).apply {
|
||||
this.timeZone = effectiveTimeZone ?: TimeZone.getTimeZone("GMT") // don't interpret zone time
|
||||
}.parse(dateString)
|
||||
dateFormat.timeZone = effectiveTimeZone ?: TimeZone.getTimeZone("GMT") // don't interpret zone time
|
||||
val parsed = dateFormat.parse(dateString)
|
||||
if (parsed != null) {
|
||||
calendar.time = parsed
|
||||
if (calendar.get(Calendar.YEAR) < PARSED_DATE_YEAR_MAX) {
|
||||
|
|
|
@ -3,7 +3,11 @@ package deckers.thibault.aves.model.provider
|
|||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.app.RecoverableSecurityException
|
||||
import android.content.*
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.graphics.BitmapFactory
|
||||
import android.media.MediaScannerConnection
|
||||
import android.net.Uri
|
||||
|
@ -35,7 +39,7 @@ import java.io.FileOutputStream
|
|||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.io.SyncFailedException
|
||||
import java.util.*
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
|
@ -45,11 +49,11 @@ import kotlin.coroutines.suspendCoroutine
|
|||
class MediaStoreImageProvider : ImageProvider() {
|
||||
fun fetchAll(
|
||||
context: Context,
|
||||
knownEntries: Map<Int?, Int?>,
|
||||
knownEntries: Map<Long?, Int?>,
|
||||
directory: String?,
|
||||
handleNewEntry: NewEntryHandler,
|
||||
) {
|
||||
val isModified = fun(contentId: Int, dateModifiedSecs: Int): Boolean {
|
||||
val isModified = fun(contentId: Long, dateModifiedSecs: Int): Boolean {
|
||||
val knownDate = knownEntries[contentId]
|
||||
return knownDate == null || knownDate < dateModifiedSecs
|
||||
}
|
||||
|
@ -89,7 +93,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
var found = false
|
||||
val fetched = arrayListOf<FieldMap>()
|
||||
val id = uri.tryParseId()
|
||||
val alwaysValid: NewEntryChecker = fun(_: Int, _: Int): Boolean = true
|
||||
val alwaysValid: NewEntryChecker = fun(_: Long, _: Int): Boolean = true
|
||||
val onSuccess: NewEntryHandler = fun(entry: FieldMap) { fetched.add(entry) }
|
||||
if (id != null) {
|
||||
if (sourceMimeType == null || isImage(sourceMimeType)) {
|
||||
|
@ -119,8 +123,8 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
}
|
||||
}
|
||||
|
||||
fun checkObsoleteContentIds(context: Context, knownContentIds: List<Int?>): List<Int> {
|
||||
val foundContentIds = HashSet<Int>()
|
||||
fun checkObsoleteContentIds(context: Context, knownContentIds: List<Long?>): List<Long> {
|
||||
val foundContentIds = HashSet<Long>()
|
||||
fun check(context: Context, contentUri: Uri) {
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID)
|
||||
try {
|
||||
|
@ -128,7 +132,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
if (cursor != null) {
|
||||
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
|
||||
while (cursor.moveToNext()) {
|
||||
foundContentIds.add(cursor.getInt(idColumn))
|
||||
foundContentIds.add(cursor.getLong(idColumn))
|
||||
}
|
||||
cursor.close()
|
||||
}
|
||||
|
@ -141,8 +145,8 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
return knownContentIds.subtract(foundContentIds).filterNotNull().toList()
|
||||
}
|
||||
|
||||
fun checkObsoletePaths(context: Context, knownPathById: Map<Int?, String?>): List<Int> {
|
||||
val obsoleteIds = ArrayList<Int>()
|
||||
fun checkObsoletePaths(context: Context, knownPathById: Map<Long?, String?>): List<Long> {
|
||||
val obsoleteIds = ArrayList<Long>()
|
||||
fun check(context: Context, contentUri: Uri) {
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID, MediaStore.MediaColumns.DATA)
|
||||
try {
|
||||
|
@ -151,7 +155,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
|
||||
val pathColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getInt(idColumn)
|
||||
val id = cursor.getLong(idColumn)
|
||||
val path = cursor.getString(pathColumn)
|
||||
if (knownPathById.containsKey(id) && knownPathById[id] != path) {
|
||||
obsoleteIds.add(id)
|
||||
|
@ -168,6 +172,31 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
return obsoleteIds
|
||||
}
|
||||
|
||||
fun getChangedUris(context: Context, sinceGeneration: Int): List<String> {
|
||||
val changedUris = ArrayList<String>()
|
||||
fun check(context: Context, contentUri: Uri) {
|
||||
val projection = arrayOf(MediaStore.MediaColumns._ID)
|
||||
val selection = "${MediaStore.MediaColumns.GENERATION_MODIFIED} > ?"
|
||||
val selectionArgs = arrayOf(sinceGeneration.toString())
|
||||
try {
|
||||
val cursor = context.contentResolver.query(contentUri, projection, selection, selectionArgs, null)
|
||||
if (cursor != null) {
|
||||
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)
|
||||
while (cursor.moveToNext()) {
|
||||
val id = cursor.getLong(idColumn)
|
||||
changedUris.add(ContentUris.withAppendedId(contentUri, id).toString())
|
||||
}
|
||||
cursor.close()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(LOG_TAG, "failed to get content IDs for contentUri=$contentUri", e)
|
||||
}
|
||||
}
|
||||
check(context, IMAGE_CONTENT_URI)
|
||||
check(context, VIDEO_CONTENT_URI)
|
||||
return changedUris
|
||||
}
|
||||
|
||||
private fun fetchFrom(
|
||||
context: Context,
|
||||
isValidEntry: NewEntryChecker,
|
||||
|
@ -207,12 +236,12 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
val needDuration = projection.contentEquals(VIDEO_PROJECTION)
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
val contentId = cursor.getInt(idColumn)
|
||||
val id = cursor.getLong(idColumn)
|
||||
val dateModifiedSecs = cursor.getInt(dateModifiedColumn)
|
||||
if (isValidEntry(contentId, dateModifiedSecs)) {
|
||||
if (isValidEntry(id, dateModifiedSecs)) {
|
||||
// for multiple items, `contentUri` is the root without ID,
|
||||
// but for single items, `contentUri` already contains the ID
|
||||
val itemUri = if (contentUriContainsId) contentUri else ContentUris.withAppendedId(contentUri, contentId.toLong())
|
||||
val itemUri = if (contentUriContainsId) contentUri else ContentUris.withAppendedId(contentUri, id)
|
||||
// `mimeType` can be registered as null for file media URIs with unsupported media types (e.g. TIFF on old devices)
|
||||
// in that case we try to use the MIME type provided along the URI
|
||||
val mimeType: String? = cursor.getString(mimeTypeColumn) ?: fileMimeType
|
||||
|
@ -237,7 +266,7 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
"sourceDateTakenMillis" to if (dateTakenColumn != -1) cursor.getLong(dateTakenColumn) else null,
|
||||
"durationMillis" to durationMillis,
|
||||
// only for map export
|
||||
"contentId" to contentId,
|
||||
"contentId" to id,
|
||||
)
|
||||
|
||||
if (MimeTypes.isHeic(mimeType)) {
|
||||
|
@ -930,8 +959,10 @@ class MediaStoreImageProvider : ImageProvider() {
|
|||
try {
|
||||
val cursor = context.contentResolver.query(contentUri, projection, selection, selectionArgs, null)
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
cursor.getColumnIndex(MediaStore.MediaColumns._ID).let {
|
||||
if (it != -1) mediaContentUri = ContentUris.withAppendedId(contentUri, cursor.getLong(it))
|
||||
val idColumn = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
|
||||
if (idColumn != -1) {
|
||||
val id = cursor.getLong(idColumn)
|
||||
mediaContentUri = ContentUris.withAppendedId(contentUri, id)
|
||||
}
|
||||
cursor.close()
|
||||
}
|
||||
|
@ -994,4 +1025,4 @@ object MediaColumns {
|
|||
|
||||
typealias NewEntryHandler = (entry: FieldMap) -> Unit
|
||||
|
||||
private typealias NewEntryChecker = (contentId: Int, dateModifiedSecs: Int) -> Boolean
|
||||
private typealias NewEntryChecker = (contentId: Long, dateModifiedSecs: Int) -> Boolean
|
|
@ -7,11 +7,13 @@ object MemoryUtils {
|
|||
|
||||
fun canAllocate(byteSize: Number?): Boolean {
|
||||
byteSize ?: return true
|
||||
val availableHeapSize = Runtime.getRuntime().let { it.maxMemory() - (it.totalMemory() - it.freeMemory()) }
|
||||
val availableHeapSize = getAvailableHeapSize()
|
||||
val danger = byteSize.toLong() > availableHeapSize
|
||||
if (danger) {
|
||||
Log.e(LOG_TAG, "trying to handle $byteSize bytes, with only $availableHeapSize free bytes")
|
||||
}
|
||||
return !danger
|
||||
}
|
||||
|
||||
fun getAvailableHeapSize() = Runtime.getRuntime().let { it.maxMemory() - (it.totalMemory() - it.freeMemory()) }
|
||||
}
|
|
@ -8,4 +8,5 @@
|
|||
<string name="analysis_notification_action_stop">توقف کردن</string>
|
||||
<string name="app_widget_label">قاب عکس</string>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="safe_mode_shortcut_short_label">حالت امن</string>
|
||||
</resources>
|
4
fastlane/metadata/android/en-US/changelogs/115.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/115.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
In v1.10.6:
|
||||
- detect HDR videos (but do not play them in their full HDR glory)
|
||||
- removing animations also applies to pop up menus
|
||||
Full changelog available on GitHub
|
4
fastlane/metadata/android/en-US/changelogs/11501.txt
Normal file
4
fastlane/metadata/android/en-US/changelogs/11501.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
In v1.10.6:
|
||||
- detect HDR videos (but do not play them in their full HDR glory)
|
||||
- removing animations also applies to pop up menus
|
||||
Full changelog available on GitHub
|
|
@ -228,7 +228,7 @@
|
|||
"@filterTypeSphericalVideoLabel": {},
|
||||
"filterNoTitleLabel": "Без назвы",
|
||||
"@filterNoTitleLabel": {},
|
||||
"filterOnThisDayLabel": "У гэты дзень",
|
||||
"filterOnThisDayLabel": "Ў гэты дзень",
|
||||
"@filterOnThisDayLabel": {},
|
||||
"filterRatingRejectedLabel": "Адхілена",
|
||||
"@filterRatingRejectedLabel": {},
|
||||
|
@ -263,7 +263,7 @@
|
|||
"@nameConflictStrategyReplace": {},
|
||||
"filterAspectRatioLandscapeLabel": "Ландшафтныя",
|
||||
"@filterAspectRatioLandscapeLabel": {},
|
||||
"filterBinLabel": "Кошык",
|
||||
"filterBinLabel": "Сметніца",
|
||||
"@filterBinLabel": {},
|
||||
"filterFavouriteLabel": "Выбранае",
|
||||
"@filterFavouriteLabel": {},
|
||||
|
@ -551,7 +551,7 @@
|
|||
"@mapPointNorthUpTooltip": {},
|
||||
"viewerInfoLabelCoordinates": "Каардынаты",
|
||||
"@viewerInfoLabelCoordinates": {},
|
||||
"viewerInfoLabelOwner": "Уладальнік",
|
||||
"viewerInfoLabelOwner": "Ўладальнік",
|
||||
"@viewerInfoLabelOwner": {},
|
||||
"viewerInfoLabelDuration": "Працягласць",
|
||||
"@viewerInfoLabelDuration": {},
|
||||
|
@ -625,7 +625,7 @@
|
|||
"@mapZoomOutTooltip": {},
|
||||
"openMapPageTooltip": "Паглядзець на старонцы карты",
|
||||
"@openMapPageTooltip": {},
|
||||
"mapEmptyRegion": "У гэтым рэгіёне няма малюнкаў",
|
||||
"mapEmptyRegion": "Ў гэтым рэгіёне няма малюнкаў",
|
||||
"@mapEmptyRegion": {},
|
||||
"viewerInfoSearchEmpty": "Няма адпаведных ключоў",
|
||||
"@viewerInfoSearchEmpty": {},
|
||||
|
@ -683,7 +683,7 @@
|
|||
"@setCoverDialogCustom": {},
|
||||
"aboutBugCopyInfoInstruction": "Скапіяваць сістэмную інфармацыю",
|
||||
"@aboutBugCopyInfoInstruction": {},
|
||||
"vaultBinUsageDialogMessage": "Некаторыя сховішчы выкарыстоўваюць кошык.",
|
||||
"vaultBinUsageDialogMessage": "Некаторыя сховішчы выкарыстоўваюць сметніцу.",
|
||||
"@vaultBinUsageDialogMessage": {},
|
||||
"aboutBugSaveLogInstruction": "Захаваць журналы праграмы ў файл",
|
||||
"@aboutBugSaveLogInstruction": {},
|
||||
|
@ -707,7 +707,7 @@
|
|||
"@aboutBugSectionTitle": {},
|
||||
"aboutBugCopyInfoButton": "Скапіяваць",
|
||||
"@aboutBugCopyInfoButton": {},
|
||||
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Перамясціць гэты элемент у кошык?} few{Перамясціць гэтыя {count} элемента у кошык?} other{Перамясціць гэтыя {count} элементаў у кошык?}}",
|
||||
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Перамясціць гэты элемент ў сметніцу?} few{Перамясціць гэтыя {count} элемента ў сметніцу?} other{Перамясціць гэтыя {count} элементаў ў сметніцу?}}",
|
||||
"@binEntriesConfirmationDialogMessage": {
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
|
@ -937,7 +937,7 @@
|
|||
"@settingsActionImportDialogTitle": {},
|
||||
"albumGroupTier": "Па ўзроўні",
|
||||
"@albumGroupTier": {},
|
||||
"drawerCollectionAll": "Уся калекцыя",
|
||||
"drawerCollectionAll": "Ўся калекцыя",
|
||||
"@drawerCollectionAll": {},
|
||||
"sortByItemCount": "Па колькасці элементаў",
|
||||
"@sortByItemCount": {},
|
||||
|
@ -985,7 +985,7 @@
|
|||
"@searchMetadataSectionTitle": {},
|
||||
"settingsShowBottomNavigationBar": "Паказаць ніжнюю панэль навігацыі",
|
||||
"@settingsShowBottomNavigationBar": {},
|
||||
"collectionActionEmptyBin": "Ачысціць кошык",
|
||||
"collectionActionEmptyBin": "Ачысціць сметніцу",
|
||||
"@collectionActionEmptyBin": {},
|
||||
"sortOrderAtoZ": "Ад А да Я",
|
||||
"@sortOrderAtoZ": {},
|
||||
|
@ -1127,7 +1127,7 @@
|
|||
"@viewDialogLayoutSectionTitle": {},
|
||||
"searchStatesSectionTitle": "Штаты",
|
||||
"@searchStatesSectionTitle": {},
|
||||
"dateThisMonth": "У гэтым месяцы",
|
||||
"dateThisMonth": "Ў гэтым месяцы",
|
||||
"@dateThisMonth": {},
|
||||
"aboutPageTitle": "Пра нас",
|
||||
"@aboutPageTitle": {},
|
||||
|
@ -1165,7 +1165,7 @@
|
|||
"@sortOrderHighestFirst": {},
|
||||
"aboutLicensesAndroidLibrariesSectionTitle": "Бібліятэкі Android",
|
||||
"@aboutLicensesAndroidLibrariesSectionTitle": {},
|
||||
"binPageTitle": "Кошык",
|
||||
"binPageTitle": "Сметніца",
|
||||
"@binPageTitle": {},
|
||||
"sortByAlbumFileName": "Па назве альбома і файла",
|
||||
"@sortByAlbumFileName": {},
|
||||
|
@ -1183,9 +1183,9 @@
|
|||
"@settingsConfirmationBeforeDeleteItems": {},
|
||||
"settingsConfirmationBeforeMoveUndatedItems": "Спытаць, перш чым перамяшчаць прадметы без даты",
|
||||
"@settingsConfirmationBeforeMoveUndatedItems": {},
|
||||
"settingsConfirmationAfterMoveToBinItems": "Паказваць паведамленне пасля перамяшчэння элементаў у кошык",
|
||||
"settingsConfirmationAfterMoveToBinItems": "Паказваць паведамленне пасля перамяшчэння элементаў ў сметніцу",
|
||||
"@settingsConfirmationAfterMoveToBinItems": {},
|
||||
"settingsConfirmationBeforeMoveToBinItems": "Спытаць, перш чым перамяшчаць элементы ў кошык",
|
||||
"settingsConfirmationBeforeMoveToBinItems": "Спытаць перад тым, як пераносіць элементы ў сметніцу",
|
||||
"@settingsConfirmationBeforeMoveToBinItems": {},
|
||||
"settingsNavigationDrawerAddAlbum": "Дадаць альбом",
|
||||
"@settingsNavigationDrawerAddAlbum": {},
|
||||
|
@ -1289,7 +1289,7 @@
|
|||
"@settingsVideoGestureDoubleTapTogglePlay": {},
|
||||
"addPathTooltip": "Дадаць шлях",
|
||||
"@addPathTooltip": {},
|
||||
"settingsEnableBin": "Выкарыстоўваць кошык",
|
||||
"settingsEnableBin": "Выкарыстоўваць сметніцу",
|
||||
"@settingsEnableBin": {},
|
||||
"collectionMoveSuccessFeedback": "{count, plural, =1{Перамяшчаны 1 элемент} few{Перамяшчаны {count} элементы} other{Перамяшчаны {count} элементаў}}",
|
||||
"@collectionMoveSuccessFeedback": {
|
||||
|
@ -1377,7 +1377,7 @@
|
|||
"@settingsViewerShowDescription": {},
|
||||
"settingsViewerQuickActionEditorBanner": "Націсніце і ўтрымлівайце, каб перамяшчаць кнопкі і выбіраць дзеянні для адлюстравання ў праглядніку.",
|
||||
"@settingsViewerQuickActionEditorBanner": {},
|
||||
"settingsDisablingBinWarningDialogMessage": "Элементы ў кошыку будуць выдалены назаўжды.",
|
||||
"settingsDisablingBinWarningDialogMessage": "Элементы ў сметніцы будуць выдалены назаўсёды.",
|
||||
"@settingsDisablingBinWarningDialogMessage": {},
|
||||
"settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "Адлюстраваныя кнопкі",
|
||||
"@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {},
|
||||
|
@ -1521,7 +1521,7 @@
|
|||
},
|
||||
"collectionActionSetHome": "Ўсталяваць як галоўную",
|
||||
"@collectionActionSetHome": {},
|
||||
"setHomeCustomCollection": "Уласная калекцыя",
|
||||
"setHomeCustomCollection": "Ўласная калекцыя",
|
||||
"@setHomeCustomCollection": {},
|
||||
"settingsThumbnailShowHdrIcon": "Паказаць значок HDR",
|
||||
"@settingsThumbnailShowHdrIcon": {}
|
||||
|
|
|
@ -1354,5 +1354,17 @@
|
|||
"aboutDataUsageClearCache": "Cache leeren",
|
||||
"@aboutDataUsageClearCache": {},
|
||||
"settingsViewerShowHistogram": "Histogramm anzeigen",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
"@settingsViewerShowHistogram": {},
|
||||
"overlayHistogramNone": "Keines",
|
||||
"@overlayHistogramNone": {},
|
||||
"collectionActionSetHome": "Als Startseite setzen",
|
||||
"@collectionActionSetHome": {},
|
||||
"setHomeCustomCollection": "Benutzerdefinierte Sammlung",
|
||||
"@setHomeCustomCollection": {},
|
||||
"settingsThumbnailShowHdrIcon": "HDR-Symbol anzeigen",
|
||||
"@settingsThumbnailShowHdrIcon": {},
|
||||
"entryActionCast": "Übertragen",
|
||||
"@entryActionCast": {},
|
||||
"castDialogTitle": "Geräte zur Übertragung",
|
||||
"@castDialogTitle": {}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@
|
|||
"@chipActionGoToAlbumPage": {},
|
||||
"filterNoTagLabel": "بدون برچسب",
|
||||
"@filterNoTagLabel": {},
|
||||
"appName": "اِیوْز",
|
||||
"appName": "اِیوز",
|
||||
"@appName": {},
|
||||
"entryActionRestore": "برگرداندن",
|
||||
"@entryActionRestore": {},
|
||||
|
@ -544,5 +544,41 @@
|
|||
"wallpaperTargetHomeLock": "صفحهٔ خانه و صفحهٔ قفل",
|
||||
"@wallpaperTargetHomeLock": {},
|
||||
"viewDialogSortSectionTitle": "ترتیب بندی",
|
||||
"@viewDialogSortSectionTitle": {}
|
||||
"@viewDialogSortSectionTitle": {},
|
||||
"filterRatingRejectedLabel": "رد شده",
|
||||
"@filterRatingRejectedLabel": {},
|
||||
"filterLocatedLabel": "واقع شده",
|
||||
"@filterLocatedLabel": {},
|
||||
"setCoverDialogAuto": "خودکار",
|
||||
"@setCoverDialogAuto": {},
|
||||
"albumTierSpecial": "مشترک",
|
||||
"@albumTierSpecial": {},
|
||||
"viewerTransitionParallax": "انطباق",
|
||||
"@viewerTransitionParallax": {},
|
||||
"coordinateFormatDecimal": "درجه های اعشاری",
|
||||
"@coordinateFormatDecimal": {},
|
||||
"videoResumeButtonLabel": "ادامه",
|
||||
"@videoResumeButtonLabel": {},
|
||||
"addShortcutDialogLabel": "عنوان میانبر",
|
||||
"@addShortcutDialogLabel": {},
|
||||
"overlayHistogramNone": "هیچکدام",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramLuminance": "درخشش",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"videoPlaybackSkip": "رد کردن",
|
||||
"@videoPlaybackSkip": {},
|
||||
"addShortcutButtonLabel": "",
|
||||
"@addShortcutButtonLabel": {},
|
||||
"setCoverDialogCustom": "شخصی",
|
||||
"@setCoverDialogCustom": {},
|
||||
"nameConflictDialogSingleSourceMessage": "برخی از پرونده های موجود در پوشه مقصد به همین نام هستند.",
|
||||
"@nameConflictDialogSingleSourceMessage": {},
|
||||
"missingSystemFilePickerDialogMessage": "انتخابگر پرونده سیستم وجود ندارد یا غیرفعال است. لطفا آن را فعال کنید و دوباره امتحان کنید",
|
||||
"@missingSystemFilePickerDialogMessage": {},
|
||||
"nameConflictDialogMultipleSourceMessage": "برخی پرونده ها نام های یکسانی دارند",
|
||||
"@nameConflictDialogMultipleSourceMessage": {},
|
||||
"noMatchingAppDialogMessage": "هیچ کاره ای وجود ندارد که بتواند این موضوع را مدیریت کند.",
|
||||
"@noMatchingAppDialogMessage": {},
|
||||
"filterTaggedLabel": "نشان شده",
|
||||
"@filterTaggedLabel": {}
|
||||
}
|
||||
|
|
|
@ -1356,5 +1356,15 @@
|
|||
"settingsViewerShowHistogram": "Tampilkan histogram",
|
||||
"@settingsViewerShowHistogram": {},
|
||||
"aboutDataUsageClearCache": "Bersihkan cache",
|
||||
"@aboutDataUsageClearCache": {}
|
||||
"@aboutDataUsageClearCache": {},
|
||||
"entryActionCast": "Siarkan",
|
||||
"@entryActionCast": {},
|
||||
"setHomeCustomCollection": "Koleksi kustom",
|
||||
"@setHomeCustomCollection": {},
|
||||
"collectionActionSetHome": "Tetapkan sebagai beranda",
|
||||
"@collectionActionSetHome": {},
|
||||
"settingsThumbnailShowHdrIcon": "Tampilkan ikon HDR",
|
||||
"@settingsThumbnailShowHdrIcon": {},
|
||||
"castDialogTitle": "Siarkan Perangkat",
|
||||
"@castDialogTitle": {}
|
||||
}
|
||||
|
|
|
@ -1232,5 +1232,19 @@
|
|||
"lengthUnitPixel": "px",
|
||||
"@lengthUnitPixel": {},
|
||||
"lengthUnitPercent": "%",
|
||||
"@lengthUnitPercent": {}
|
||||
"@lengthUnitPercent": {},
|
||||
"saveCopyButtonLabel": "コピーを保存",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"columnCount": "{count, plural, =1{1 列} other{{count} 列}}",
|
||||
"@columnCount": {
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"chipActionShowCountryStates": "地域を表示",
|
||||
"@chipActionShowCountryStates": {},
|
||||
"entryActionCast": "キャスト",
|
||||
"@entryActionCast": {},
|
||||
"editorTransformRotate": "回転",
|
||||
"@editorTransformRotate": {}
|
||||
}
|
||||
|
|
|
@ -1492,5 +1492,37 @@
|
|||
"settingsAccessibilityShowPinchGestureAlternatives": "顯示多點觸控手勢的備選方案",
|
||||
"@settingsAccessibilityShowPinchGestureAlternatives": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {}
|
||||
"@overlayHistogramRGB": {},
|
||||
"settingsThumbnailShowHdrIcon": "顯示 HDR 圖示",
|
||||
"@settingsThumbnailShowHdrIcon": {},
|
||||
"aboutDataUsageSectionTitle": "資料用量",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "資料",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "快取記憶體",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "資料庫",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "雜項",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "內部儲存",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "外部儲存",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"overlayHistogramNone": "無",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramLuminance": "亮度",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"collectionActionSetHome": "設為首頁",
|
||||
"@collectionActionSetHome": {},
|
||||
"setHomeCustomCollection": "自訂收藏品",
|
||||
"@setHomeCustomCollection": {},
|
||||
"aboutDataUsageClearCache": "清除快取",
|
||||
"@aboutDataUsageClearCache": {},
|
||||
"settingsViewerShowHistogram": "顯示直方圖",
|
||||
"@settingsViewerShowHistogram": {},
|
||||
"entryActionCast": "投放",
|
||||
"@entryActionCast": {},
|
||||
"castDialogTitle": "投放裝置",
|
||||
"@castDialogTitle": {}
|
||||
}
|
||||
|
|
|
@ -78,6 +78,7 @@ class Contributors {
|
|||
Contributor('minh', 'teaminh@skiff.com'),
|
||||
Contributor('luckris25', 'lk1thebestl@gmail.com'),
|
||||
Contributor('Marc Amorós', 'marquitus99@gmail.com'),
|
||||
Contributor('elea11', 'p.manuel.warnecke@gmail.com'),
|
||||
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
|
||||
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
|
||||
// Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese
|
||||
|
@ -94,6 +95,7 @@ class Contributors {
|
|||
// Contributor('امیر جهانگرد', 'ijahangard.a@gmail.com'), // Persian
|
||||
// Contributor('slasb37', 'p84haghi@gmail.com'), // Persian
|
||||
// Contributor('mimvahedi', 'vahedi0vahedi@gmail.com'), // Persian
|
||||
// Contributor('Alireza Rashidi', 'alirezarashidigoorabi@gmail.com'), // Persian
|
||||
// Contributor('Prasanta-Hembram', 'Prasantahembram720@gmail.com'), // Santali
|
||||
// Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian
|
||||
// Contributor('Shift18', 'bribable.lawyer@posteo.net'), // Swedish
|
||||
|
|
|
@ -242,6 +242,11 @@ class AvesEntry with AvesEntryBase {
|
|||
return _bestDate;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
|
||||
|
||||
bool get isHdr => _catalogMetadata?.isHdr ?? false;
|
||||
|
||||
int get rating => _catalogMetadata?.rating ?? 0;
|
||||
|
||||
@override
|
||||
|
@ -303,9 +308,6 @@ class AvesEntry with AvesEntryBase {
|
|||
return d == null ? null : DateTime(d.year, d.month, d.day);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
|
||||
|
||||
@override
|
||||
int? get durationMillis => _durationMillis;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/props.dart';
|
||||
import 'package:aves/model/geotiff.dart';
|
||||
|
@ -6,10 +8,14 @@ import 'package:aves/model/video/metadata.dart';
|
|||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/services/metadata/svg_metadata_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
extension ExtraAvesEntryCatalog on AvesEntry {
|
||||
Future<void> catalog({required bool background, required bool force, required bool persist}) async {
|
||||
if (isCatalogued && !force) return;
|
||||
|
||||
final beforeAvailableHeapSize = await deviceService.getAvailableHeapSize();
|
||||
|
||||
if (isSvg) {
|
||||
// vector image sizing is not essential, so we should not spend time for it during loading
|
||||
// but it is useful anyway (for aspect ratios etc.) so we size them during cataloguing
|
||||
|
@ -53,5 +59,14 @@ extension ExtraAvesEntryCatalog on AvesEntry {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
final afterAvailableHeapSize = await deviceService.getAvailableHeapSize();
|
||||
final diff = beforeAvailableHeapSize - afterAvailableHeapSize;
|
||||
const largeHeapUsageThreshold = 15 * (1 << 20); // MB
|
||||
|
||||
if (diff > largeHeapUsageThreshold) {
|
||||
debugPrint('Large heap usage (${diff}B) from cataloguing entry=$this size=$sizeBytes');
|
||||
await deviceService.requestGarbageCollection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,8 +13,6 @@ extension ExtraAvesEntryMultipage on AvesEntry {
|
|||
|
||||
bool get isMotionPhoto => catalogMetadata?.isMotionPhoto ?? false;
|
||||
|
||||
bool get isHdr => catalogMetadata?.hasHdrGainMap ?? false;
|
||||
|
||||
String? getBurstKey(List<String> patterns) {
|
||||
final key = BurstPatterns.getKeyForName(filenameWithoutExtension, patterns);
|
||||
return key != null ? '$directory/$key' : null;
|
||||
|
|
|
@ -4,7 +4,7 @@ import 'package:flutter/foundation.dart';
|
|||
class CatalogMetadata {
|
||||
final int id;
|
||||
final int? dateMillis;
|
||||
final bool isAnimated, isGeotiff, is360, isMultiPage, isMotionPhoto, hasHdrGainMap;
|
||||
final bool isAnimated, isGeotiff, is360, isMultiPage, isMotionPhoto, isHdr;
|
||||
bool isFlipped;
|
||||
int? rotationDegrees;
|
||||
final String? mimeType, xmpSubjects, xmpTitle;
|
||||
|
@ -21,7 +21,7 @@ class CatalogMetadata {
|
|||
static const _is360Mask = 1 << 3;
|
||||
static const _isMultiPageMask = 1 << 4;
|
||||
static const _isMotionPhotoMask = 1 << 5;
|
||||
static const _hasHdrGainMapMask = 1 << 6;
|
||||
static const _isHdr = 1 << 6; // for images: embedded HDR gainmap, for videos: HDR color transfer
|
||||
|
||||
CatalogMetadata({
|
||||
required this.id,
|
||||
|
@ -33,7 +33,7 @@ class CatalogMetadata {
|
|||
this.is360 = false,
|
||||
this.isMultiPage = false,
|
||||
this.isMotionPhoto = false,
|
||||
this.hasHdrGainMap = false,
|
||||
this.isHdr = false,
|
||||
this.rotationDegrees,
|
||||
this.xmpSubjects,
|
||||
this.xmpTitle,
|
||||
|
@ -75,7 +75,7 @@ class CatalogMetadata {
|
|||
is360: is360,
|
||||
isMultiPage: isMultiPage ?? this.isMultiPage,
|
||||
isMotionPhoto: isMotionPhoto,
|
||||
hasHdrGainMap: hasHdrGainMap,
|
||||
isHdr: isHdr,
|
||||
rotationDegrees: rotationDegrees ?? this.rotationDegrees,
|
||||
xmpSubjects: xmpSubjects,
|
||||
xmpTitle: xmpTitle,
|
||||
|
@ -97,7 +97,7 @@ class CatalogMetadata {
|
|||
is360: flags & _is360Mask != 0,
|
||||
isMultiPage: flags & _isMultiPageMask != 0,
|
||||
isMotionPhoto: flags & _isMotionPhotoMask != 0,
|
||||
hasHdrGainMap: flags & _hasHdrGainMapMask != 0,
|
||||
isHdr: flags & _isHdr != 0,
|
||||
// `rotationDegrees` should default to `sourceRotationDegrees`, not 0
|
||||
rotationDegrees: map['rotationDegrees'],
|
||||
xmpSubjects: map['xmpSubjects'] ?? '',
|
||||
|
@ -112,7 +112,7 @@ class CatalogMetadata {
|
|||
'id': id,
|
||||
'mimeType': mimeType,
|
||||
'dateMillis': dateMillis,
|
||||
'flags': (isAnimated ? _isAnimatedMask : 0) | (isFlipped ? _isFlippedMask : 0) | (isGeotiff ? _isGeotiffMask : 0) | (is360 ? _is360Mask : 0) | (isMultiPage ? _isMultiPageMask : 0) | (isMotionPhoto ? _isMotionPhotoMask : 0) | (hasHdrGainMap ? _hasHdrGainMapMask : 0),
|
||||
'flags': (isAnimated ? _isAnimatedMask : 0) | (isFlipped ? _isFlippedMask : 0) | (isGeotiff ? _isGeotiffMask : 0) | (is360 ? _is360Mask : 0) | (isMultiPage ? _isMultiPageMask : 0) | (isMotionPhoto ? _isMotionPhotoMask : 0) | (isHdr ? _isHdr : 0),
|
||||
'rotationDegrees': rotationDegrees,
|
||||
'xmpSubjects': xmpSubjects,
|
||||
'xmpTitle': xmpTitle,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
extension ExtraAccessibilityAnimations on AccessibilityAnimations {
|
||||
bool get animate {
|
||||
|
@ -14,4 +16,17 @@ extension ExtraAccessibilityAnimations on AccessibilityAnimations {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Duration get popUpAnimationDuration => animate ? ADurations.popupMenuAnimation : Duration.zero;
|
||||
|
||||
Duration get popUpAnimationDelay => popUpAnimationDuration + const Duration(milliseconds: ADurations.transitionMarginMillis);
|
||||
|
||||
AnimationStyle get popUpAnimationStyle {
|
||||
return animate
|
||||
? AnimationStyle(
|
||||
curve: Curves.easeInOutCubic,
|
||||
duration: popUpAnimationDuration,
|
||||
)
|
||||
: AnimationStyle.noAnimation;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -449,6 +449,8 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
}
|
||||
|
||||
if (dataTypes.contains(EntryDataType.catalog)) {
|
||||
// explicit GC before cataloguing multiple items
|
||||
await deviceService.requestGarbageCollection();
|
||||
await Future.forEach(entries, (entry) async {
|
||||
await entry.catalog(background: background, force: dataTypes.contains(EntryDataType.catalog), persist: persist);
|
||||
await metadataDb.updateCatalogMetadata(entry.id, entry.catalogMetadata);
|
||||
|
@ -499,6 +501,8 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
entryIds: entries?.map((entry) => entry.id).toList(),
|
||||
);
|
||||
} else {
|
||||
// explicit GC before cataloguing multiple items
|
||||
await deviceService.requestGarbageCollection();
|
||||
await catalogEntries(_analysisController, todoEntries);
|
||||
updateDerivedFilters(todoEntries);
|
||||
await locateEntries(_analysisController, todoEntries);
|
||||
|
|
|
@ -11,12 +11,17 @@ import 'package:aves/model/source/analysis_controller.dart';
|
|||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/model/vaults/vaults.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/utils/debouncer.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class MediaStoreSource extends CollectionSource {
|
||||
final Debouncer _changeDebouncer = Debouncer(delay: ADurations.mediaContentChangeDebounceDelay);
|
||||
final Set<String> _changedUris = {};
|
||||
int? _lastGeneration;
|
||||
SourceInitializationState _initState = SourceInitializationState.none;
|
||||
|
||||
@override
|
||||
|
@ -36,6 +41,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
_initState = directory != null ? SourceInitializationState.directory : SourceInitializationState.full;
|
||||
}
|
||||
addDirectories(albums: settings.pinnedFilters.whereType<AlbumFilter>().map((v) => v.album).toSet());
|
||||
await updateGeneration();
|
||||
unawaited(_loadEntries(
|
||||
analysisController: analysisController,
|
||||
directory: directory,
|
||||
|
@ -305,6 +311,34 @@ class MediaStoreSource extends CollectionSource {
|
|||
return tempUris;
|
||||
}
|
||||
|
||||
void onStoreChanged(String? uri) {
|
||||
if (uri != null) _changedUris.add(uri);
|
||||
if (_changedUris.isNotEmpty) {
|
||||
_changeDebouncer(() async {
|
||||
final todo = _changedUris.toSet();
|
||||
_changedUris.clear();
|
||||
final tempUris = await refreshUris(todo);
|
||||
if (tempUris.isNotEmpty) {
|
||||
_changedUris.addAll(tempUris);
|
||||
onStoreChanged(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkForChanges() async {
|
||||
final sinceGeneration = _lastGeneration;
|
||||
if (sinceGeneration != null) {
|
||||
_changedUris.addAll(await mediaStoreService.getChangedUris(sinceGeneration));
|
||||
onStoreChanged(null);
|
||||
}
|
||||
await updateGeneration();
|
||||
}
|
||||
|
||||
Future<void> updateGeneration() async {
|
||||
_lastGeneration = await mediaStoreService.getGeneration();
|
||||
}
|
||||
|
||||
// vault
|
||||
|
||||
Future<void> _loadVaultEntries(String? directory) async {
|
||||
|
|
|
@ -17,6 +17,10 @@ abstract class DeviceService {
|
|||
Future<bool> isSystemFilePickerEnabled();
|
||||
|
||||
Future<void> requestMediaManagePermission();
|
||||
|
||||
Future<int> getAvailableHeapSize();
|
||||
|
||||
Future<void> requestGarbageCollection();
|
||||
}
|
||||
|
||||
class PlatformDeviceService implements DeviceService {
|
||||
|
@ -104,4 +108,24 @@ class PlatformDeviceService implements DeviceService {
|
|||
await reportService.recordError(e, stack);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> getAvailableHeapSize() async {
|
||||
try {
|
||||
final result = await _platform.invokeMethod('getAvailableHeapSize');
|
||||
if (result != null) return result as int;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> requestGarbageCollection() async {
|
||||
try {
|
||||
await _platform.invokeMethod('requestGarbageCollection');
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ abstract class MediaStoreService {
|
|||
|
||||
Future<List<int>> checkObsoletePaths(Map<int?, String?> knownPathById);
|
||||
|
||||
Future<List<String>> getChangedUris(int sinceGeneration);
|
||||
|
||||
Future<int?> getGeneration();
|
||||
|
||||
// knownEntries: map of contentId -> dateModifiedSecs
|
||||
Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory});
|
||||
|
||||
|
@ -47,6 +51,29 @@ class PlatformMediaStoreService implements MediaStoreService {
|
|||
return [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> getChangedUris(int sinceGeneration) async {
|
||||
try {
|
||||
final result = await _platform.invokeMethod('getChangedUris', <String, dynamic>{
|
||||
'sinceGeneration': sinceGeneration,
|
||||
});
|
||||
return (result as List).cast<String>();
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int?> getGeneration() async {
|
||||
try {
|
||||
return await _platform.invokeMethod('getGeneration');
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory}) {
|
||||
try {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/image_providers/app_icon_image_provider.dart';
|
||||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -32,7 +33,7 @@ class AvesColorsProvider extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return ProxyProvider<Settings, AvesColorsData>(
|
||||
update: (context, settings, __) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final isDark = Theme.of(context).isDark;
|
||||
var mode = settings.themeColorMode;
|
||||
if (!allowMonochrome && mode == AvesThemeColorMode.monochrome) {
|
||||
mode = AvesThemeColorMode.polychrome;
|
||||
|
|
|
@ -2,12 +2,13 @@ import 'package:flutter/foundation.dart';
|
|||
|
||||
class ADurations {
|
||||
// Flutter animations (with margin)
|
||||
static const popupMenuAnimation = Duration(milliseconds: 300 + 20); // ref `_kMenuDuration` used in `_PopupMenuRoute`
|
||||
static const transitionMarginMillis = 20;
|
||||
|
||||
// page transition duration also available via `ModalRoute.of(context)!.transitionDuration * timeDilation`
|
||||
static const pageTransitionAnimation = Duration(milliseconds: 300 + 20); // ref `transitionDuration` used in `MaterialRouteTransitionMixin`
|
||||
static const dialogTransitionAnimation = Duration(milliseconds: 150 + 20); // ref `transitionDuration` used in `DialogRoute`
|
||||
static const drawerTransitionAnimation = Duration(milliseconds: 246 + 20); // ref `_kBaseSettleDuration` used in `DrawerControllerState`
|
||||
static const toggleableTransitionAnimation = Duration(milliseconds: 200 + 20); // ref `_kToggleDuration` used in `ToggleableStateMixin`
|
||||
static const pageTransitionAnimation = Duration(milliseconds: 300 + transitionMarginMillis); // ref `transitionDuration` used in `MaterialRouteTransitionMixin`
|
||||
static const dialogTransitionAnimation = Duration(milliseconds: 150 + transitionMarginMillis); // ref `transitionDuration` used in `DialogRoute`
|
||||
static const drawerTransitionAnimation = Duration(milliseconds: 246 + transitionMarginMillis); // ref `_kBaseSettleDuration` used in `DrawerControllerState`
|
||||
static const toggleableTransitionAnimation = Duration(milliseconds: 200 + transitionMarginMillis); // ref `_kToggleDuration` used in `ToggleableStateMixin`
|
||||
|
||||
// common animations
|
||||
static const sweeperOpacityAnimation = Duration(milliseconds: 150);
|
||||
|
@ -16,6 +17,7 @@ class ADurations {
|
|||
|
||||
static const appBarTitleAnimation = Duration(milliseconds: 300);
|
||||
static const appBarActionChangeAnimation = Duration(milliseconds: 200);
|
||||
static const popupMenuAnimation = Duration(milliseconds: 300);
|
||||
|
||||
// filter grids animations
|
||||
static const chipDecorationAnimation = Duration(milliseconds: 200);
|
||||
|
|
|
@ -19,11 +19,9 @@ import 'package:aves/model/source/media_store_source.dart';
|
|||
import 'package:aves/services/accessibility_service.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/colors.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/utils/debouncer.dart';
|
||||
import 'package:aves/widgets/collection/collection_grid.dart';
|
||||
import 'package:aves/widgets/collection/collection_page.dart';
|
||||
import 'package:aves/widgets/common/basic/scaffold.dart';
|
||||
|
@ -154,9 +152,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
late final Future<void> _appSetup;
|
||||
late final Future<bool> _shouldUseBoldFontLoader;
|
||||
final TvRailController _tvRailController = TvRailController();
|
||||
final CollectionSource _mediaStoreSource = MediaStoreSource();
|
||||
final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: ADurations.mediaContentChangeDebounceDelay);
|
||||
final Set<String> _changedUris = {};
|
||||
final MediaStoreSource _mediaStoreSource = MediaStoreSource();
|
||||
Size? _screenSize;
|
||||
|
||||
final ValueNotifier<PageTransitionsBuilder> _pageTransitionsBuilderNotifier = ValueNotifier(defaultPageTransitionsBuilder);
|
||||
|
@ -184,7 +180,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
EquatableConfig.stringify = true;
|
||||
_appSetup = _setup();
|
||||
_shouldUseBoldFontLoader = AccessibilityService.shouldUseBoldFont();
|
||||
_subscriptions.add(_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChanged(event as String?)));
|
||||
_subscriptions.add(_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _mediaStoreSource.onStoreChanged(event as String?)));
|
||||
_subscriptions.add(_newIntentChannel.receiveBroadcastStream().listen((event) => _onNewIntent(event as Map?)));
|
||||
_subscriptions.add(_analysisCompletionChannel.receiveBroadcastStream().listen((event) => _onAnalysisCompletion()));
|
||||
_subscriptions.add(_errorChannel.receiveBroadcastStream().listen((event) => _onError(event as String?)));
|
||||
|
@ -399,6 +395,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
}
|
||||
case AppLifecycleState.resumed:
|
||||
RecentlyAddedFilter.updateNow();
|
||||
_mediaStoreSource.checkForChanges();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -614,21 +611,6 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
_mediaStoreSource.updateDerivedFilters();
|
||||
}
|
||||
|
||||
void _onMediaStoreChanged(String? uri) {
|
||||
if (uri != null) _changedUris.add(uri);
|
||||
if (_changedUris.isNotEmpty) {
|
||||
_mediaStoreChangeDebouncer(() async {
|
||||
final todo = _changedUris.toSet();
|
||||
_changedUris.clear();
|
||||
final tempUris = await _mediaStoreSource.refreshUris(todo);
|
||||
if (tempUris.isNotEmpty) {
|
||||
_changedUris.addAll(tempUris);
|
||||
_onMediaStoreChanged(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _onError(String? error) => reportService.recordError(error, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/model/filters/query.dart';
|
|||
import 'package:aves/model/filters/trash.dart';
|
||||
import 'package:aves/model/query.dart';
|
||||
import 'package:aves/model/selection.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
|
@ -382,6 +383,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
(action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
|
||||
);
|
||||
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return [
|
||||
...quickActionButtons,
|
||||
PopupMenuButton<EntrySetAction>(
|
||||
|
@ -432,9 +434,10 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
},
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
await _onActionSelected(action);
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:aves/widgets/common/action_mixins/overlay_snack_bar.dart';
|
|||
import 'package:aves/widgets/common/basic/circle.dart';
|
||||
import 'package:aves/widgets/common/basic/text/change_highlight.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
@ -202,7 +203,8 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
final progressColor = colorScheme.primary;
|
||||
final animate = context.select<Settings, bool>((v) => v.accessibilityAnimations.animate);
|
||||
return PopScope(
|
||||
|
@ -223,7 +225,7 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
width: diameter + 2,
|
||||
height: diameter + 2,
|
||||
decoration: BoxDecoration(
|
||||
color: colorScheme.brightness == Brightness.dark ? const Color(0xBB000000) : const Color(0xEEFFFFFF),
|
||||
color: theme.isDark ? const Color(0xBB000000) : const Color(0xEEFFFFFF),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// adapted from Flutter `SnackBar` in `/material/snack_bar.dart`
|
||||
|
@ -115,7 +116,7 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
final ThemeData theme = Theme.of(context);
|
||||
final ColorScheme colorScheme = theme.colorScheme;
|
||||
final SnackBarThemeData snackBarTheme = theme.snackBarTheme;
|
||||
final bool isThemeDark = theme.brightness == Brightness.dark;
|
||||
final bool isThemeDark = theme.isDark;
|
||||
final Color buttonColor = isThemeDark ? colorScheme.primary : colorScheme.secondary;
|
||||
final SnackBarThemeData defaults = _SnackbarDefaultsM3(context);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/source/events.dart';
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -77,7 +78,7 @@ class SourceStateSubtitle extends StatelessWidget {
|
|||
const WidgetSpan(child: SizedBox(width: 8)),
|
||||
TextSpan(
|
||||
text: '${progress.done}/${progress.total}',
|
||||
style: TextStyle(color: theme.brightness == Brightness.dark ? Colors.white30 : Colors.black26),
|
||||
style: TextStyle(color: theme.isDark ? Colors.white30 : Colors.black26),
|
||||
),
|
||||
]
|
||||
],
|
||||
|
|
|
@ -21,6 +21,7 @@ class AvesPopupMenuButton<T> extends PopupMenuButton<T> {
|
|||
super.enableFeedback,
|
||||
super.iconSize,
|
||||
this.onMenuOpened,
|
||||
super.popUpAnimationStyle,
|
||||
});
|
||||
|
||||
@override
|
||||
|
|
5
lib/widgets/common/extensions/theme.dart
Normal file
5
lib/widgets/common/extensions/theme.dart
Normal file
|
@ -0,0 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
extension ExtraThemeData on ThemeData {
|
||||
bool get isDark => brightness == Brightness.dark;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AvesBorder {
|
||||
static Color _borderColor(BuildContext context) => Theme.of(context).brightness == Brightness.dark ? Colors.white30 : Colors.black26;
|
||||
static Color _borderColor(BuildContext context) => Theme.of(context).isDark ? Colors.white30 : Colors.black26;
|
||||
|
||||
// 1 device pixel for straight lines is fine
|
||||
static double straightBorderWidth(BuildContext context) => 1 / View.of(context).devicePixelRatio;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
|
@ -99,7 +100,7 @@ class _OverlayBackgroundState extends State<_OverlayBackground> {
|
|||
}
|
||||
|
||||
BoxDecoration _buildBackgroundDecoration(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final isDark = Theme.of(context).isDark;
|
||||
final gradientCenter = widget.gradientCenter;
|
||||
return _initialized
|
||||
? BoxDecoration(
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/grid/sections/mosaic/scale_grid.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
|
@ -104,7 +105,7 @@ class _OverlayBackgroundState extends State<_OverlayBackground> {
|
|||
}
|
||||
|
||||
BoxDecoration _buildBackgroundDecoration(BuildContext context) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
final isDark = Theme.of(context).isDark;
|
||||
return _initialized
|
||||
? BoxDecoration(
|
||||
color: isDark ? Colors.black87 : const Color(0xDDFFFFFF),
|
||||
|
|
|
@ -91,12 +91,12 @@ class GridThemeData {
|
|||
if (located && showLocated) LocationIcon.located(),
|
||||
if (!located && showUnlocated) LocationIcon.unlocated(),
|
||||
if (entry.rating != 0 && showRating) RatingIcon(entry: entry),
|
||||
if (entry.isHdr && showHdr) const HdrIcon(),
|
||||
if (entry.isPureVideo)
|
||||
VideoIcon(entry: entry)
|
||||
else if (entry.isAnimated)
|
||||
const AnimatedImageIcon()
|
||||
else ...[
|
||||
if (entry.isHdr && showHdr) const HdrIcon(),
|
||||
if (entry.isRaw && showRaw) const RawIcon(),
|
||||
if (entry.is360) const PanoramaIcon(),
|
||||
],
|
||||
|
|
|
@ -11,7 +11,6 @@ import 'package:aves/model/filters/tag.dart';
|
|||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/colors.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/collection/filter_bar.dart';
|
||||
|
@ -117,6 +116,7 @@ class AvesFilterChip extends StatefulWidget {
|
|||
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
const touchArea = Size(kMinInteractiveDimension, kMinInteractiveDimension);
|
||||
final actionDelegate = ChipActionDelegate();
|
||||
final animations = context.read<Settings>().accessibilityAnimations;
|
||||
final selectedAction = await showMenu<ChipAction>(
|
||||
context: context,
|
||||
position: RelativeRect.fromRect(tapPosition & touchArea, Offset.zero & overlay.size),
|
||||
|
@ -149,10 +149,11 @@ class AvesFilterChip extends StatefulWidget {
|
|||
);
|
||||
}),
|
||||
],
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
);
|
||||
if (selectedAction != null) {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
actionDelegate.onActionSelected(context, filter, selectedAction);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/model/entry/extensions/props.dart';
|
|||
import 'package:aves/model/vaults/vaults.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/grid/theme.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -295,7 +296,7 @@ class OverlayIcon extends StatelessWidget {
|
|||
margin: margin,
|
||||
padding: text != null ? EdgeInsetsDirectional.only(end: size / 4) : null,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark ? const Color(0xAA000000) : const Color(0xCCFFFFFF),
|
||||
color: Theme.of(context).isDark ? const Color(0xAA000000) : const Color(0xCCFFFFFF),
|
||||
borderRadius: BorderRadius.all(Radius.circular(size)),
|
||||
),
|
||||
child: text == null
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:aves/widgets/common/fx/borders.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -174,7 +175,7 @@ class OverlayTextButton extends StatelessWidget {
|
|||
style: ButtonStyle(
|
||||
backgroundColor: MaterialStateProperty.all<Color>(Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred)),
|
||||
foregroundColor: MaterialStateProperty.all<Color>(theme.colorScheme.onSurface),
|
||||
overlayColor: theme.brightness == Brightness.dark ? MaterialStateProperty.all<Color>(Colors.white.withOpacity(0.12)) : null,
|
||||
overlayColor: theme.isDark ? MaterialStateProperty.all<Color>(Colors.white.withOpacity(0.12)) : null,
|
||||
minimumSize: _minSize,
|
||||
side: MaterialStateProperty.all<BorderSide>(AvesBorder.curvedSide(context)),
|
||||
shape: MaterialStateProperty.all<OutlinedBorder>(const RoundedRectangleBorder(
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/colors.dart';
|
||||
import 'package:aves/widgets/common/basic/text/outlined.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/fx/highlight_decoration.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -27,7 +28,7 @@ class HighlightTitle extends StatelessWidget {
|
|||
|
||||
static List<Shadow> shadows(BuildContext context) => [
|
||||
Shadow(
|
||||
color: Theme.of(context).brightness == Brightness.dark ? Colors.black : Colors.white,
|
||||
color: Theme.of(context).isDark ? Colors.black : Colors.white,
|
||||
offset: const Offset(0, 1),
|
||||
blurRadius: 2,
|
||||
)
|
||||
|
|
|
@ -4,9 +4,9 @@ import 'package:aves/model/favourites.dart';
|
|||
import 'package:aves/model/filters/location.dart';
|
||||
import 'package:aves/model/filters/path.dart';
|
||||
import 'package:aves/model/filters/tag.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/common/basic/font_size_icon_theme.dart';
|
||||
import 'package:aves/widgets/common/basic/popup/menu_row.dart';
|
||||
import 'package:aves/widgets/common/basic/scaffold.dart';
|
||||
|
@ -25,6 +25,7 @@ import 'package:aves/widgets/debug/media_store_scan_dialog.dart';
|
|||
import 'package:aves/widgets/debug/report.dart';
|
||||
import 'package:aves/widgets/debug/settings.dart';
|
||||
import 'package:aves/widgets/debug/storage.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -36,6 +37,7 @@ class AppDebugPage extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: AvesScaffold(
|
||||
|
@ -56,9 +58,10 @@ class AppDebugPage extends StatelessWidget {
|
|||
.toList(),
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
unawaited(_onActionSelected(context, action));
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -2,8 +2,8 @@ import 'dart:math';
|
|||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/naming_pattern.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/widgets/collection/collection_grid.dart';
|
||||
|
@ -14,8 +14,10 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:aves/widgets/common/grid/theme.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/decorated.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class RenameEntrySetPage extends StatefulWidget {
|
||||
static const routeName = '/rename_entry_set';
|
||||
|
@ -62,6 +64,7 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
|
|||
final l10n = context.l10n;
|
||||
final textScaler = MediaQuery.textScalerOf(context);
|
||||
final effectiveThumbnailExtent = max(thumbnailExtent, textScaler.scale(thumbnailExtent));
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return AvesScaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.renameEntrySetPageTitle),
|
||||
|
@ -104,11 +107,12 @@ class _RenameEntrySetPageState extends State<RenameEntrySetPage> {
|
|||
},
|
||||
onSelected: (key) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
_insertProcessor(key);
|
||||
},
|
||||
tooltip: l10n.renameEntrySetPageInsertTooltip,
|
||||
icon: const Icon(AIcons.add),
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:math';
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/fx/borders.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -50,7 +51,7 @@ class ItemPicker extends StatelessWidget {
|
|||
bottom: -1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark ? const Color(0xAA000000) : const Color(0xCCFFFFFF),
|
||||
color: Theme.of(context).isDark ? const Color(0xAA000000) : const Color(0xCCFFFFFF),
|
||||
border: AvesBorder.border(context),
|
||||
borderRadius: actionBoxBorderRadius,
|
||||
),
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:aves/app_mode.dart';
|
|||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/selection.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/album.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
|
@ -205,6 +206,7 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
|||
required bool Function(ChipSetAction action) isVisible,
|
||||
required void Function(ChipSetAction action) onActionSelected,
|
||||
}) {
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return [
|
||||
if (widget.moveType != null)
|
||||
..._quickActions.where(isVisible).map(
|
||||
|
@ -227,9 +229,10 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
|||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
onActionSelected(action);
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/app_mode.dart';
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/query.dart';
|
||||
import 'package:aves/model/selection.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
|
@ -329,6 +330,7 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
|||
(action) => _buildButtonIcon(context, actionDelegate, action, enabled: canApply(action)),
|
||||
);
|
||||
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return [
|
||||
...quickActionButtons,
|
||||
PopupMenuButton<ChipSetAction>(
|
||||
|
@ -366,9 +368,10 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
|||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
_onActionSelected(context, action, actionDelegate);
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/model/filters/coordinate.dart';
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/geotiff.dart';
|
||||
import 'package:aves/model/highlight.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/enums/map_style.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
|
@ -462,6 +463,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
) async {
|
||||
final overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
|
||||
const touchArea = Size(kMinInteractiveDimension, kMinInteractiveDimension);
|
||||
final animations = context.read<Settings>().accessibilityAnimations;
|
||||
final selectedAction = await showMenu<MapClusterAction>(
|
||||
context: context,
|
||||
position: RelativeRect.fromRect(tapLocalPosition & touchArea, Offset.zero & overlay.size),
|
||||
|
@ -482,10 +484,11 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
MapClusterAction.removeLocation,
|
||||
].map(_buildMenuItem),
|
||||
],
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
);
|
||||
if (selectedAction != null) {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
final delegate = EntrySetActionDelegate();
|
||||
switch (selectedAction) {
|
||||
case MapClusterAction.editLocation:
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||
import 'package:decorated_icon/decorated_icon.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
@ -16,7 +17,8 @@ class SettingsTileLeading extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final theme = Theme.of(context);
|
||||
final colorScheme = theme.colorScheme;
|
||||
return AnimatedContainer(
|
||||
padding: const EdgeInsets.all(6),
|
||||
decoration: BoxDecoration(
|
||||
|
@ -32,7 +34,7 @@ class SettingsTileLeading extends StatelessWidget {
|
|||
icon,
|
||||
size: 18,
|
||||
color: DefaultTextStyle.of(context).style.color,
|
||||
shadows: colorScheme.brightness == Brightness.dark ? AStyles.embossShadows : null,
|
||||
shadows: theme.isDark ? AStyles.embossShadows : null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
|
@ -18,6 +19,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class FilePickerPage extends StatefulWidget {
|
||||
static const routeName = '/file_picker';
|
||||
|
@ -57,6 +59,7 @@ class _FilePickerPageState extends State<FilePickerPage> {
|
|||
return !isHidden;
|
||||
}
|
||||
}).toList();
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return PopScope(
|
||||
canPop: _directory.relativeDir.isEmpty,
|
||||
onPopInvoked: (didPop) {
|
||||
|
@ -82,13 +85,14 @@ class _FilePickerPageState extends State<FilePickerPage> {
|
|||
},
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
switch (action) {
|
||||
case _PickerAction.toggleHiddenView:
|
||||
settings.filePickerShowHiddenFiles = !showHidden;
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||
|
@ -45,6 +46,7 @@ class _SettingsMobilePageState extends State<SettingsMobilePage> with FeedbackMi
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return AvesScaffold(
|
||||
appBar: AppBar(
|
||||
title: InteractiveAppBarTitle(
|
||||
|
@ -72,9 +74,10 @@ class _SettingsMobilePageState extends State<SettingsMobilePage> with FeedbackMi
|
|||
},
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
_onActionSelected(action);
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
].map((v) => FontSizeIconTheme(child: v)).toList(),
|
||||
),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/widgets/common/basic/text/outlined.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
|
@ -20,7 +21,7 @@ class LinearPercentIndicatorText extends StatelessWidget {
|
|||
TextSpan(
|
||||
text: percentFormat.format(percent),
|
||||
style: TextStyle(
|
||||
shadows: theme.brightness == Brightness.dark ? AStyles.embossShadows : null,
|
||||
shadows: theme.isDark ? AStyles.embossShadows : null,
|
||||
),
|
||||
)
|
||||
],
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/multipage.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/viewer/controls/cast.dart';
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:aves/model/settings/settings.dart';
|
|||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/common/behaviour/springy_scroll_physics.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/viewer/action/entry_action_delegate.dart';
|
||||
import 'package:aves/widgets/viewer/controls/controller.dart';
|
||||
import 'package:aves/widgets/viewer/controls/intents.dart';
|
||||
|
@ -171,7 +172,7 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
|
|||
return ValueListenableBuilder<double>(
|
||||
valueListenable: widget.overlayOpacity,
|
||||
builder: (context, overlayOpacity, child) {
|
||||
final background = Theme.of(context).brightness == Brightness.dark ? Colors.black : Color.lerp(Colors.black, Colors.white, overlayOpacity)!;
|
||||
final background = Theme.of(context).isDark ? Colors.black : Color.lerp(Colors.black, Colors.white, overlayOpacity)!;
|
||||
return Container(
|
||||
color: background.withOpacity(backgroundOpacity),
|
||||
child: child,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/widgets/common/basic/scaffold.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/viewer/controls/controller.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_stack.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/bottom.dart';
|
||||
|
@ -26,6 +27,8 @@ class EntryViewerPage extends StatefulWidget {
|
|||
static EdgeInsets snackBarMargin(BuildContext context) {
|
||||
return EdgeInsets.only(bottom: ViewerBottomOverlay.actionSafeHeight(context));
|
||||
}
|
||||
|
||||
static Color getBackground(BuildContext context) => Theme.of(context).isDark ? Colors.black : Colors.white;
|
||||
}
|
||||
|
||||
class _EntryViewerPageState extends State<EntryViewerPage> {
|
||||
|
@ -56,11 +59,7 @@ class _EntryViewerPageState extends State<EntryViewerPage> {
|
|||
viewerController: _viewerController,
|
||||
),
|
||||
),
|
||||
backgroundColor: Navigator.canPop(context)
|
||||
? Colors.transparent
|
||||
: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.black
|
||||
: Colors.white,
|
||||
backgroundColor: Navigator.canPop(context) ? Colors.transparent : EntryViewerPage.getBackground(context),
|
||||
resizeToAvoidBottomInset: false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'package:aves/app_mode.dart';
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/props.dart';
|
||||
import 'package:aves/model/selection.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
|
@ -49,6 +49,7 @@ class InfoAppBar extends StatelessWidget {
|
|||
final commonActions = EntryActions.commonMetadataActions.where(isVisible);
|
||||
final formatSpecificActions = EntryActions.formatSpecificMetadataActions.where(isVisible);
|
||||
final useTvLayout = settings.useTvLayout;
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return SliverAppBar(
|
||||
leading: useTvLayout
|
||||
? null
|
||||
|
@ -91,9 +92,10 @@ class InfoAppBar extends StatelessWidget {
|
|||
],
|
||||
onSelected: (action) async {
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
await Future.delayed(ADurations.popupMenuAnimation * timeDilation);
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
actionDelegate.onActionSelected(context, entry, collection, action);
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
].map((v) => FontSizeIconTheme(child: v)).toList(),
|
||||
floating: true,
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/model/settings/settings.dart';
|
|||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/viewer/multipage/controller.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/details/date.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/details/description.dart';
|
||||
|
@ -136,7 +137,7 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
|||
static const double iconPadding = 8.0;
|
||||
static const double iconSize = 16.0;
|
||||
|
||||
static List<Shadow>? shadows(BuildContext context) => Theme.of(context).brightness == Brightness.dark ? AStyles.embossShadows : null;
|
||||
static List<Shadow>? shadows(BuildContext context) => Theme.of(context).isDark ? AStyles.embossShadows : null;
|
||||
|
||||
const ViewerDetailOverlayContent({
|
||||
super.key,
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/theme/format.dart';
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/styles.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/common/fx/blurred.dart';
|
||||
import 'package:aves/widgets/common/fx/borders.dart';
|
||||
import 'package:aves_video/aves_video.dart';
|
||||
|
@ -39,9 +40,9 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final blurred = settings.enableBlurEffect;
|
||||
final brightness = Theme.of(context).brightness;
|
||||
final theme = Theme.of(context);
|
||||
final textStyle = TextStyle(
|
||||
shadows: brightness == Brightness.dark ? AStyles.embossShadows : null,
|
||||
shadows: theme.isDark ? AStyles.embossShadows : null,
|
||||
);
|
||||
const strutStyle = StrutStyle(
|
||||
forceStrutHeight: true,
|
||||
|
@ -71,7 +72,7 @@ class _VideoProgressBarState extends State<VideoProgressBar> {
|
|||
alignment: Alignment.center,
|
||||
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Themes.overlayBackgroundColor(brightness: brightness, blurred: blurred),
|
||||
color: Themes.overlayBackgroundColor(brightness: theme.brightness, blurred: blurred),
|
||||
border: AvesBorder.border(context),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(radius)),
|
||||
),
|
||||
|
|
|
@ -4,9 +4,9 @@ import 'package:aves/app_mode.dart';
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/multipage.dart';
|
||||
import 'package:aves/model/entry/extensions/props.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/common/action_controls/quick_choosers/move_button.dart';
|
||||
|
@ -250,6 +250,7 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
final exportActions = widget.exportActions;
|
||||
final videoActions = widget.videoActions;
|
||||
final hasOverflowMenu = pageEntry.canRotate || pageEntry.canFlip || topLevelActions.isNotEmpty || exportActions.isNotEmpty || videoActions.isNotEmpty;
|
||||
final animations = context.select<Settings, AccessibilityAnimations>((s) => s.accessibilityAnimations);
|
||||
return Selector<VideoConductor, AvesVideoController?>(
|
||||
selector: (context, vc) => vc.getController(pageEntry),
|
||||
builder: (context, videoController, child) {
|
||||
|
@ -301,10 +302,11 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
]
|
||||
];
|
||||
},
|
||||
onSelected: (action) {
|
||||
onSelected: (action) async {
|
||||
_popupExpandedNotifier.value = null;
|
||||
// wait for the popup menu to hide before proceeding with the action
|
||||
Future.delayed(ADurations.popupMenuAnimation * timeDilation, () => widget.actionDelegate.onActionSelected(context, action));
|
||||
await Future.delayed(animations.popUpAnimationDelay * timeDilation);
|
||||
widget.actionDelegate.onActionSelected(context, action);
|
||||
},
|
||||
onCanceled: () {
|
||||
_popupExpandedNotifier.value = null;
|
||||
|
@ -316,6 +318,7 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
// so we make sure overlay stays visible
|
||||
const ToggleOverlayNotification(visible: true).dispatch(context);
|
||||
},
|
||||
popUpAnimationStyle: animations.popUpAnimationStyle,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/model/entry/entry.dart';
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/identity/empty.dart';
|
||||
import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
|
@ -40,7 +41,7 @@ class _ErrorViewState extends State<ErrorView> {
|
|||
// use container to expand constraints, so that the user can tap anywhere
|
||||
child: Container(
|
||||
// opaque to cover potential lower quality layer below
|
||||
color: Colors.black,
|
||||
color: EntryViewerPage.getBackground(context),
|
||||
child: FutureBuilder<bool>(
|
||||
future: _exists,
|
||||
builder: (context, snapshot) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/theme/durations.dart';
|
|||
import 'package:aves/widgets/aves_app.dart';
|
||||
import 'package:aves/widgets/common/basic/insets.dart';
|
||||
import 'package:aves/widgets/common/basic/scaffold.dart';
|
||||
import 'package:aves/widgets/common/extensions/theme.dart';
|
||||
import 'package:aves/widgets/viewer/action/video_action_delegate.dart';
|
||||
import 'package:aves/widgets/viewer/controls/controller.dart';
|
||||
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||
|
@ -50,7 +51,7 @@ class WallpaperPage extends StatelessWidget {
|
|||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
backgroundColor: Theme.of(context).brightness == Brightness.dark ? Colors.black : Colors.white,
|
||||
backgroundColor: Theme.of(context).isDark ? Colors.black : Colors.white,
|
||||
resizeToAvoidBottomInset: false,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -81,10 +81,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
|
|
@ -137,10 +137,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||
sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
version: "2.1.0"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -201,10 +201,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -262,10 +262,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.5.1"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -5,10 +5,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: "737321f9be522620ed3794937298fb0027a48a402624fa2500f7532f94aea810"
|
||||
sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.22"
|
||||
version: "1.3.25"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -68,10 +68,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "7e049e32a9d347616edb39542cf92cd53fdb4a99fb6af0a0bff327c14cd76445"
|
||||
sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.25.4"
|
||||
version: "2.27.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -84,26 +84,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "57e61d6010e253b36d38191cefd6199d7849152cdcd234b61ca290cdb278a0ba"
|
||||
sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.4"
|
||||
version: "2.11.5"
|
||||
firebase_crashlytics:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_crashlytics
|
||||
sha256: "4c754db28a7daabe03c4cbf1079dbe81e6f0681284fed6d07e0e640b7f1027c4"
|
||||
sha256: c4f1b723d417bc9c4774810e774ff91df8fb0032d33fb2888b2c887e865581b8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.15"
|
||||
version: "3.4.18"
|
||||
firebase_crashlytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics_platform_interface
|
||||
sha256: "08c5d7b5f93dbad7306d26702935abd8b579313ea256eb27006562a1867df249"
|
||||
sha256: c5a11fca3df76a98e3fa68fde8b10a08aacb9a7639f619fbfd4dad6c67a08643
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.22"
|
||||
version: "3.6.25"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
|
|
@ -144,10 +144,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||
sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
version: "2.1.0"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -208,10 +208,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -269,10 +269,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.5.1"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -53,7 +53,7 @@ class EntryGoogleMap<T> extends StatefulWidget {
|
|||
State<StatefulWidget> createState() => _EntryGoogleMapState<T>();
|
||||
}
|
||||
|
||||
class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindingObserver {
|
||||
class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> {
|
||||
GoogleMapController? _serviceMapController;
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
Map<MarkerKey<T>, GeoEntry<T>> _geoEntryByMarkerKey = {};
|
||||
|
@ -72,7 +72,6 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
_sizeNotifier.addListener(_onSizeChanged);
|
||||
_registerWidget(widget);
|
||||
}
|
||||
|
@ -88,7 +87,6 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
|||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
_serviceMapController?.dispose();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_sizeNotifier.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
@ -109,15 +107,6 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> with WidgetsBindi
|
|||
..clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
// workaround for blank map when resuming app
|
||||
// cf https://github.com/flutter/flutter/issues/40284
|
||||
_serviceMapController?.setMapStyle(null);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
|
|
|
@ -187,50 +187,50 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_maps
|
||||
sha256: "555d5d736339b0478e821167ac521c810d7b51c3b2734e6802a9f046b64ea37a"
|
||||
sha256: "47eef3836b49bb030d5cb3afc60b8451408bf34cf753e571b645d6529eb4251a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
version: "7.1.0"
|
||||
google_maps_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter
|
||||
sha256: ae66fef3e71261d7df2eff29b2a119e190b2884325ecaa55321b1e17b5504066
|
||||
sha256: "982c2e22ec78d32701bfbd351311e8579a4952035381ac66462c0af11003107b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.3"
|
||||
version: "2.6.0"
|
||||
google_maps_flutter_android:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter_android
|
||||
sha256: "714530f865f13bb3b9505c58821c3baed5d247a871724acf5d2ea5808fbed02c"
|
||||
sha256: "256b3c974e415bd17555ceff76a5d0badd2cbfd29febfc23070993358f639550"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.2"
|
||||
version: "2.7.0"
|
||||
google_maps_flutter_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_ios
|
||||
sha256: "29503b5159da2308a66212c3827963998bfb943ba073e2114fb2d486b47fd2c8"
|
||||
sha256: "0997f99d8bd8712f648a49bfc96a3cf2713cfdaf73a005c719aab74eaef94030"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.5.0"
|
||||
google_maps_flutter_platform_interface:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter_platform_interface
|
||||
sha256: "6060779f020638a8eedeb0fb14234818e5fa32ec45a4653d6428ab436e2bbc64"
|
||||
sha256: "167af879da4d004cd58771f1469b91dcc3b9b0a2c5334cc6bf71fd41d4b35403"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
version: "2.6.0"
|
||||
google_maps_flutter_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_web
|
||||
sha256: "6245721c160d6f531c1ef568cf9bef8d660cd585a982aa75121269030163785a"
|
||||
sha256: "861c6dd430123e58bb1154342345c5bfa36064120c8ec3dbc0d0b7522add1861"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.4+3"
|
||||
version: "0.5.6+2"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -307,10 +307,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||
sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
version: "2.1.0"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -379,10 +379,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
sanitize_html:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -456,18 +456,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.5.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.3.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -486,4 +486,4 @@ packages:
|
|||
version: "2.0.0"
|
||||
sdks:
|
||||
dart: ">=3.3.0 <4.0.0"
|
||||
flutter: ">=3.16.6"
|
||||
flutter: ">=3.19.0"
|
||||
|
|
|
@ -183,10 +183,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||
sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
version: "2.1.0"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -255,10 +255,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -316,10 +316,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.5.1"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -151,10 +151,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||
sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
version: "2.1.0"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -215,10 +215,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -276,10 +276,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "1d9158c616048c38f712a6646e317a3426da10e884447626167240d45209cbad"
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.0"
|
||||
version: "0.5.1"
|
||||
wkt_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -441,10 +441,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.3.0"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
70
pubspec.lock
70
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: "737321f9be522620ed3794937298fb0027a48a402624fa2500f7532f94aea810"
|
||||
sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.22"
|
||||
version: "1.3.25"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -314,10 +314,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: dynamic_color
|
||||
sha256: a866f1f8947bfdaf674d7928e769eac7230388a2e7a2542824fad4bb5b87be3b
|
||||
sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.9"
|
||||
version: "1.7.0"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -388,10 +388,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "7e049e32a9d347616edb39542cf92cd53fdb4a99fb6af0a0bff327c14cd76445"
|
||||
sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.25.4"
|
||||
version: "2.27.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -404,26 +404,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "57e61d6010e253b36d38191cefd6199d7849152cdcd234b61ca290cdb278a0ba"
|
||||
sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.4"
|
||||
version: "2.11.5"
|
||||
firebase_crashlytics:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics
|
||||
sha256: "4c754db28a7daabe03c4cbf1079dbe81e6f0681284fed6d07e0e640b7f1027c4"
|
||||
sha256: c4f1b723d417bc9c4774810e774ff91df8fb0032d33fb2888b2c887e865581b8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.15"
|
||||
version: "3.4.18"
|
||||
firebase_crashlytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics_platform_interface
|
||||
sha256: "08c5d7b5f93dbad7306d26702935abd8b579313ea256eb27006562a1867df249"
|
||||
sha256: c5a11fca3df76a98e3fa68fde8b10a08aacb9a7639f619fbfd4dad6c67a08643
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.22"
|
||||
version: "3.6.25"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -436,10 +436,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flex_color_picker
|
||||
sha256: "0871edc170153cfc3de316d30625f40a85daecfa76ce541641f3cc0ec7757cbf"
|
||||
sha256: "904373c7b0531fd4a92d29705a80ab4594b7647da2d93044487aaec4614cb6ed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.1"
|
||||
version: "3.4.0"
|
||||
flex_seed_scheme:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -452,10 +452,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: floating
|
||||
sha256: d9d563089e34fbd714ffdcdd2df447ec41b40c9226dacae6b4f78847aef8b991
|
||||
sha256: "04c3c96909b94dd6d2d121c69707739825e1f3dceca5ae451a9b8c0e652d246b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.1"
|
||||
version: "2.0.2"
|
||||
fluster:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -532,10 +532,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: a64c5323ac83ed2b7940d2b6288d160aa1753ff271ba9d9b2a86770414aa3eab
|
||||
sha256: cb44f7831b23a6bdd0f501718b0d2e8045cbc625a15f668af37ddb80314821db
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.20+1"
|
||||
version: "0.6.21"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -635,26 +635,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_android
|
||||
sha256: "714530f865f13bb3b9505c58821c3baed5d247a871724acf5d2ea5808fbed02c"
|
||||
sha256: "256b3c974e415bd17555ceff76a5d0badd2cbfd29febfc23070993358f639550"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.2"
|
||||
version: "2.7.0"
|
||||
google_maps_flutter_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_ios
|
||||
sha256: "29503b5159da2308a66212c3827963998bfb943ba073e2114fb2d486b47fd2c8"
|
||||
sha256: "0997f99d8bd8712f648a49bfc96a3cf2713cfdaf73a005c719aab74eaef94030"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.2"
|
||||
version: "2.5.0"
|
||||
google_maps_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_platform_interface
|
||||
sha256: "6060779f020638a8eedeb0fb14234818e5fa32ec45a4653d6428ab436e2bbc64"
|
||||
sha256: "167af879da4d004cd58771f1469b91dcc3b9b0a2c5334cc6bf71fd41d4b35403"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
version: "2.6.0"
|
||||
google_maps_flutter_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -835,10 +835,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: logger
|
||||
sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac"
|
||||
sha256: b3ff55aeb08d9d8901b767650285872cb1bb8f508373b3e348d60268b0c7f770
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2+1"
|
||||
version: "2.1.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -851,10 +851,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: markdown
|
||||
sha256: "1b134d9f8ff2da15cb298efe6cd8b7d2a78958c1b00384ebcbdf13fe340a6c90"
|
||||
sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.2.1"
|
||||
version: "7.2.2"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1237,10 +1237,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096"
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.1"
|
||||
version: "6.1.2"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1619,10 +1619,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "75bb6fe3f60070407704282a2d295630cab232991eb52542b18347a8a941df03"
|
||||
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.2.4"
|
||||
version: "6.2.5"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1755,10 +1755,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "464f5674532865248444b4c3daca12bd9bf2d7c47f759ce2617986e7229494a8"
|
||||
sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.3.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1801,4 +1801,4 @@ packages:
|
|||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.3.0 <4.0.0"
|
||||
flutter: ">=3.19.1"
|
||||
flutter: ">=3.19.3"
|
||||
|
|
|
@ -7,13 +7,13 @@ repository: https://github.com/deckerst/aves
|
|||
# - play changelog: /whatsnew/whatsnew-en-US
|
||||
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
|
||||
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
|
||||
version: 1.10.5+114
|
||||
version: 1.10.6+115
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
# this project bundles Flutter SDK via `flutter_wrapper`
|
||||
# cf https://github.com/passsy/flutter_wrapper
|
||||
flutter: 3.19.1
|
||||
flutter: 3.19.3
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
|
||||
# use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor
|
||||
|
|
|
@ -7,37 +7,37 @@ cd /d %sdk%\platform-tools
|
|||
|
||||
@echo on
|
||||
|
||||
adb.exe shell setprop log.tag.ACodec WARN
|
||||
adb.exe shell setprop log.tag.AHierarchicalStateMachine ERROR
|
||||
adb.exe shell setprop log.tag.AudioCapabilities ERROR
|
||||
adb.exe shell setprop log.tag.AudioTrack INFO
|
||||
adb.exe shell setprop log.tag.BufferPoolAccessor2.0 INFO
|
||||
adb.exe shell setprop log.tag.CCodec INFO
|
||||
adb.exe shell setprop log.tag.CCodecBufferChannel INFO
|
||||
adb.exe shell setprop log.tag.CCodecBuffers INFO
|
||||
adb.exe shell setprop log.tag.CCodecConfig INFO
|
||||
adb.exe shell setprop log.tag.Codec2Client INFO
|
||||
adb.exe shell setprop log.tag.CompatibilityChangeReporter INFO
|
||||
adb.exe shell setprop log.tag.Counters WARN
|
||||
adb.exe shell setprop log.tag.CustomizedTextParser INFO
|
||||
adb.exe shell setprop log.tag.EGL_emulation INFO
|
||||
adb.exe shell setprop log.tag.HostConnection INFO
|
||||
adb.exe shell setprop log.tag.InputMethodManager WARN
|
||||
adb.exe shell setprop log.tag.InsetsSourceConsumer INFO
|
||||
adb.exe shell setprop log.tag.InputTransport INFO
|
||||
adb.exe shell setprop log.tag.J4A INFO
|
||||
adb.exe shell setprop log.tag.MediaCodec WARN
|
||||
adb.exe shell setprop log.tag.MediaMetadataRetriever INFO
|
||||
adb.exe shell setprop log.tag.MediaMetadataRetrieverJNI INFO
|
||||
adb.exe shell setprop log.tag.NativeTiffDecoder INFO
|
||||
adb.exe shell setprop log.tag.NuMediaExtractor INFO
|
||||
adb.exe shell setprop log.tag.PipelineWatcher INFO
|
||||
adb.exe shell setprop log.tag.ReflectedParamUpdater INFO
|
||||
adb.exe shell setprop log.tag.skia INFO
|
||||
adb.exe shell setprop log.tag.SurfaceControl WARN
|
||||
adb.exe shell setprop log.tag.SurfaceUtils INFO
|
||||
adb.exe shell setprop log.tag.SurfaceView WARN
|
||||
adb.exe shell setprop log.tag.VideoCapabilities ERROR
|
||||
adb.exe shell setprop persist.log.tag.ACodec WARN
|
||||
adb.exe shell setprop persist.log.tag.AHierarchicalStateMachine ERROR
|
||||
adb.exe shell setprop persist.log.tag.AudioCapabilities ERROR
|
||||
adb.exe shell setprop persist.log.tag.AudioTrack INFO
|
||||
adb.exe shell setprop persist.log.tag.BufferPoolAccessor2.0 INFO
|
||||
adb.exe shell setprop persist.log.tag.CCodec INFO
|
||||
adb.exe shell setprop persist.log.tag.CCodecBufferChannel INFO
|
||||
adb.exe shell setprop persist.log.tag.CCodecBuffers INFO
|
||||
adb.exe shell setprop persist.log.tag.CCodecConfig INFO
|
||||
adb.exe shell setprop persist.log.tag.Codec2Client INFO
|
||||
adb.exe shell setprop persist.log.tag.CompatibilityChangeReporter INFO
|
||||
adb.exe shell setprop persist.log.tag.Counters WARN
|
||||
adb.exe shell setprop persist.log.tag.CustomizedTextParser INFO
|
||||
adb.exe shell setprop persist.log.tag.EGL_emulation INFO
|
||||
adb.exe shell setprop persist.log.tag.HostConnection INFO
|
||||
adb.exe shell setprop persist.log.tag.InputMethodManager WARN
|
||||
adb.exe shell setprop persist.log.tag.InsetsSourceConsumer INFO
|
||||
adb.exe shell setprop persist.log.tag.InputTransport INFO
|
||||
adb.exe shell setprop persist.log.tag.J4A INFO
|
||||
adb.exe shell setprop persist.log.tag.MediaCodec WARN
|
||||
adb.exe shell setprop persist.log.tag.MediaMetadataRetriever INFO
|
||||
adb.exe shell setprop persist.log.tag.MediaMetadataRetrieverJNI INFO
|
||||
adb.exe shell setprop persist.log.tag.NativeTiffDecoder INFO
|
||||
adb.exe shell setprop persist.log.tag.NuMediaExtractor INFO
|
||||
adb.exe shell setprop persist.log.tag.PipelineWatcher INFO
|
||||
adb.exe shell setprop persist.log.tag.ReflectedParamUpdater INFO
|
||||
adb.exe shell setprop persist.log.tag.skia INFO
|
||||
adb.exe shell setprop persist.log.tag.SurfaceControl WARN
|
||||
adb.exe shell setprop persist.log.tag.SurfaceUtils INFO
|
||||
adb.exe shell setprop persist.log.tag.SurfaceView WARN
|
||||
adb.exe shell setprop persist.log.tag.VideoCapabilities ERROR
|
||||
|
||||
@echo off
|
||||
endlocal
|
||||
|
|
|
@ -1,32 +1,36 @@
|
|||
#!/bin/bash
|
||||
adb shell setprop log.tag.ACodec WARN
|
||||
adb shell setprop log.tag.AHierarchicalStateMachine ERROR
|
||||
adb shell setprop log.tag.AudioCapabilities ERROR
|
||||
adb shell setprop log.tag.AudioTrack INFO
|
||||
adb shell setprop log.tag.BufferPoolAccessor2.0 INFO
|
||||
adb shell setprop log.tag.CCodec INFO
|
||||
adb shell setprop log.tag.CCodecBufferChannel INFO
|
||||
adb shell setprop log.tag.CCodecBuffers INFO
|
||||
adb shell setprop log.tag.CCodecConfig INFO
|
||||
adb shell setprop log.tag.Codec2Client INFO
|
||||
adb shell setprop log.tag.CompatibilityChangeReporter INFO
|
||||
adb shell setprop log.tag.Counters WARN
|
||||
adb shell setprop log.tag.CustomizedTextParser INFO
|
||||
adb shell setprop log.tag.EGL_emulation INFO
|
||||
adb shell setprop log.tag.HostConnection INFO
|
||||
adb shell setprop log.tag.InputMethodManager WARN
|
||||
adb shell setprop log.tag.InsetsSourceConsumer INFO
|
||||
adb shell setprop log.tag.InputTransport INFO
|
||||
adb shell setprop log.tag.J4A INFO
|
||||
adb shell setprop log.tag.MediaCodec WARN
|
||||
adb shell setprop log.tag.MediaMetadataRetriever INFO
|
||||
adb shell setprop log.tag.MediaMetadataRetrieverJNI INFO
|
||||
adb shell setprop log.tag.NativeTiffDecoder INFO
|
||||
adb shell setprop log.tag.NuMediaExtractor INFO
|
||||
adb shell setprop log.tag.PipelineWatcher INFO
|
||||
adb shell setprop log.tag.ReflectedParamUpdater INFO
|
||||
adb shell setprop log.tag.skia INFO
|
||||
adb shell setprop log.tag.SurfaceControl WARN
|
||||
adb shell setprop log.tag.SurfaceUtils INFO
|
||||
adb shell setprop log.tag.SurfaceView WARN
|
||||
adb shell setprop log.tag.VideoCapabilities ERROR
|
||||
adb shell setprop persist.log.tag.ACodec WARN
|
||||
adb shell setprop persist.log.tag.AHierarchicalStateMachine ERROR
|
||||
adb shell setprop persist.log.tag.AudioCapabilities ERROR
|
||||
adb shell setprop persist.log.tag.AudioTrack INFO
|
||||
adb shell setprop persist.log.tag.BufferPoolAccessor2.0 INFO
|
||||
adb shell setprop persist.log.tag.CCodec INFO
|
||||
adb shell setprop persist.log.tag.CCodecBufferChannel INFO
|
||||
adb shell setprop persist.log.tag.CCodecBuffers INFO
|
||||
adb shell setprop persist.log.tag.CCodecConfig INFO
|
||||
adb shell setprop persist.log.tag.Codec2Client INFO
|
||||
adb shell setprop persist.log.tag.CompatibilityChangeReporter INFO
|
||||
adb shell setprop persist.log.tag.ConnectivityManager INFO
|
||||
adb shell setprop persist.log.tag.Counters WARN
|
||||
adb shell setprop persist.log.tag.CustomizedTextParser INFO
|
||||
adb shell setprop persist.log.tag.EGL_emulation INFO
|
||||
adb shell setprop persist.log.tag.ffmpeg-kit-flutter INFO
|
||||
adb shell setprop persist.log.tag.HostConnection INFO
|
||||
adb shell setprop persist.log.tag.InputMethodManager WARN
|
||||
adb shell setprop persist.log.tag.InsetsSourceConsumer INFO
|
||||
adb shell setprop persist.log.tag.InputTransport INFO
|
||||
adb shell setprop persist.log.tag.J4A INFO
|
||||
adb shell setprop persist.log.tag.MediaCodec WARN
|
||||
adb shell setprop persist.log.tag.MediaMetadataRetriever INFO
|
||||
adb shell setprop persist.log.tag.MediaMetadataRetrieverJNI INFO
|
||||
adb shell setprop persist.log.tag.NativeTiffDecoder INFO
|
||||
adb shell setprop persist.log.tag.NuMediaExtractor INFO
|
||||
adb shell setprop persist.log.tag.OpenGLRenderer INFO
|
||||
adb shell setprop persist.log.tag.PipelineWatcher INFO
|
||||
adb shell setprop persist.log.tag.ReflectedParamUpdater INFO
|
||||
adb shell setprop persist.log.tag.skia INFO
|
||||
adb shell setprop persist.log.tag.SurfaceControl WARN
|
||||
adb shell setprop persist.log.tag.SurfaceUtils INFO
|
||||
adb shell setprop persist.log.tag.SurfaceView WARN
|
||||
adb shell setprop persist.log.tag.VideoCapabilities ERROR
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -5,4 +5,10 @@ import 'package:test/fake.dart';
|
|||
class FakeDeviceService extends Fake implements DeviceService {
|
||||
@override
|
||||
Future<int> getDefaultTimeZoneRawOffsetMillis() => SynchronousFuture(3600000);
|
||||
|
||||
@override
|
||||
Future<int> getAvailableHeapSize() => SynchronousFuture(0x7fffffff);
|
||||
|
||||
@override
|
||||
Future<void> requestGarbageCollection() => SynchronousFuture(null);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,12 @@ class FakeMediaStoreService extends Fake implements MediaStoreService {
|
|||
return [];
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int?> getGeneration() async {
|
||||
if (latency != null) await Future.delayed(latency!);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<AvesEntry> getEntries(Map<int?, int?> knownEntries, {String? directory}) => Stream.fromIterable(entries);
|
||||
|
||||
|
|
|
@ -1889,15 +1889,6 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"entryActionCast",
|
||||
"overlayHistogramNone",
|
||||
"castDialogTitle",
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
"settingsThumbnailShowHdrIcon"
|
||||
],
|
||||
|
||||
"el": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
|
@ -1916,37 +1907,19 @@
|
|||
],
|
||||
|
||||
"fa": [
|
||||
"filterLocatedLabel",
|
||||
"filterTaggedLabel",
|
||||
"filterRatingRejectedLabel",
|
||||
"albumTierSpecial",
|
||||
"coordinateFormatDms",
|
||||
"coordinateFormatDecimal",
|
||||
"coordinateDms",
|
||||
"coordinateDmsNorth",
|
||||
"coordinateDmsSouth",
|
||||
"coordinateDmsEast",
|
||||
"coordinateDmsWest",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramRGB",
|
||||
"overlayHistogramLuminance",
|
||||
"videoPlaybackSkip",
|
||||
"viewerTransitionParallax",
|
||||
"missingSystemFilePickerDialogMessage",
|
||||
"unsupportedTypeDialogMessage",
|
||||
"nameConflictDialogSingleSourceMessage",
|
||||
"nameConflictDialogMultipleSourceMessage",
|
||||
"addShortcutDialogLabel",
|
||||
"addShortcutButtonLabel",
|
||||
"noMatchingAppDialogMessage",
|
||||
"binEntriesConfirmationDialogMessage",
|
||||
"deleteEntriesConfirmationDialogMessage",
|
||||
"moveUndatedConfirmationDialogMessage",
|
||||
"moveUndatedConfirmationDialogSetDate",
|
||||
"videoResumeButtonLabel",
|
||||
"setCoverDialogLatest",
|
||||
"setCoverDialogAuto",
|
||||
"setCoverDialogCustom",
|
||||
"hideFilterConfirmationDialogMessage",
|
||||
"newAlbumDialogTitle",
|
||||
"newAlbumDialogNameLabel",
|
||||
|
@ -4795,14 +4768,6 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"id": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
"settingsThumbnailShowHdrIcon"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
|
@ -4810,18 +4775,13 @@
|
|||
],
|
||||
|
||||
"ja": [
|
||||
"columnCount",
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
"editorTransformCrop",
|
||||
"editorTransformRotate",
|
||||
"cropAspectRatioFree",
|
||||
"cropAspectRatioOriginal",
|
||||
"cropAspectRatioSquare",
|
||||
|
@ -9274,24 +9234,5 @@
|
|||
"filePickerOpenFrom",
|
||||
"filePickerNoItems",
|
||||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"zh_Hant": [
|
||||
"entryActionCast",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramLuminance",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
"aboutDataUsageDatabase",
|
||||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"collectionActionSetHome",
|
||||
"setHomeCustomCollection",
|
||||
"settingsThumbnailShowHdrIcon",
|
||||
"settingsViewerShowHistogram"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
In v1.10.5:
|
||||
- enjoy the app in Catalan
|
||||
In v1.10.6:
|
||||
- detect HDR videos (but do not play them in their full HDR glory)
|
||||
- removing animations also applies to pop up menus
|
||||
Full changelog available on GitHub
|
Loading…
Reference in a new issue