Compare commits
14 commits
Author | SHA1 | Date | |
---|---|---|---|
9037f8e610 | |||
05bb77a793 | |||
2f0f3da2fa | |||
31f85c3e01 | |||
84a822022a | |||
94c83914a4 | |||
a461e2c55f | |||
99c9f85eaf | |||
848ad5220e | |||
![]() |
7577466978 | ||
![]() |
dfcaf4d35a | ||
![]() |
171394056f | ||
![]() |
60211545e1 | ||
![]() |
edbf9744f5 |
21 changed files with 201 additions and 70 deletions
4
.github/workflows/quality-check.yml
vendored
4
.github/workflows/quality-check.yml
vendored
|
@ -72,7 +72,7 @@ jobs:
|
|||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
|
@ -86,6 +86,6 @@ jobs:
|
|||
./flutterw build apk --profile -t lib/main_play.dart --flavor play
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
2
.github/workflows/scorecards.yml
vendored
2
.github/workflows/scorecards.yml
vendored
|
@ -71,6 +71,6 @@ jobs:
|
|||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18
|
||||
uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Info: show matching dynamic albums
|
||||
|
||||
### Fixed
|
||||
|
||||
- crash when decoding some large thumbnails
|
||||
|
||||
## <a id="v1.13.2"></a>[v1.13.2] - 2025-06-02
|
||||
|
||||
### Changed
|
||||
|
|
85
README.md
85
README.md
|
@ -111,17 +111,96 @@ Some users have expressed the wish to financially support the project. Thanks!
|
|||
|
||||
## Project Setup
|
||||
|
||||
### Install dependencies
|
||||
|
||||
Before running or building the app, update the dependencies for the desired flavor:
|
||||
```
|
||||
# scripts/apply_flavor_play.sh
|
||||
scripts/apply_flavor_play.sh
|
||||
```
|
||||
|
||||
To build the project, create a file named `<app dir>/android/key.properties`. It should contain a reference to a keystore for app signing, and other necessary credentials. See [key_template.properties](https://github.com/deckerst/aves/blob/develop/android/key_template.properties) for the expected keys.
|
||||
|
||||
To run the app:
|
||||
### To run the app:
|
||||
```
|
||||
# ./flutterw run -t lib/main_play.dart --flavor play
|
||||
./flutterw run -t lib/main_play.dart --flavor play
|
||||
```
|
||||
### To build the app:
|
||||
|
||||
creare file con le tue credenziali file.keystore
|
||||
|
||||
dove YOUR_ALIAS_NAME è il tuo unico alias name
|
||||
|
||||
e YOUR_ALIAS_PWD è la password del tuo alias
|
||||
```sh
|
||||
keytool -genkey -v -keystore file.keystore -alias YOUR_ALIAS_NAME -storepass YOUR_ALIAS_PWD -keypass YOUR_ALIAS_PWD -keyalg RSA -validity 36500
|
||||
```
|
||||
in questo caso ho inserito
|
||||
```sh
|
||||
cd android
|
||||
keytool -genkey -v -keystore file.keystore -alias FabioMich66 -storepass Master66 -keypass Master66 -keyalg RSA -validity 36500
|
||||
```
|
||||
se non puoi eseguire keytool perchè non è nel path di sistema cercalo usando
|
||||
```sh
|
||||
cd /
|
||||
sudo find -name keytool
|
||||
```
|
||||
compilare il file `<app dir>/android/key.properties`
|
||||
```
|
||||
nano android/key.properties
|
||||
```
|
||||
questi i miei dati utilizzando il format key_template.properties
|
||||
```
|
||||
storeFile=/Users/fabio/flutter_apps/aves/android/file.keystore
|
||||
storePassword=Master66
|
||||
keyAlias=FabioMich66
|
||||
keyPassword=Master66
|
||||
googleApiKey=<GOOGLE_API_KEY>
|
||||
```
|
||||
infine compilare l'apk
|
||||
```
|
||||
./flutterw build apk -t lib/main_play.dart --flavor play
|
||||
```
|
||||
|
||||
[Version badge]: https://img.shields.io/github/v/release/deckerst/aves?include_prereleases&sort=semver
|
||||
[Build badge]: https://img.shields.io/github/actions/workflow/status/deckerst/aves/quality-check.yml?branch=develop
|
||||
|
||||
## Android studio
|
||||
|
||||
caricare il file da github selezionando le mnù a tendina File-New-project from Version Control
|
||||
|
||||
selezionare version control tipo: git
|
||||
|
||||
inserire URL di aves
|
||||
|
||||
https://github.com/deckerst/aves
|
||||
|
||||
flaggare shallow clone with history troncated 1 commits
|
||||
|
||||
aprire la console sulla dir aves appena creata e caricare le dipendenze
|
||||
|
||||
```
|
||||
scripts/apply_flavor_izzy.sh
|
||||
```
|
||||
in settings - Languages and Framework - Dart inserire il path
|
||||
|
||||
```
|
||||
/home/fabio/flutter/bin/cache/
|
||||
```
|
||||
e spuntare project aves
|
||||
|
||||
Edit configurations e aggiungere shell script con un nome x es izzi
|
||||
|
||||
poi flaggare script text e inserire
|
||||
|
||||
./flutterw run -t lib/main_izzy.dart --flavor izzy
|
||||
|
||||
la working directory sarà una cosa così
|
||||
|
||||
/home/fabio/StudioProjects/aves
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -33,13 +33,13 @@ kotlin {
|
|||
}
|
||||
|
||||
android {
|
||||
namespace 'deckers.thibault.aves'
|
||||
compileSdk 35
|
||||
namespace = 'deckers.thibault.aves'
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
applicationId packageName
|
||||
minSdk flutter.minSdkVersion
|
||||
targetSdk 35
|
||||
targetSdk 36
|
||||
versionCode flutter.versionCode
|
||||
versionName flutter.versionName
|
||||
manifestPlaceholders = [googleApiKey: keystoreProperties["googleApiKey"] ?: "<NONE>"]
|
||||
|
@ -149,14 +149,14 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2'
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.7.0"
|
||||
implementation "androidx.appcompat:appcompat:1.7.1"
|
||||
implementation 'androidx.core:core-ktx:1.16.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.9.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.9.1'
|
||||
implementation 'androidx.media:media:1.7.0'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.security:security-crypto:1.1.0-alpha07'
|
||||
implementation 'androidx.security:security-crypto:1.1.0-beta01'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.10.1'
|
||||
|
||||
implementation 'com.commonsware.cwac:document:0.5.0'
|
||||
|
@ -164,7 +164,7 @@ dependencies {
|
|||
implementation "com.github.bumptech.glide:glide:$glide_version"
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
// SLF4J implementation for `mp4parser`
|
||||
implementation 'org.slf4j:slf4j-simple:2.0.16'
|
||||
implementation 'org.slf4j:slf4j-simple:2.0.17'
|
||||
|
||||
// forked, built by JitPack:
|
||||
// - https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
|
||||
|
@ -178,7 +178,7 @@ dependencies {
|
|||
implementation 'com.github.deckerst:pixymeta-android:cb1cdc932e'
|
||||
implementation project(':exifinterface')
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.4'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.13.1'
|
||||
|
||||
kapt 'androidx.annotation:annotation:1.9.1'
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
|
|
|
@ -2,6 +2,7 @@ package deckers.thibault.aves.channel.calls
|
|||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
|
@ -18,7 +19,6 @@ import kotlinx.coroutines.SupervisorJob
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
||||
class AnalysisHandler(private val activity: FlutterFragmentActivity, private val onAnalysisCompleted: () -> Unit) : MethodChannel.MethodCallHandler {
|
||||
private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
|
@ -38,9 +38,8 @@ class AnalysisHandler(private val activity: FlutterFragmentActivity, private val
|
|||
}
|
||||
|
||||
val preferences = activity.getSharedPreferences(AnalysisWorker.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||
with(preferences.edit()) {
|
||||
preferences.edit {
|
||||
putLong(AnalysisWorker.PREF_CALLBACK_HANDLE_KEY, callbackHandle)
|
||||
apply()
|
||||
}
|
||||
result.success(true)
|
||||
}
|
||||
|
@ -69,9 +68,8 @@ class AnalysisHandler(private val activity: FlutterFragmentActivity, private val
|
|||
// work `Data` cannot occupy more than 10240 bytes when serialized
|
||||
// so we save the possibly long list of entry IDs to shared preferences
|
||||
val preferences = activity.getSharedPreferences(AnalysisWorker.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||
with(preferences.edit()) {
|
||||
preferences.edit {
|
||||
putStringSet(AnalysisWorker.PREF_ENTRY_IDS_KEY, allEntryIds?.map { it.toString() }?.toSet())
|
||||
apply()
|
||||
}
|
||||
|
||||
val workData = workDataOf(
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.LocaleConfig
|
||||
import android.app.LocaleManager
|
||||
import android.content.Context
|
||||
|
@ -102,6 +103,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
@SuppressLint("WrongConstant")
|
||||
val lm = context.getSystemService(Context.LOCALE_SERVICE) as? LocaleManager
|
||||
lm?.overrideLocaleConfig = LocaleConfig(LocaleList.forLanguageTags(locales.joinToString(",")))
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ class GeocodingHandler(private val context: Context) : MethodCallHandler {
|
|||
private fun getAddress(call: MethodCall, result: MethodChannel.Result) {
|
||||
val latitude = call.argument<Number>("latitude")?.toDouble()
|
||||
val longitude = call.argument<Number>("longitude")?.toDouble()
|
||||
val localeString = call.argument<String>("locale")
|
||||
val localeLanguageTag = call.argument<String>("localeLanguageTag")
|
||||
val maxResults = call.argument<Int>("maxResults") ?: 1
|
||||
if (latitude == null || longitude == null) {
|
||||
result.error("getAddress-args", "missing arguments", null)
|
||||
|
@ -43,11 +43,8 @@ class GeocodingHandler(private val context: Context) : MethodCallHandler {
|
|||
return
|
||||
}
|
||||
|
||||
geocoder = geocoder ?: if (localeString != null) {
|
||||
val split = localeString.split("_")
|
||||
val language = split[0]
|
||||
val country = if (split.size > 1) split[1] else ""
|
||||
Geocoder(context, Locale(language, country))
|
||||
geocoder = geocoder ?: if (localeLanguageTag != null) {
|
||||
Geocoder(context, Locale.forLanguageTag(localeLanguageTag))
|
||||
} else {
|
||||
Geocoder(context)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.content.edit
|
||||
import deckers.thibault.aves.SearchSuggestionsProvider
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
@ -29,9 +30,8 @@ class GlobalSearchHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
val preferences = context.getSharedPreferences(SearchSuggestionsProvider.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
|
||||
with(preferences.edit()) {
|
||||
preferences.edit {
|
||||
putLong(SearchSuggestionsProvider.CALLBACK_HANDLE_KEY, callbackHandle)
|
||||
apply()
|
||||
}
|
||||
result.success(true)
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package deckers.thibault.aves.channel.calls
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
|
@ -45,7 +46,7 @@ class SecurityHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
val preferences = getStore()
|
||||
with(preferences.edit()) {
|
||||
preferences.edit {
|
||||
when (value) {
|
||||
is Boolean -> putBoolean(key, value)
|
||||
is Float -> putFloat(key, value)
|
||||
|
@ -58,7 +59,6 @@ class SecurityHandler(private val context: Context) : MethodCallHandler {
|
|||
return
|
||||
}
|
||||
}
|
||||
apply()
|
||||
}
|
||||
result.success(true)
|
||||
}
|
||||
|
|
|
@ -5,8 +5,10 @@ import android.graphics.Bitmap
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import android.util.Size
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.core.graphics.scale
|
||||
import androidx.core.net.toUri
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.DecodeFormat
|
||||
|
@ -17,6 +19,7 @@ import deckers.thibault.aves.decoder.AvesAppGlideModule
|
|||
import deckers.thibault.aves.decoder.MultiPageImage
|
||||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.applyExifOrientation
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.MimeTypes.SVG
|
||||
import deckers.thibault.aves.utils.MimeTypes.isVideo
|
||||
|
@ -25,6 +28,8 @@ import deckers.thibault.aves.utils.MimeTypes.needRotationAfterGlide
|
|||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import deckers.thibault.aves.utils.UriUtils.tryParseId
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class ThumbnailFetcher internal constructor(
|
||||
private val context: Context,
|
||||
|
@ -77,6 +82,29 @@ class ThumbnailFetcher internal constructor(
|
|||
}
|
||||
}
|
||||
|
||||
if (bitmap != null) {
|
||||
if (bitmap.width > width && bitmap.height > height) {
|
||||
val scalingFactor: Double = min(bitmap.width.toDouble() / width, bitmap.height.toDouble() / height)
|
||||
val dstWidth = (bitmap.width / scalingFactor).roundToInt()
|
||||
val dstHeight = (bitmap.height / scalingFactor).roundToInt()
|
||||
Log.d(
|
||||
LOG_TAG, "rescale thumbnail for mimeType=$mimeType uri=$uri width=$width height=$height" +
|
||||
", with bitmap byteCount=${bitmap.byteCount} size=${bitmap.width}x${bitmap.height}" +
|
||||
", to target=${dstWidth}x${dstHeight}"
|
||||
)
|
||||
bitmap = bitmap.scale(dstWidth, dstHeight)
|
||||
}
|
||||
|
||||
if (bitmap.byteCount > BITMAP_SIZE_DANGER_THRESHOLD) {
|
||||
result.error(
|
||||
"getThumbnail-large", "thumbnail bitmap dangerously large" +
|
||||
" for mimeType=$mimeType uri=$uri pageId=$pageId width=$width height=$height" +
|
||||
", with bitmap byteCount=${bitmap.byteCount} size=${bitmap.width}x${bitmap.height} config=${bitmap.config?.name}", null
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// do not recycle bitmaps fetched from `ContentResolver` or Glide as their lifecycle is unknown
|
||||
val recycle = false
|
||||
val bytes = BitmapUtils.getRawBytes(bitmap, recycle = recycle)
|
||||
|
@ -144,4 +172,9 @@ class ThumbnailFetcher internal constructor(
|
|||
Glide.with(context).clear(target)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<ThumbnailFetcher>()
|
||||
private const val BITMAP_SIZE_DANGER_THRESHOLD = 20 * (1 shl 20) // MB
|
||||
}
|
||||
}
|
|
@ -81,12 +81,12 @@ object PixyMetaHelper {
|
|||
output: OutputStream,
|
||||
iptcDataList: List<FieldMap>?,
|
||||
) {
|
||||
val iptc = iptcDataList?.flatMap {
|
||||
val iptc: List<IPTCDataSet> = iptcDataList?.flatMap {
|
||||
val record = it["record"] as Int
|
||||
val tag = it["tag"] as Int
|
||||
val values = it["values"] as List<*>
|
||||
values.map { data -> IPTCDataSet(IPTCRecord.fromRecordNumber(record), tag, data as ByteArray) }
|
||||
} ?: ArrayList<IPTCDataSet>()
|
||||
} ?: ArrayList()
|
||||
Metadata.insertIPTC(input, output, iptc)
|
||||
}
|
||||
|
||||
|
|
|
@ -5,5 +5,5 @@ import kotlin.math.pow
|
|||
|
||||
object MathUtils {
|
||||
fun highestPowerOf2(x: Int): Int = highestPowerOf2(x.toDouble())
|
||||
private fun highestPowerOf2(x: Double): Int = if (x < 1) 0 else 2.toDouble().pow(log2(x).toInt()).toInt()
|
||||
fun highestPowerOf2(x: Double): Int = if (x < 1) 0 else 2.toDouble().pow(log2(x).toInt()).toInt()
|
||||
}
|
|
@ -18,10 +18,10 @@ pluginManagement {
|
|||
|
||||
plugins {
|
||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.8.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.10" apply false
|
||||
id("com.google.devtools.ksp") version "2.1.10-1.0.29" apply false
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
id("com.android.application") version "8.10.1" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.21" apply false
|
||||
id("com.google.devtools.ksp") version "2.1.21-2.0.1" apply false
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
|
||||
include(":app")
|
||||
|
|
|
@ -15,7 +15,7 @@ class GeocodingService {
|
|||
final result = await _platform.invokeMethod('getAddress', <String, dynamic>{
|
||||
'latitude': coordinates.latitude,
|
||||
'longitude': coordinates.longitude,
|
||||
'locale': locale.toString(),
|
||||
'localeLanguageTag': locale.toLanguageTag(),
|
||||
// we only really need one address, but sometimes the native geocoder
|
||||
// returns nothing with `maxResults` of 1, but succeeds with `maxResults` of 2+
|
||||
'maxResults': 2,
|
||||
|
|
|
@ -247,7 +247,7 @@ class PlatformMediaFetchService implements MediaFetchService {
|
|||
return InteropDecoding.bytesToCodec(bytes);
|
||||
}
|
||||
} on PlatformException catch (e, stack) {
|
||||
if (_isUnknownVisual(mimeType)) {
|
||||
if (_isUnknownVisual(mimeType) || e.code == 'getThumbnail-large') {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:aves/theme/durations.dart';
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/aves_app.dart';
|
||||
import 'package:aves/widgets/collection/collection_page.dart';
|
||||
import 'package:aves/widgets/collection/entry_set_action_delegate.dart';
|
||||
import 'package:aves/widgets/collection/filter_bar.dart';
|
||||
|
@ -56,7 +57,7 @@ class CollectionAppBar extends StatefulWidget {
|
|||
State<CollectionAppBar> createState() => _CollectionAppBarState();
|
||||
}
|
||||
|
||||
class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerProviderStateMixin, WidgetsBindingObserver {
|
||||
class _CollectionAppBarState extends State<CollectionAppBar> with RouteAware, SingleTickerProviderStateMixin, WidgetsBindingObserver {
|
||||
final Set<StreamSubscription> _subscriptions = {};
|
||||
final EntrySetActionDelegate _actionDelegate = EntrySetActionDelegate();
|
||||
late AnimationController _browseToSelectAnimation;
|
||||
|
@ -122,6 +123,15 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final route = ModalRoute.of(context);
|
||||
if (route is PageRoute) {
|
||||
AvesApp.pageRouteObserver.subscribe(this, route);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant CollectionAppBar oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
|
@ -140,6 +150,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
..forEach((sub) => sub.cancel())
|
||||
..clear();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
AvesApp.pageRouteObserver.unsubscribe(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -151,6 +162,13 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
widget.collection.filterChangeNotifier.removeListener(_onFilterChanged);
|
||||
}
|
||||
|
||||
@override
|
||||
void didPushNext() {
|
||||
// unfocus when navigating away, so that when navigating back,
|
||||
// the query bar does not get back focus and bring the keyboard
|
||||
_queryBarFocusNode.unfocus();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
// when top padding or text scale factor change
|
||||
|
|
|
@ -11,6 +11,7 @@ import 'package:aves/model/source/collection_source.dart';
|
|||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/theme/themes.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/aves_app.dart';
|
||||
import 'package:aves/widgets/common/action_controls/togglers/title_search.dart';
|
||||
import 'package:aves/widgets/common/app_bar/app_bar_subtitle.dart';
|
||||
import 'package:aves/widgets/common/app_bar/app_bar_title.dart';
|
||||
|
@ -78,7 +79,7 @@ class FilterGridAppBar<T extends CollectionFilter, CSAD extends ChipSetActionDel
|
|||
}
|
||||
}
|
||||
|
||||
class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetActionDelegate<T>> extends State<FilterGridAppBar<T, CSAD>> with SingleTickerProviderStateMixin, WidgetsBindingObserver {
|
||||
class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetActionDelegate<T>> extends State<FilterGridAppBar<T, CSAD>> with RouteAware, SingleTickerProviderStateMixin, WidgetsBindingObserver {
|
||||
final Set<StreamSubscription> _subscriptions = {};
|
||||
late AnimationController _browseToSelectAnimation;
|
||||
final ValueNotifier<bool> _isSelectingNotifier = ValueNotifier(false);
|
||||
|
@ -112,6 +113,15 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
|||
WidgetsBinding.instance.addPostFrameCallback((_) => _updateAppBarHeight());
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
final route = ModalRoute.of(context);
|
||||
if (route is PageRoute) {
|
||||
AvesApp.pageRouteObserver.subscribe(this, route);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_queryBarFocusNode.dispose();
|
||||
|
@ -122,9 +132,17 @@ class _FilterGridAppBarState<T extends CollectionFilter, CSAD extends ChipSetAct
|
|||
..forEach((sub) => sub.cancel())
|
||||
..clear();
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
AvesApp.pageRouteObserver.unsubscribe(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didPushNext() {
|
||||
// unfocus when navigating away, so that when navigating back,
|
||||
// the query bar does not get back focus and bring the keyboard
|
||||
_queryBarFocusNode.unfocus();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeMetrics() {
|
||||
// when text scale factor changes
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:aves/app_mode.dart';
|
||||
import 'package:aves/image_providers/app_icon_image_provider.dart';
|
||||
import 'package:aves/model/app_inventory.dart';
|
||||
import 'package:aves/model/dynamic_albums.dart';
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/favourites.dart';
|
||||
import 'package:aves/model/entry/extensions/multipage.dart';
|
||||
|
@ -133,6 +134,7 @@ class _BasicSectionState extends State<BasicSection> {
|
|||
if (entry.isPureVideo && !entry.is360) MimeFilter.video,
|
||||
if (dateTime != null) ...[DateFilter(DateLevel.ymd, dateTime.date), WeekDayFilter(dateTime.weekday)],
|
||||
if (album != null) StoredAlbumFilter(album, collection?.source.getStoredAlbumDisplayName(context, album)),
|
||||
...dynamicAlbums.all.where((v) => v.test(entry)).toSet(),
|
||||
if (entry.rating != 0) RatingFilter(entry.rating),
|
||||
...tags.map(TagFilter.new),
|
||||
};
|
||||
|
|
|
@ -209,7 +209,7 @@ class _EntryGoogleMapState<T> extends State<EntryGoogleMap<T>> {
|
|||
bitmapScaling: MapBitmapScaling.none,
|
||||
),
|
||||
position: _toServiceLatLng(dotLocation),
|
||||
zIndex: 1,
|
||||
zIndexInt: 1,
|
||||
)
|
||||
},
|
||||
polylines: {
|
||||
|
|
32
pubspec.lock
32
pubspec.lock
|
@ -112,14 +112,6 @@ packages:
|
|||
url: "https://github.com/deckerst/flutter_google_charts.git"
|
||||
source: git
|
||||
version: "0.12.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -180,10 +172,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "4b8701e48a58f7712492c9b1f7ba0bb9d525644dd66d023b62e1fc8cdb560c8a"
|
||||
sha256: aa07dbe5f2294c827b7edb9a87bba44a9c15a3cc81bc8da2ca19b37322d30080
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.14.0"
|
||||
version: "1.14.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -588,10 +580,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_platform_interface
|
||||
sha256: "970c8f766c02909c7be282dea923c971f83a88adaf07f8871d0aacebc3b07bb2"
|
||||
sha256: f8293f072ed8b068b092920a72da6693aa8b3d62dc6b5c5f0bc44c969a8a776c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.1"
|
||||
version: "2.12.1"
|
||||
google_maps_flutter_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -680,14 +672,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
latlong2:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1234,14 +1218,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
qr:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
Loading…
Reference in a new issue