diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 483894b13..02c18dd4d 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -17,7 +17,7 @@ jobs:
# Available versions may lag behind https://github.com/flutter/flutter.git
- uses: subosito/flutter-action@v2
with:
- flutter-version: '3.0.1'
+ flutter-version: '3.0.2'
channel: 'stable'
- name: Clone the repository.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cae6d2637..273a45cba 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ jobs:
# Available versions may lag behind https://github.com/flutter/flutter.git
- uses: subosito/flutter-action@v2
with:
- flutter-version: '3.0.1'
+ flutter-version: '3.0.2'
channel: 'stable'
# Workaround for this Android Gradle Plugin issue (supposedly fixed in AGP 4.1):
@@ -56,15 +56,15 @@ jobs:
rm release.keystore.asc
mkdir outputs
(cd scripts/; ./apply_flavor_play.sh)
- flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.1.sksl.json
+ flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.2.sksl.json
cp build/app/outputs/bundle/playRelease/*.aab outputs
- flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.1.sksl.json
+ flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.2.sksl.json
cp build/app/outputs/apk/play/release/*.apk outputs
(cd scripts/; ./apply_flavor_huawei.sh)
- flutter build apk -t lib/main_huawei.dart --flavor huawei --bundle-sksl-path shaders_3.0.1.sksl.json
+ flutter build apk -t lib/main_huawei.dart --flavor huawei --bundle-sksl-path shaders_3.0.2.sksl.json
cp build/app/outputs/apk/huawei/release/*.apk outputs
(cd scripts/; ./apply_flavor_izzy.sh)
- flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.0.1.sksl.json
+ flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.0.2.sksl.json
cp build/app/outputs/apk/izzy/release/*.apk outputs
rm $AVES_STORE_FILE
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ad280197..ac8faa429 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.6.9] - 2022-06-18
+
+### Added
+
+- slideshow
+- set wallpaper from any media
+- optional dynamic accent color on Android 12+
+- Search: date/dimension/size field equality (undocumented)
+- support Android 13 (API 33)
+- Turkish translation (thanks metezd)
+
+### Changed
+
+- do not force quit on storage permission denial
+- upgraded Flutter to stable v3.0.2
+
+### Fixed
+
+- merge ambiguously cased directories
+
## [v1.6.8] - 2022-05-27
### Fixed
diff --git a/README.md b/README.md
index 271e29520..9af7d5a6a 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,9 @@ Aves is a gallery and metadata explorer app. It is built for Android, with Flutt
[
](https://play.google.com/store/apps/details?id=deckers.thibault.aves&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1)
+[
](https://appgallery.huawei.com/app/C106014023)
[
](https://www.amazon.com/dp/B09XQHQQ72)
@@ -90,7 +93,7 @@ At this stage this project does *not* accept PRs, except for translations.
### Translations
-If you want to translate this app in your language and share the result, [there is a guide](https://github.com/deckerst/aves/wiki/Contributing-to-Translations). English, Korean and French are already handled by me. Russian, German, Spanish, Portuguese, Indonesian, Japanese, Italian & Chinese are handled by generous volunteers.
+If you want to translate this app in your language and share the result, [there is a guide](https://github.com/deckerst/aves/wiki/Contributing-to-Translations). English, Korean and French are already handled by me. Russian, German, Spanish, Portuguese, Indonesian, Japanese, Italian, Chinese & Turkish are handled by generous volunteers.
### Donations
diff --git a/android/app/build.gradle b/android/app/build.gradle
index a8d4c0b0a..78f84d8c2 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -41,7 +41,7 @@ if (keystorePropertiesFile.exists()) {
}
android {
- compileSdkVersion 32
+ compileSdkVersion 33
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
@@ -57,7 +57,7 @@ android {
// which implementation `DocumentBuilderImpl` is provided by the OS and is not customizable on Android,
// but the implementation on API <19 is not robust enough and fails to build XMP documents
minSdkVersion 19
- targetSdkVersion 32
+ targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
manifestPlaceholders = [googleApiKey: keystoreProperties['googleApiKey']]
@@ -154,7 +154,7 @@ repositories {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
- implementation 'androidx.core:core-ktx:1.7.0'
+ implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.exifinterface:exifinterface:1.3.3'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.caverock:androidsvg-aar:1.4'
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 055de7299..68c90244b 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -6,16 +6,21 @@
Scoped storage on Android Q is inconvenient because users need to confirm edition on each individual file.
So we request `WRITE_EXTERNAL_STORAGE` until Q (29), and enable `requestLegacyExternalStorage`
-->
-
-
-
-
-
+
+
+
+
+
+
+
+
@@ -128,6 +133,26 @@
android:name="android.app.searchable"
android:resource="@xml/searchable" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
runBlocking {
FlutterUtils.runOnUiThread {
- val entryIds = data.get(KEY_ENTRY_IDS)?.takeIf { it is IntArray }?.let { (it as IntArray).toList() }
+ val entryIds = data.getIntArray(KEY_ENTRY_IDS)?.toList()
backgroundChannel?.invokeMethod(
"start", hashMapOf(
"entryIds" to entryIds,
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
index e1b78809a..f7f1a5b4b 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
@@ -15,6 +15,7 @@ import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.streams.*
import deckers.thibault.aves.utils.LogUtils
+import deckers.thibault.aves.utils.getParcelableExtraCompat
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
@@ -215,7 +216,7 @@ class MainActivity : FlutterActivity() {
}
}
Intent.ACTION_VIEW, Intent.ACTION_SEND, "com.android.camera.action.REVIEW" -> {
- (intent.data ?: (intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri))?.let { uri ->
+ (intent.data ?: intent.getParcelableExtraCompat(Intent.EXTRA_STREAM))?.let { uri ->
// MIME type is optional
val type = intent.type ?: intent.resolveType(context)
return hashMapOf(
@@ -332,6 +333,7 @@ class MainActivity : FlutterActivity() {
const val INTENT_ACTION_PICK = "pick"
const val INTENT_ACTION_SEARCH = "search"
+ const val INTENT_ACTION_SET_WALLPAPER = "set_wallpaper"
const val INTENT_ACTION_VIEW = "view"
const val SHORTCUT_KEY_PAGE = "page"
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
new file mode 100644
index 000000000..d7f28867e
--- /dev/null
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
@@ -0,0 +1,107 @@
+package deckers.thibault.aves
+
+import android.content.Intent
+import android.net.Uri
+import android.os.*
+import android.util.Log
+import app.loup.streams_channel.StreamsChannel
+import deckers.thibault.aves.channel.calls.*
+import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
+import deckers.thibault.aves.utils.LogUtils
+import deckers.thibault.aves.utils.getParcelableExtraCompat
+import io.flutter.embedding.android.FlutterActivity
+import io.flutter.plugin.common.MethodChannel
+
+class WallpaperActivity : FlutterActivity() {
+ private lateinit var intentDataMap: MutableMap
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ Log.i(LOG_TAG, "onCreate intent=$intent")
+ intent.extras?.takeUnless { it.isEmpty }?.let {
+ Log.i(LOG_TAG, "onCreate intent extras=$it")
+ }
+
+ super.onCreate(savedInstanceState)
+
+ val messenger = flutterEngine!!.dartExecutor.binaryMessenger
+
+ // dart -> platform -> dart
+ // - need Context
+ MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
+ MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this))
+ MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
+ // - need Activity
+ MethodChannel(messenger, AccessibilityHandler.CHANNEL).setMethodCallHandler(AccessibilityHandler(this))
+ MethodChannel(messenger, MediaFileHandler.CHANNEL).setMethodCallHandler(MediaFileHandler(this))
+ MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(this))
+ MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(WindowHandler(this))
+
+ // result streaming: dart -> platform ->->-> dart
+ // - need Context
+ StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(this, args) }
+
+ // intent handling
+ // detail fetch: dart -> platform
+ intentDataMap = extractIntentData(intent)
+ MethodChannel(messenger, VIEWER_CHANNEL).setMethodCallHandler { call, result ->
+ when (call.method) {
+ "getIntentData" -> {
+ result.success(intentDataMap)
+ intentDataMap.clear()
+ }
+ }
+ }
+ }
+
+ override fun onStart() {
+ Log.i(LOG_TAG, "onStart")
+ super.onStart()
+
+ // as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
+ // are incorrect on startup in some environments (e.g. API 29 emulator),
+ // so we manually request to apply the insets to update the window metrics
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
+ Handler(Looper.getMainLooper()).postDelayed({
+ window.decorView.requestApplyInsets()
+ }, 100)
+ }
+ }
+
+ override fun onStop() {
+ Log.i(LOG_TAG, "onStop")
+ super.onStop()
+ }
+
+ override fun onDestroy() {
+ Log.i(LOG_TAG, "onDestroy")
+ super.onDestroy()
+ }
+
+ private fun extractIntentData(intent: Intent?): MutableMap {
+ when (intent?.action) {
+ Intent.ACTION_ATTACH_DATA, Intent.ACTION_SET_WALLPAPER -> {
+ (intent.data ?: intent.getParcelableExtraCompat(Intent.EXTRA_STREAM))?.let { uri ->
+ // MIME type is optional
+ val type = intent.type ?: intent.resolveType(context)
+ return hashMapOf(
+ MainActivity.INTENT_DATA_KEY_ACTION to MainActivity.INTENT_ACTION_SET_WALLPAPER,
+ MainActivity.INTENT_DATA_KEY_MIME_TYPE to type,
+ MainActivity.INTENT_DATA_KEY_URI to uri.toString(),
+ )
+ }
+ }
+ Intent.ACTION_RUN -> {
+ // flutter run
+ }
+ else -> {
+ Log.w(LOG_TAG, "unhandled intent action=${intent?.action}")
+ }
+ }
+ return HashMap()
+ }
+
+ companion object {
+ private val LOG_TAG = LogUtils.createTag()
+ const val VIEWER_CHANNEL = "deckers.thibault/aves/viewer"
+ }
+}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt
index 9437745a3..c5b2d2223 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt
@@ -30,6 +30,8 @@ import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.BitmapUtils
import deckers.thibault.aves.utils.BitmapUtils.getBytes
import deckers.thibault.aves.utils.LogUtils
+import deckers.thibault.aves.utils.getApplicationInfoCompat
+import deckers.thibault.aves.utils.queryIntentActivitiesCompat
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
@@ -77,7 +79,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
}
val pm = context.packageManager
- for (resolveInfo in pm.queryIntentActivities(intent, 0)) {
+ for (resolveInfo in pm.queryIntentActivitiesCompat(intent, 0)) {
val appInfo = resolveInfo.activityInfo.applicationInfo
val packageName = appInfo.packageName
if (!packages.containsKey(packageName)) {
@@ -149,7 +151,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val size = (sizeDip * density).roundToInt()
var data: ByteArray? = null
try {
- val iconResourceId = context.packageManager.getApplicationInfo(packageName, 0).icon
+ val iconResourceId = context.packageManager.getApplicationInfoCompat(packageName, 0).icon
if (iconResourceId != Resources.ID_NULL) {
val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
@@ -444,4 +446,4 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
index 7bb635ad6..eda3f1d32 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
@@ -32,6 +32,8 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
"canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT),
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
+ "canSetLockScreenWallpaper" to (sdkInt >= Build.VERSION_CODES.N),
+ "isDynamicColorAvailable" to (sdkInt >= Build.VERSION_CODES.S),
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/GeocodingHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/GeocodingHandler.kt
index 0821058f3..1c16e2b02 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/GeocodingHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/GeocodingHandler.kt
@@ -1,12 +1,17 @@
package deckers.thibault.aves.channel.calls
import android.content.Context
+import android.location.Address
import android.location.Geocoder
+import android.os.Build
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.launch
import java.io.IOException
import java.util.*
@@ -48,36 +53,48 @@ class GeocodingHandler(private val context: Context) : MethodCallHandler {
Geocoder(context)
}
- val addresses = try {
- geocoder!!.getFromLocation(latitude, longitude, maxResults) ?: ArrayList()
- } catch (e: IOException) {
- // `grpc failed`, etc.
- result.error("getAddress-network", "failed to get address because of network issues", e.message)
- return
- } catch (e: Exception) {
- result.error("getAddress-exception", "failed to get address", e.message)
- return
+ fun processAddresses(addresses: List) {
+ if (addresses.isEmpty()) {
+ result.error("getAddress-empty", "failed to find any address for latitude=$latitude, longitude=$longitude", null)
+ } else {
+ val addressMapList: ArrayList