accessibility: fixed system check to remove animations
This commit is contained in:
parent
8560ebdd14
commit
3a0124a8e9
7 changed files with 71 additions and 36 deletions
|
@ -3,21 +3,35 @@ package deckers.thibault.aves.channel.calls
|
|||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
|
||||
class AccessibilityHandler(private val context: Activity) : MethodCallHandler {
|
||||
class AccessibilityHandler(private val activity: Activity) : MethodCallHandler {
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"areAnimationsRemoved" -> safe(call, result, ::areAnimationsRemoved)
|
||||
"hasRecommendedTimeouts" -> safe(call, result, ::hasRecommendedTimeouts)
|
||||
"getRecommendedTimeoutMillis" -> safe(call, result, ::getRecommendedTimeoutMillis)
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
private fun areAnimationsRemoved(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||
var removed = false
|
||||
try {
|
||||
removed = Settings.Global.getFloat(activity.contentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE) == 0f
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get settings", e)
|
||||
}
|
||||
result.success(removed)
|
||||
}
|
||||
|
||||
private fun hasRecommendedTimeouts(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
}
|
||||
|
@ -48,7 +62,7 @@ class AccessibilityHandler(private val context: Activity) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager
|
||||
val am = activity.getSystemService(Context.ACCESSIBILITY_SERVICE) as? AccessibilityManager
|
||||
if (am == null) {
|
||||
result.error("getRecommendedTimeoutMillis-service", "failed to get accessibility manager", null)
|
||||
return
|
||||
|
@ -59,6 +73,7 @@ class AccessibilityHandler(private val context: Activity) : MethodCallHandler {
|
|||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<AccessibilityHandler>()
|
||||
const val CHANNEL = "deckers.thibault/aves/accessibility"
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
|
||||
private val contentObserver = object : ContentObserver(null) {
|
||||
private var accelerometerRotation: Int = 0
|
||||
private var transitionAnimationScale: Float = 1f
|
||||
|
||||
init {
|
||||
update()
|
||||
|
@ -33,7 +34,8 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
if (update()) {
|
||||
success(
|
||||
hashMapOf(
|
||||
Settings.System.ACCELEROMETER_ROTATION to accelerometerRotation
|
||||
Settings.System.ACCELEROMETER_ROTATION to accelerometerRotation,
|
||||
Settings.Global.TRANSITION_ANIMATION_SCALE to transitionAnimationScale,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -47,6 +49,12 @@ class SettingsChangeStreamHandler(private val context: Context) : EventChannel.S
|
|||
accelerometerRotation = newAccelerometerRotation
|
||||
changed = true
|
||||
}
|
||||
val newTransitionAnimationScale = Settings.Global.getFloat(context.contentResolver, Settings.Global.TRANSITION_ANIMATION_SCALE)
|
||||
if (transitionAnimationScale != newTransitionAnimationScale) {
|
||||
transitionAnimationScale = newTransitionAnimationScale
|
||||
changed = true
|
||||
}
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get settings", e)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
|
@ -20,7 +19,9 @@ extension ExtraAccessibilityAnimations on AccessibilityAnimations {
|
|||
bool get animate {
|
||||
switch (this) {
|
||||
case AccessibilityAnimations.system:
|
||||
return !window.accessibilityFeatures.disableAnimations;
|
||||
// as of Flutter v2.5.1, the check for `disableAnimations` is unreliable
|
||||
// so we cannot use `window.accessibilityFeatures.disableAnimations` nor `MediaQuery.of(context).disableAnimations`
|
||||
return !settings.areAnimationsRemoved;
|
||||
case AccessibilityAnimations.disabled:
|
||||
return false;
|
||||
case AccessibilityAnimations.enabled:
|
||||
|
|
|
@ -116,11 +116,18 @@ class Settings extends ChangeNotifier {
|
|||
// cf Android `Settings.System.ACCELEROMETER_ROTATION`
|
||||
static const platformAccelerometerRotationKey = 'accelerometer_rotation';
|
||||
|
||||
// cf Android `Settings.Global.TRANSITION_ANIMATION_SCALE`
|
||||
static const platformTransitionAnimationScaleKey = 'transition_animation_scale';
|
||||
|
||||
bool get initialized => _prefs != null;
|
||||
|
||||
Future<void> init({bool isRotationLocked = false}) async {
|
||||
Future<void> init({
|
||||
bool isRotationLocked = false,
|
||||
bool areAnimationsRemoved = false,
|
||||
}) async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
_isRotationLocked = isRotationLocked;
|
||||
_areAnimationsRemoved = areAnimationsRemoved;
|
||||
}
|
||||
|
||||
Future<void> reset({required bool includeInternalKeys}) async {
|
||||
|
@ -447,10 +454,11 @@ class Settings extends ChangeNotifier {
|
|||
// platform settings
|
||||
|
||||
void _onPlatformSettingsChange(Map? fields) {
|
||||
var changed = false;
|
||||
fields?.forEach((key, value) {
|
||||
switch (key) {
|
||||
case platformAccelerometerRotationKey:
|
||||
if (value is int) {
|
||||
if (value is num) {
|
||||
final newValue = value == 0;
|
||||
if (_isRotationLocked != newValue) {
|
||||
_isRotationLocked = newValue;
|
||||
|
@ -458,18 +466,34 @@ class Settings extends ChangeNotifier {
|
|||
windowService.requestOrientation();
|
||||
}
|
||||
_updateStreamController.add(key);
|
||||
notifyListeners();
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case platformTransitionAnimationScaleKey:
|
||||
if (value is num) {
|
||||
final newValue = value == 0;
|
||||
if (_areAnimationsRemoved != newValue) {
|
||||
_areAnimationsRemoved = newValue;
|
||||
_updateStreamController.add(key);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
if (changed) {
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
bool _isRotationLocked = false;
|
||||
|
||||
bool get isRotationLocked => _isRotationLocked;
|
||||
|
||||
bool _areAnimationsRemoved = false;
|
||||
|
||||
bool get areAnimationsRemoved => _areAnimationsRemoved;
|
||||
|
||||
// import/export
|
||||
|
||||
String toJson() => jsonEncode(Map.fromEntries(
|
||||
|
|
|
@ -4,6 +4,16 @@ import 'package:flutter/services.dart';
|
|||
class AccessibilityService {
|
||||
static const platform = MethodChannel('deckers.thibault/aves/accessibility');
|
||||
|
||||
static Future<bool> areAnimationsRemoved() async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('areAnimationsRemoved');
|
||||
if (result != null) return result as bool;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<bool> hasRecommendedTimeouts() async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('hasRecommendedTimeouts');
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:aves/model/settings/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/enums.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -72,7 +71,7 @@ class Durations {
|
|||
static const lastVersionCheckInterval = Duration(days: 7);
|
||||
}
|
||||
|
||||
class DurationsProvider extends StatefulWidget {
|
||||
class DurationsProvider extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const DurationsProvider({
|
||||
|
@ -80,30 +79,6 @@ class DurationsProvider extends StatefulWidget {
|
|||
required this.child,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_DurationsProviderState createState() => _DurationsProviderState();
|
||||
}
|
||||
|
||||
class _DurationsProviderState extends State<DurationsProvider> with WidgetsBindingObserver {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance!.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance!.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAccessibilityFeatures() {
|
||||
if (settings.accessibilityAnimations == AccessibilityAnimations.system) {
|
||||
// TODO TLAD update provider
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ProxyProvider<Settings, DurationsData>(
|
||||
|
@ -111,7 +86,7 @@ class _DurationsProviderState extends State<DurationsProvider> with WidgetsBindi
|
|||
final enabled = settings.accessibilityAnimations.animate;
|
||||
return enabled ? DurationsData() : DurationsData.noAnimation();
|
||||
},
|
||||
child: widget.child,
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/model/settings/screen_on.dart';
|
|||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_source.dart';
|
||||
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/durations.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
|
@ -144,6 +145,7 @@ class _AvesAppState extends State<AvesApp> {
|
|||
Future<void> _setup() async {
|
||||
await settings.init(
|
||||
isRotationLocked: await windowService.isRotationLocked(),
|
||||
areAnimationsRemoved: await AccessibilityService.areAnimationsRemoved(),
|
||||
);
|
||||
|
||||
// keep screen on
|
||||
|
|
Loading…
Reference in a new issue