support for kitkat (API 19)

This commit is contained in:
Thibault Deckers 2021-11-24 11:16:16 +09:00
parent 2d5f125431
commit 0f5bf13634
13 changed files with 65 additions and 41 deletions

View file

@ -8,6 +8,7 @@ All notable changes to this project will be documented in this file.
- add and remove tags to JPEG/GIF/PNG/TIFF images - add and remove tags to JPEG/GIF/PNG/TIFF images
- French translation - French translation
- restored support for Android KitKat (without Google Maps)
## [v1.5.6] - 2021-11-12 ## [v1.5.6] - 2021-11-12

View file

@ -29,7 +29,7 @@ It scans your media collection to identify **motion photos**, **panoramas** (aka
**Navigation and search** is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. **Navigation and search** is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc.
Aves integrates with Android (from **API 20 to 31**, i.e. from Lollipop to S) with features such as **app shortcuts** and **global search** handling. It also works as a **media viewer and picker**. Aves integrates with Android (from **API 19 to 31**, i.e. from KitKat to S) with features such as **app shortcuts** and **global search** handling. It also works as a **media viewer and picker**.
## Screenshots ## Screenshots
@ -82,5 +82,10 @@ To run the app:
# flutter run -t lib/main_play.dart --flavor play # flutter run -t lib/main_play.dart --flavor play
``` ```
To run the app on API 19 emulators:
```
# flutter run -t lib/main_play.dart --flavor play --enable-software-rendering
```
[Version badge]: https://img.shields.io/github/v/release/deckerst/aves?include_prereleases&sort=semver [Version badge]: https://img.shields.io/github/v/release/deckerst/aves?include_prereleases&sort=semver
[Build badge]: https://img.shields.io/github/workflow/status/deckerst/aves/Quality%20check [Build badge]: https://img.shields.io/github/workflow/status/deckerst/aves/Quality%20check

View file

@ -55,9 +55,9 @@ android {
applicationId appId applicationId appId
// minSdkVersion constraints: // minSdkVersion constraints:
// - Flutter & other plugins: 16 // - Flutter & other plugins: 16
// - google_maps_flutter v2.0.5: 20 // - google_maps_flutter v2.1.1: 20
// - Aves native: 19 // - Aves native: 19
minSdkVersion 20 minSdkVersion 19
targetSdkVersion 31 targetSdkVersion 31
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName

View file

@ -4,21 +4,12 @@
android:installLocation="auto"> android:installLocation="auto">
<!-- <!--
Scoped storage for primary storage is unusable on Android Q, Scoped storage on Android Q is inconvenient because users need to confirm edition on each individual file.
because users are required to confirm each file to be edited or deleted. So we request `WRITE_EXTERNAL_STORAGE` until Q (29), and enable `requestLegacyExternalStorage`
These items can only be deleted one by one after catching
a `RecoverableSecurityException` and requesting permission for each.
Android R improvements:
- bulk changes (e.g. `createDeleteRequest`):
https://developer.android.com/preview/privacy/storage#media-file-access
- raw path access:
https://developer.android.com/preview/privacy/storage#media-files-raw-paths
--> -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- request write permission until Q (29) included, because scoped storage is unusable -->
<uses-permission <uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="29" android:maxSdkVersion="29"
@ -34,6 +25,9 @@
<!-- for API < 26 --> <!-- for API < 26 -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" /> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- allow install on API 19, but Google Maps is from API 20 -->
<uses-sdk tools:overrideLibrary="io.flutter.plugins.googlemaps" />
<!-- from Android R, we should define <queries> to make other apps visible to this app --> <!-- from Android R, we should define <queries> to make other apps visible to this app -->
<queries> <queries>
<intent> <intent>

View file

@ -27,7 +27,7 @@ class AccessibilityHandler(private val activity: Activity) : MethodCallHandler {
try { try {
removed = Settings.Global.getFloat(activity.contentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE) == 0f removed = Settings.Global.getFloat(activity.contentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE) == 0f
} catch (e: Exception) { } catch (e: Exception) {
Log.w(LOG_TAG, "failed to get settings", e) Log.w(LOG_TAG, "failed to get settings with error=${e.message}", null)
} }
result.success(removed) result.success(removed)
} }

View file

@ -45,7 +45,7 @@ class WindowHandler(private val activity: Activity) : MethodCallHandler {
try { try {
locked = Settings.System.getInt(activity.contentResolver, Settings.System.ACCELEROMETER_ROTATION) == 0 locked = Settings.System.getInt(activity.contentResolver, Settings.System.ACCELEROMETER_ROTATION) == 0
} catch (e: Exception) { } catch (e: Exception) {
Log.w(LOG_TAG, "failed to get settings", e) Log.w(LOG_TAG, "failed to get settings with error=${e.message}", null)
} }
result.success(locked) result.success(locked)
} }

View file

@ -56,7 +56,7 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.w(LOG_TAG, "failed to get settings", e) Log.w(LOG_TAG, "failed to get settings with error=${e.message}", null)
} }
return changed return changed
} }

View file

@ -2,4 +2,4 @@
<b>Navigation und Suche</b> ist ein wichtiger Bestandteil von <i>Aves</i>. Das Ziel besteht darin, dass Benutzer problemlos von Alben zu Fotos zu Tags zu Karten usw. wechseln können. <b>Navigation und Suche</b> ist ein wichtiger Bestandteil von <i>Aves</i>. Das Ziel besteht darin, dass Benutzer problemlos von Alben zu Fotos zu Tags zu Karten usw. wechseln können.
<i>Aves</i> lässt sich mit Android (von <b>API 20 bis 31</b>, d. h. von Lollipop bis S) mit Funktionen wie <b>App-Verknüpfungen</b> und <b>globaler Suche</b> integrieren. Es funktioniert auch als <b>Medienbetrachter und -auswahl</b>. <i>Aves</i> lässt sich mit Android (von <b>API 19 bis 31</b>, d. h. von KitKat bis S) mit Funktionen wie <b>App-Verknüpfungen</b> und <b>globaler Suche</b> integrieren. Es funktioniert auch als <b>Medienbetrachter und -auswahl</b>.

View file

@ -2,4 +2,4 @@
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc. <b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
<i>Aves</i> integrates with Android (from <b>API 20 to 31</b>, i.e. from Lollipop to S) with features such as <b>app shortcuts</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>. <i>Aves</i> integrates with Android (from <b>API 19 to 31</b>, i.e. from KitKat to S) with features such as <b>app shortcuts</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.

View file

@ -1,6 +1,7 @@
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:github/github.dart'; import 'package:github/github.dart';
@ -17,6 +18,8 @@ abstract class AvesAvailability {
Future<bool> get canLocatePlaces; Future<bool> get canLocatePlaces;
Future<bool> get canUseGoogleMaps;
Future<bool> get isNewVersionAvailable; Future<bool> get isNewVersionAvailable;
} }
@ -59,6 +62,13 @@ class LiveAvesAvailability implements AvesAvailability {
@override @override
Future<bool> get canLocatePlaces => Future.wait<bool>([isConnected, hasPlayServices]).then((results) => results.every((result) => result)); Future<bool> get canLocatePlaces => Future.wait<bool>([isConnected, hasPlayServices]).then((results) => results.every((result) => result));
// as of google_maps_flutter v2.1.1, minSDK is 20 because of default PlatformView usage,
// but using hybrid composition would make it usable on 19 too, cf https://github.com/flutter/flutter/issues/23728
Future<bool> get _isUseGoogleMapRenderingSupported => DeviceInfoPlugin().androidInfo.then((androidInfo) => (androidInfo.version.sdkInt ?? 0) >= 20);
@override
Future<bool> get canUseGoogleMaps => Future.wait<bool>([_isUseGoogleMapRenderingSupported, hasPlayServices]).then((results) => results.every((result) => result));
@override @override
Future<bool> get isNewVersionAvailable async { Future<bool> get isNewVersionAvailable async {
if (_isNewVersionAvailable != null) return SynchronousFuture(_isNewVersionAvailable!); if (_isNewVersionAvailable != null) return SynchronousFuture(_isNewVersionAvailable!);

View file

@ -152,8 +152,8 @@ class Settings extends ChangeNotifier {
enableOverlayBlurEffect = performanceClass >= 29; enableOverlayBlurEffect = performanceClass >= 29;
// availability // availability
final hasPlayServices = await availability.hasPlayServices; final canUseGoogleMaps = await availability.canUseGoogleMaps;
if (hasPlayServices) { if (canUseGoogleMaps) {
infoMapStyle = EntryMapStyle.googleNormal; infoMapStyle = EntryMapStyle.googleNormal;
} else { } else {
final styles = EntryMapStyle.values.whereNot((v) => v.isGoogleMaps).toList(); final styles = EntryMapStyle.values.whereNot((v) => v.isGoogleMaps).toList();

View file

@ -135,8 +135,8 @@ class MapButtonPanel extends StatelessWidget {
child: MapOverlayButton( child: MapOverlayButton(
icon: const Icon(AIcons.layers), icon: const Icon(AIcons.layers),
onPressed: () async { onPressed: () async {
final hasPlayServices = await availability.hasPlayServices; final canUseGoogleMaps = await availability.canUseGoogleMaps;
final availableStyles = EntryMapStyle.values.where((style) => !style.isGoogleMaps || hasPlayServices); final availableStyles = EntryMapStyle.values.where((style) => !style.isGoogleMaps || canUseGoogleMaps);
final preferredStyle = settings.infoMapStyle; final preferredStyle = settings.infoMapStyle;
final initialStyle = availableStyles.contains(preferredStyle) ? preferredStyle : availableStyles.first; final initialStyle = availableStyles.contains(preferredStyle) ? preferredStyle : availableStyles.first;
final style = await showDialog<EntryMapStyle>( final style = await showDialog<EntryMapStyle>(

View file

@ -119,14 +119,14 @@ packages:
name: connectivity_plus name: connectivity_plus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
connectivity_plus_linux: connectivity_plus_linux:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_linux name: connectivity_plus_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
connectivity_plus_macos: connectivity_plus_macos:
dependency: transitive dependency: transitive
description: description:
@ -140,7 +140,7 @@ packages:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
connectivity_plus_web: connectivity_plus_web:
dependency: transitive dependency: transitive
description: description:
@ -196,7 +196,7 @@ packages:
name: dbus name: dbus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.6" version: "0.6.6"
decorated_icon: decorated_icon:
dependency: "direct main" dependency: "direct main"
description: description:
@ -340,7 +340,7 @@ packages:
name: flex_color_picker name: flex_color_picker
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.2" version: "2.2.0"
fluster: fluster:
dependency: "direct main" dependency: "direct main"
description: description:
@ -404,7 +404,7 @@ packages:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.4" version: "2.0.5"
flutter_staggered_animations: flutter_staggered_animations:
dependency: "direct main" dependency: "direct main"
description: description:
@ -468,7 +468,7 @@ packages:
name: google_maps_flutter name: google_maps_flutter
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
google_maps_flutter_platform_interface: google_maps_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -573,7 +573,7 @@ packages:
name: markdown name: markdown
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
@ -629,7 +629,7 @@ packages:
name: nm name: nm
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.4.1"
node_preamble: node_preamble:
dependency: transitive dependency: transitive
description: description:
@ -727,7 +727,7 @@ packages:
name: path_provider_linux name: path_provider_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -741,14 +741,14 @@ packages:
name: path_provider_windows name: path_provider_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
pdf: pdf:
dependency: "direct main" dependency: "direct main"
description: description:
name: pdf name: pdf
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.6.1" version: "3.6.3"
pedantic: pedantic:
dependency: transitive dependency: transitive
description: description:
@ -769,7 +769,7 @@ packages:
name: permission_handler name: permission_handler
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "8.2.6" version: "8.3.0"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -818,7 +818,7 @@ packages:
name: printing name: printing
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.6.0" version: "5.6.3"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -867,6 +867,20 @@ packages:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.9"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
shared_preferences_ios:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8" version: "2.0.8"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
@ -874,7 +888,7 @@ packages:
name: shared_preferences_linux name: shared_preferences_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shared_preferences_macos: shared_preferences_macos:
dependency: transitive dependency: transitive
description: description:
@ -902,7 +916,7 @@ packages:
name: shared_preferences_windows name: shared_preferences_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -1084,7 +1098,7 @@ packages:
name: url_launcher name: url_launcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.12" version: "6.0.15"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@ -1175,7 +1189,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.10" version: "2.3.0"
wkt_parser: wkt_parser:
dependency: transitive dependency: transitive
description: description: