android 14

This commit is contained in:
Thibault Deckers 2023-06-11 19:28:59 +02:00
parent bd55ff2971
commit 55acf408d0
16 changed files with 38 additions and 64 deletions

View file

@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
### Changed ### Changed
- target Android 14 (API 34)
- upgraded Flutter to stable v3.10.4 - upgraded Flutter to stable v3.10.4
### Fixed ### Fixed

View file

@ -48,7 +48,7 @@ if (keystorePropertiesFile.exists()) {
android { android {
namespace 'deckers.thibault.aves' namespace 'deckers.thibault.aves'
compileSdk 33 compileSdk 34
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {
@ -75,7 +75,7 @@ android {
// which implementation `DocumentBuilderImpl` is provided by the OS and is not customizable on 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 // but the implementation on API <19 is not robust enough and fails to build XMP documents
minSdkVersion 19 minSdkVersion 19
targetSdkVersion 33 targetSdkVersion 34
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
manifestPlaceholders = [googleApiKey: keystoreProperties["googleApiKey"] ?: "<NONE>", manifestPlaceholders = [googleApiKey: keystoreProperties["googleApiKey"] ?: "<NONE>",

View file

@ -15,6 +15,7 @@
TODO TLAD [Android 14 (API 34)] request/handle READ_MEDIA_VISUAL_USER_SELECTED permission TODO TLAD [Android 14 (API 34)] request/handle READ_MEDIA_VISUAL_USER_SELECTED permission
cf https://developer.android.com/about/versions/14/changes/partial-photo-video-access cf https://developer.android.com/about/versions/14/changes/partial-photo-video-access
--> -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission <uses-permission

View file

@ -96,12 +96,7 @@ class SvgRegionFetcher internal constructor(
svg.renderToCanvas(canvas, renderOptions) svg.renderToCanvas(canvas, renderOptions)
bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight) bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight)
result.success(bitmap.getBytes(canHaveAlpha = true, recycle = true))
if (bitmap != null) {
result.success(bitmap.getBytes(canHaveAlpha = true, recycle = true))
} else {
result.error("fetch-null", "failed to decode region for uri=$uri regionRect=$regionRect", null)
}
} catch (e: Exception) { } catch (e: Exception) {
result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message) result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message)
} }

View file

@ -36,7 +36,6 @@ fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): Ap
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
getApplicationInfo(packageName, PackageManager.ApplicationInfoFlags.of(flags.toLong())) getApplicationInfo(packageName, PackageManager.ApplicationInfoFlags.of(flags.toLong()))
} else { } else {
@Suppress("deprecation")
getApplicationInfo(packageName, flags) getApplicationInfo(packageName, flags)
} }
} }
@ -45,7 +44,6 @@ fun PackageManager.queryIntentActivitiesCompat(intent: Intent, flags: Int): List
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(flags.toLong())) queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(flags.toLong()))
} else { } else {
@Suppress("deprecation")
queryIntentActivities(intent, flags) queryIntentActivities(intent, flags)
} }
} }

View file

@ -1,13 +1,10 @@
package deckers.thibault.aves.utils package deckers.thibault.aves.utils
import android.app.ActivityManager
import android.app.Service
import android.content.ContentResolver import android.content.ContentResolver
import android.content.ContentUris import android.content.ContentUris
import android.content.Context import android.content.Context
import android.database.Cursor import android.database.Cursor
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract
import android.provider.MediaStore import android.provider.MediaStore
import android.util.Log import android.util.Log
import deckers.thibault.aves.utils.UriUtils.tryParseId import deckers.thibault.aves.utils.UriUtils.tryParseId
@ -24,19 +21,6 @@ object ContextUtils {
.build() .build()
} }
fun Context.isMyServiceRunning(serviceClass: Class<out Service>): Boolean {
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
am ?: return false
@Suppress("deprecation")
return am.getRunningServices(Integer.MAX_VALUE).any { it.service.className == serviceClass.name }
}
// `flag`: `DocumentsContract.Document.FLAG_SUPPORTS_COPY`, etc.
fun Context.queryDocumentProviderFlag(docUri: Uri, flag: Int): Boolean {
val flags = queryContentPropValue(docUri, "", DocumentsContract.Document.COLUMN_FLAGS) as Long?
return if (flags != null) (flags.toInt() and flag) == flag else false
}
fun Context.queryContentPropValue(uri: Uri, mimeType: String, column: String): Any? { fun Context.queryContentPropValue(uri: Uri, mimeType: String, column: String): Any? {
var contentUri: Uri = uri var contentUri: Uri = uri
if (StorageUtils.isMediaStoreContentUri(uri)) { if (StorageUtils.isMediaStoreContentUri(uri)) {

View file

@ -11,6 +11,7 @@ import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.embedding.engine.loader.FlutterLoader import io.flutter.embedding.engine.loader.FlutterLoader
import io.flutter.view.FlutterCallbackInformation import io.flutter.view.FlutterCallbackInformation
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@ -60,7 +61,7 @@ object FlutterUtils {
suspend fun runOnUiThread(r: Runnable) { suspend fun runOnUiThread(r: Runnable) {
val mainLooper = Looper.getMainLooper() val mainLooper = Looper.getMainLooper()
if (Looper.myLooper() != mainLooper) { if (Looper.myLooper() != mainLooper) {
suspendCoroutine<Boolean> { cont -> suspendCoroutine { cont: Continuation<Boolean> ->
Handler(mainLooper).post { Handler(mainLooper).post {
r.run() r.run()
cont.resume(true) cont.resume(true)

View file

@ -1,7 +1,7 @@
buildscript { buildscript {
ext { ext {
kotlin_version = '1.8.21' kotlin_version = '1.8.21'
agp_version = '8.0.1' agp_version = '8.0.2'
glide_version = '4.15.1' glide_version = '4.15.1'
// AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550 // AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550
// TODO TLAD AppGallery Connect plugin v1.9.0.300 does not support Gradle 8+ // TODO TLAD AppGallery Connect plugin v1.9.0.300 does not support Gradle 8+

View file

@ -17,7 +17,7 @@ class AIcons {
static const brightnessMin = Icons.brightness_low_outlined; static const brightnessMin = Icons.brightness_low_outlined;
static const brightnessMax = Icons.brightness_high_outlined; static const brightnessMax = Icons.brightness_high_outlined;
static const checked = Icons.done_outlined; static const checked = Icons.done_outlined;
static const count = MdiIcons.counter; static final count = MdiIcons.counter;
static const counter = Icons.plus_one_outlined; static const counter = Icons.plus_one_outlined;
static const date = Icons.calendar_today_outlined; static const date = Icons.calendar_today_outlined;
static const dateByDay = Icons.today_outlined; static const dateByDay = Icons.today_outlined;
@ -42,11 +42,11 @@ class AIcons {
static const mainStorage = Icons.smartphone_outlined; static const mainStorage = Icons.smartphone_outlined;
static const mimeType = Icons.code_outlined; static const mimeType = Icons.code_outlined;
static const opacity = Icons.opacity; static const opacity = Icons.opacity;
static const privacy = MdiIcons.shieldAccountOutline; static final privacy = MdiIcons.shieldAccountOutline;
static const rating = Icons.star_border_outlined; static const rating = Icons.star_border_outlined;
static const ratingFull = Icons.star; static const ratingFull = Icons.star;
static const ratingRejected = MdiIcons.starMinusOutline; static final ratingRejected = MdiIcons.starMinusOutline;
static const ratingUnrated = MdiIcons.starOffOutline; static final ratingUnrated = MdiIcons.starOffOutline;
static const raw = Icons.raw_on_outlined; static const raw = Icons.raw_on_outlined;
static const shooting = Icons.camera_outlined; static const shooting = Icons.camera_outlined;
static const removableStorage = Icons.sd_storage_outlined; static const removableStorage = Icons.sd_storage_outlined;
@ -56,7 +56,7 @@ class AIcons {
static const size = Icons.data_usage_outlined; static const size = Icons.data_usage_outlined;
static const text = Icons.format_quote_outlined; static const text = Icons.format_quote_outlined;
static const tag = Icons.local_offer_outlined; static const tag = Icons.local_offer_outlined;
static const tagUntagged = MdiIcons.tagOffOutline; static final tagUntagged = MdiIcons.tagOffOutline;
static const volumeMin = Icons.volume_mute_outlined; static const volumeMin = Icons.volume_mute_outlined;
static const volumeMax = Icons.volume_up_outlined; static const volumeMax = Icons.volume_up_outlined;
@ -79,35 +79,35 @@ class AIcons {
static const clear = Icons.clear_outlined; static const clear = Icons.clear_outlined;
static const clipboard = Icons.content_copy_outlined; static const clipboard = Icons.content_copy_outlined;
static const convert = Icons.transform_outlined; static const convert = Icons.transform_outlined;
static const convertToStillImage = MdiIcons.movieRemoveOutline; static final convertToStillImage = MdiIcons.movieRemoveOutline;
static const copy = Icons.file_copy_outlined; static const copy = Icons.file_copy_outlined;
static const debug = Icons.whatshot_outlined; static const debug = Icons.whatshot_outlined;
static const delete = Icons.delete_outlined; static const delete = Icons.delete_outlined;
static const edit = Icons.edit_outlined; static const edit = Icons.edit_outlined;
static const emptyBin = Icons.delete_sweep_outlined; static const emptyBin = Icons.delete_sweep_outlined;
static const export = Icons.open_with_outlined; static const export = Icons.open_with_outlined;
static const fileExport = MdiIcons.fileExportOutline; static final fileExport = MdiIcons.fileExportOutline;
static const fileImport = MdiIcons.fileImportOutline; static final fileImport = MdiIcons.fileImportOutline;
static const flip = Icons.flip_outlined; static const flip = Icons.flip_outlined;
static const favourite = Icons.favorite_border; static const favourite = Icons.favorite_border;
static const favouriteActive = Icons.favorite; static const favouriteActive = Icons.favorite;
static const filter = MdiIcons.filterOutline; static final filter = MdiIcons.filterOutline;
static const filterOff = MdiIcons.filterOffOutline; static final filterOff = MdiIcons.filterOffOutline;
static const geoBounds = Icons.public_outlined; static const geoBounds = Icons.public_outlined;
static const goUp = Icons.arrow_upward_outlined; static const goUp = Icons.arrow_upward_outlined;
static const hide = Icons.visibility_off_outlined; static const hide = Icons.visibility_off_outlined;
static const info = Icons.info_outlined; static const info = Icons.info_outlined;
static const layers = Icons.layers_outlined; static const layers = Icons.layers_outlined;
static const map = Icons.map_outlined; static const map = Icons.map_outlined;
static const move = MdiIcons.fileMoveOutline; static final move = MdiIcons.fileMoveOutline;
static const mute = Icons.volume_off_outlined; static const mute = Icons.volume_off_outlined;
static const unmute = Icons.volume_up_outlined; static const unmute = Icons.volume_up_outlined;
static const name = Icons.abc_outlined; static const name = Icons.abc_outlined;
static const newTier = Icons.fiber_new_outlined; static const newTier = Icons.fiber_new_outlined;
static const openOutside = Icons.open_in_new_outlined; static const openOutside = Icons.open_in_new_outlined;
static const openVideo = MdiIcons.moviePlayOutline; static final openVideo = MdiIcons.moviePlayOutline;
static const pin = Icons.push_pin_outlined; static const pin = Icons.push_pin_outlined;
static const unpin = MdiIcons.pinOffOutline; static final unpin = MdiIcons.pinOffOutline;
static const play = Icons.play_arrow; static const play = Icons.play_arrow;
static const pause = Icons.pause; static const pause = Icons.pause;
static const print = Icons.print_outlined; static const print = Icons.print_outlined;
@ -123,10 +123,10 @@ class AIcons {
static const search = Icons.search_outlined; static const search = Icons.search_outlined;
static const select = Icons.select_all_outlined; static const select = Icons.select_all_outlined;
static const setAs = Icons.wallpaper_outlined; static const setAs = Icons.wallpaper_outlined;
static const setCover = MdiIcons.imageEditOutline; static final setCover = MdiIcons.imageEditOutline;
static const share = Icons.share_outlined; static const share = Icons.share_outlined;
static const show = Icons.visibility_outlined; static const show = Icons.visibility_outlined;
static const showFullscreen = MdiIcons.arrowExpand; static final showFullscreen = MdiIcons.arrowExpand;
static const slideshow = Icons.slideshow_outlined; static const slideshow = Icons.slideshow_outlined;
static const speed = Icons.speed_outlined; static const speed = Icons.speed_outlined;
static const stats = Icons.donut_small_outlined; static const stats = Icons.donut_small_outlined;
@ -136,7 +136,7 @@ class AIcons {
static const streamText = Icons.closed_caption_outlined; static const streamText = Icons.closed_caption_outlined;
static const vaultLock = Icons.lock_outline; static const vaultLock = Icons.lock_outline;
static const vaultAdd = Icons.enhanced_encryption_outlined; static const vaultAdd = Icons.enhanced_encryption_outlined;
static const vaultConfigure = MdiIcons.shieldLockOutline; static final vaultConfigure = MdiIcons.shieldLockOutline;
static const videoSettings = Icons.video_settings_outlined; static const videoSettings = Icons.video_settings_outlined;
static const view = Icons.grid_view_outlined; static const view = Icons.grid_view_outlined;
static const viewerLock = Icons.lock_outline; static const viewerLock = Icons.lock_outline;
@ -176,6 +176,6 @@ class AIcons {
static const selected = Icons.check_circle_outline; static const selected = Icons.check_circle_outline;
static const unselected = Icons.radio_button_unchecked; static const unselected = Icons.radio_button_unchecked;
static const github = MdiIcons.github; static final github = MdiIcons.github;
static const legal = MdiIcons.scaleBalance; static final legal = MdiIcons.scaleBalance;
} }

View file

@ -19,7 +19,7 @@ class AppReference extends StatefulWidget {
static List<Widget> buildLinks(BuildContext context) { static List<Widget> buildLinks(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
return [ return [
const LinkChip( LinkChip(
leading: Icon( leading: Icon(
AIcons.github, AIcons.github,
size: 24, size: 24,
@ -28,7 +28,7 @@ class AppReference extends StatefulWidget {
urlString: AppReference.avesGithub, urlString: AppReference.avesGithub,
), ),
LinkChip( LinkChip(
leading: const Icon( leading: Icon(
AIcons.legal, AIcons.legal,
size: 22, size: 22,
), ),
@ -36,7 +36,7 @@ class AppReference extends StatefulWidget {
urlString: '${AppReference.avesGithub}/blob/main/LICENSE', urlString: '${AppReference.avesGithub}/blob/main/LICENSE',
), ),
LinkChip( LinkChip(
leading: const Icon( leading: Icon(
AIcons.privacy, AIcons.privacy,
size: 22, size: 22,
), ),

View file

@ -100,7 +100,7 @@ class TagIcon extends StatelessWidget {
factory TagIcon.tagged() => const TagIcon._private(icon: AIcons.tag); factory TagIcon.tagged() => const TagIcon._private(icon: AIcons.tag);
factory TagIcon.untagged() => const TagIcon._private(icon: AIcons.tagUntagged); factory TagIcon.untagged() => TagIcon._private(icon: AIcons.tagUntagged);
static const scale = .9; static const scale = .9;

View file

@ -171,7 +171,7 @@ class _TagEditorPageState extends State<TagEditorPage> {
child: Row( child: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
const Icon(AIcons.tagUntagged, color: untaggedColor), Icon(AIcons.tagUntagged, color: untaggedColor),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
l10n.filterNoTagLabel, l10n.filterNoTagLabel,

View file

@ -61,11 +61,11 @@ class _SettingsMobilePageState extends State<SettingsMobilePage> with FeedbackMi
return [ return [
PopupMenuItem( PopupMenuItem(
value: SettingsAction.export, value: SettingsAction.export,
child: MenuRow(text: context.l10n.settingsActionExport, icon: const Icon(AIcons.fileExport)), child: MenuRow(text: context.l10n.settingsActionExport, icon: Icon(AIcons.fileExport)),
), ),
PopupMenuItem( PopupMenuItem(
value: SettingsAction.import, value: SettingsAction.import,
child: MenuRow(text: context.l10n.settingsActionImport, icon: const Icon(AIcons.fileImport)), child: MenuRow(text: context.l10n.settingsActionImport, icon: Icon(AIcons.fileImport)),
), ),
]; ];
}, },

View file

@ -77,7 +77,7 @@ class _WelcomePageState extends State<WelcomePage> {
Padding( Padding(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
child: LinkChip( child: LinkChip(
leading: const Icon( leading: Icon(
AIcons.privacy, AIcons.privacy,
size: 22, size: 22,
), ),

View file

@ -798,18 +798,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: material_color_utilities name: material_color_utilities
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.5.0" version: "0.2.0"
material_design_icons_flutter: material_design_icons_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
name: material_design_icons_flutter name: material_design_icons_flutter
sha256: "8ef8562d16e747b2d93e5da5c2508931588939c5c00ebc8e2768e803db7dfd3c" sha256: "6f986b7a51f3ad4c00e33c5c84e8de1bdd140489bbcdc8b66fc1283dad4dea5a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.7096" version: "7.0.7296"
meta: meta:
dependency: transitive dependency: transitive
description: description:

View file

@ -128,12 +128,6 @@ dev_dependencies:
shared_preferences_platform_interface: shared_preferences_platform_interface:
test: test:
dependency_overrides:
# as of Flutter beta v3.10.0-1.5.pre, `flutter_driver`
# constrains `material_color_utilities` to v0.2.0, which
# constrains `dynamic_color` to v1.6.4, which is incompatible with AGP8
material_color_utilities: ^0.5.0
flutter: flutter:
assets: assets:
- assets/ - assets/