diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a09a0acc..bd6b245cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Accessibility: apply bold font system setting + ## [v1.7.4] - 2022-11-11 ### Added diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AccessibilityHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AccessibilityHandler.kt index eaf3e0bfa..d44a5ed29 100644 --- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AccessibilityHandler.kt +++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AccessibilityHandler.kt @@ -3,6 +3,7 @@ package deckers.thibault.aves.channel.calls import android.annotation.SuppressLint import android.content.Context import android.content.ContextWrapper +import android.content.res.Configuration import android.os.Build import android.provider.Settings import android.util.Log @@ -19,6 +20,7 @@ class AccessibilityHandler(private val contextWrapper: ContextWrapper) : MethodC "areAnimationsRemoved" -> safe(call, result, ::areAnimationsRemoved) "hasRecommendedTimeouts" -> safe(call, result, ::hasRecommendedTimeouts) "getRecommendedTimeoutMillis" -> safe(call, result, ::getRecommendedTimeoutMillis) + "shouldUseBoldFont" -> safe(call, result, ::shouldUseBoldFont) else -> result.notImplemented() } } @@ -76,8 +78,28 @@ class AccessibilityHandler(private val contextWrapper: ContextWrapper) : MethodC result.success(millis) } + // Flutter v3.4 already checks the system `Configuration.fontWeightAdjustment` to update `MediaQuery` + // but we need to also check the non-standard Samsung field `bf` representing the bold font toggle + private fun shouldUseBoldFont(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { + var shouldBold = false + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val config = contextWrapper.resources.configuration + val fontWeightAdjustment = config.fontWeightAdjustment + shouldBold = if (fontWeightAdjustment != Configuration.FONT_WEIGHT_ADJUSTMENT_UNDEFINED && fontWeightAdjustment != 0) { + fontWeightAdjustment >= BOLD_TEXT_WEIGHT_ADJUSTMENT + } else { + // fallback to Samsung non-standard field + Regex(" bf=([01]) ").find(config.toString())?.groups?.get(1)?.value == "1" + } + } + result.success(shouldBold) + } + companion object { private val LOG_TAG = LogUtils.createTag() const val CHANNEL = "deckers.thibault/aves/accessibility" + + // match Flutter way: https://github.com/flutter/engine/blob/main/shell/platform/android/io/flutter/view/AccessibilityBridge.java#L125 + const val BOLD_TEXT_WEIGHT_ADJUSTMENT = 300 } } \ No newline at end of file diff --git a/lib/services/accessibility_service.dart b/lib/services/accessibility_service.dart index eb442ee7d..61ac1bb34 100644 --- a/lib/services/accessibility_service.dart +++ b/lib/services/accessibility_service.dart @@ -4,6 +4,16 @@ import 'package:flutter/services.dart'; class AccessibilityService { static const _platform = MethodChannel('deckers.thibault/aves/accessibility'); + static Future shouldUseBoldFont() async { + try { + final result = await _platform.invokeMethod('shouldUseBoldFont'); + if (result != null) return result as bool; + } on PlatformException catch (e, stack) { + await reportService.recordError(e, stack); + } + return false; + } + static Future areAnimationsRemoved() async { try { final result = await _platform.invokeMethod('areAnimationsRemoved'); diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 915dc4160..811cebf8d 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -108,6 +108,7 @@ class AvesApp extends StatefulWidget { class _AvesAppState extends State with WidgetsBindingObserver { final ValueNotifier appModeNotifier = ValueNotifier(AppMode.main); late final Future _appSetup; + late final Future _shouldUseBoldFontLoader; late final Future _dynamicColorPaletteLoader; final CollectionSource _mediaStoreSource = MediaStoreSource(); final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: Durations.mediaContentChangeDebounceDelay); @@ -129,6 +130,7 @@ class _AvesAppState extends State with WidgetsBindingObserver { _appSetup = _setup(); // remember screen size to use it later, when `context` and `window` are no longer reliable _screenSize = _getScreenSize(); + _shouldUseBoldFontLoader = AccessibilityService.shouldUseBoldFont(); _dynamicColorPaletteLoader = DynamicColorPlugin.getCorePalette(); _mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?)); _newIntentChannel.receiveBroadcastStream().listen((event) => _onNewIntent(event as Map?)); @@ -205,32 +207,43 @@ class _AvesAppState extends State with WidgetsBindingObserver { lightAccent = Color(tonalPalette?.get(60) ?? defaultAccent.value); darkAccent = Color(tonalPalette?.get(70) ?? defaultAccent.value); } - return MaterialApp( - navigatorKey: AvesApp.navigatorKey, - home: home, - navigatorObservers: _navigatorObservers, - builder: (context, child) { - if (initialized) { - WidgetsBinding.instance.addPostFrameCallback((_) => AvesApp.setSystemUIStyle(context)); - } - return AvesColorsProvider( - child: Theme( - data: Theme.of(context).copyWith( - pageTransitionsTheme: pageTransitionsTheme, - ), - child: child!, - ), + return FutureBuilder( + future: _shouldUseBoldFontLoader, + builder: (context, snapshot) { + // Flutter v3.4 already checks the system `Configuration.fontWeightAdjustment` to update `MediaQuery` + // but we need to also check the non-standard Samsung field `bf` representing the bold font toggle + final shouldUseBoldFont = snapshot.data ?? false; + return MaterialApp( + navigatorKey: AvesApp.navigatorKey, + home: home, + navigatorObservers: _navigatorObservers, + builder: (context, child) { + if (initialized) { + WidgetsBinding.instance.addPostFrameCallback((_) => AvesApp.setSystemUIStyle(context)); + } + return MediaQuery( + data: MediaQuery.of(context).copyWith(boldText: shouldUseBoldFont), + child: AvesColorsProvider( + child: Theme( + data: Theme.of(context).copyWith( + pageTransitionsTheme: pageTransitionsTheme, + ), + child: child!, + ), + ), + ); + }, + onGenerateTitle: (context) => context.l10n.appName, + theme: Themes.lightTheme(lightAccent, initialized), + darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized), + themeMode: themeBrightness.appThemeMode, + locale: settingsLocale, + localizationsDelegates: AppLocalizations.localizationsDelegates, + supportedLocales: AvesApp.supportedLocales, + // TODO TLAD remove custom scroll behavior when this is fixed: https://github.com/flutter/flutter/issues/82906 + scrollBehavior: StretchMaterialScrollBehavior(), ); }, - onGenerateTitle: (context) => context.l10n.appName, - theme: Themes.lightTheme(lightAccent, initialized), - darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized), - themeMode: themeBrightness.appThemeMode, - locale: settingsLocale, - localizationsDelegates: AppLocalizations.localizationsDelegates, - supportedLocales: AvesApp.supportedLocales, - // TODO TLAD remove custom scroll behavior when this is fixed: https://github.com/flutter/flutter/issues/82906 - scrollBehavior: StretchMaterialScrollBehavior(), ); }, );