From 07fd61fa1e1d744199aea648aea559b5a358f6db Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sat, 11 Feb 2023 15:58:47 +0300 Subject: [PATCH] ML-24 Implement caffeine feature (#27) * platform-specific code * implemented caffeine flutter side * haptics revision --- .../com/vodemn/lightmeter/MainActivity.kt | 26 +++++++++++++- ios/Runner/AppDelegate.swift | 34 +++++++++++++++---- lib/application.dart | 2 ++ lib/data/caffeine_service.dart | 15 ++++++++ lib/data/shared_prefs_service.dart | 7 +++- lib/interactors/metering_interactor.dart | 11 ++++-- lib/interactors/settings_interactor.dart | 19 +++++++++-- lib/l10n/intl_en.arb | 1 + lib/screens/metering/flow_metering.dart | 2 ++ .../caffeine/bloc_list_tile_caffeine.dart | 15 ++++++++ .../caffeine/provider_list_tile_caffeine.dart | 18 ++++++++++ .../caffeine/widget_list_tile_caffeine.dart | 21 ++++++++++++ .../haptics/bloc_list_tile_haptics.dart | 5 +-- .../haptics/widget_list_tile_haptics.dart | 2 +- lib/screens/settings/flow_settings.dart | 2 ++ lib/screens/settings/screen_settings.dart | 2 ++ 16 files changed, 163 insertions(+), 19 deletions(-) create mode 100644 lib/data/caffeine_service.dart create mode 100644 lib/screens/settings/components/caffeine/bloc_list_tile_caffeine.dart create mode 100644 lib/screens/settings/components/caffeine/provider_list_tile_caffeine.dart create mode 100644 lib/screens/settings/components/caffeine/widget_list_tile_caffeine.dart diff --git a/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt b/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt index a234f56..61af74c 100644 --- a/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt +++ b/android/app/src/main/kotlin/com/vodemn/lightmeter/MainActivity.kt @@ -1,6 +1,30 @@ package com.vodemn.lightmeter +import android.view.WindowManager import io.flutter.embedding.android.FlutterActivity +import io.flutter.embedding.engine.FlutterEngine +import io.flutter.plugin.common.MethodChannel -class MainActivity: FlutterActivity() { +class MainActivity : FlutterActivity() { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { + super.configureFlutterEngine(flutterEngine) + MethodChannel( + flutterEngine.dartExecutor.binaryMessenger, + "com.vodemn.lightmeter/keepScreenOn" + ).setMethodCallHandler { call, result -> + when (call.method) { + "isKeepScreenOn" -> result.success((window.attributes.flags and WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) != 0) + "setKeepScreenOn" -> { + if (call.arguments !is Boolean) { + result.error("invalid args", "Argument should be of type Bool for 'setKeepScreenOn' call", null) + } else { + if (call.arguments as Boolean) window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + result.success(true) + } + } + else -> result.notImplemented() + } + } + } } diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4..5dc5452 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -3,11 +3,31 @@ import Flutter @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + let controller : FlutterViewController = window?.rootViewController as! FlutterViewController + let keepScreenOnChannel = FlutterMethodChannel(name: "com.vodemn.lightmeter/keepScreenOn", + binaryMessenger: controller.binaryMessenger) + keepScreenOnChannel.setMethodCallHandler({ + (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + switch call.method { + case "isKeepScreenOn": + result(UIApplication.shared.isIdleTimerDisabled) + case "setKeepScreenOn": + guard let keepOn = call.arguments as? Bool else { + result(FlutterError(code: "invalid arguments", message: "Argument should be of type Bool for 'setKeepScreenOn' call", details: nil)) + return + } + UIApplication.shared.isIdleTimerDisabled = keepOn + result(true) + default: + result(FlutterMethodNotImplemented) + } + }) + + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } } diff --git a/lib/application.dart b/lib/application.dart index 6caa8f3..729b3db 100644 --- a/lib/application.dart +++ b/lib/application.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:light_sensor/light_sensor.dart'; +import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -37,6 +38,7 @@ class Application extends StatelessWidget { providers: [ Provider.value(value: env.copyWith(hasLightSensor: snapshot.data![1] as bool)), Provider(create: (_) => UserPreferencesService(snapshot.data![0] as SharedPreferences)), + Provider(create: (_) => const CaffeineService()), Provider(create: (_) => const HapticsService()), Provider(create: (_) => PermissionsService()), Provider(create: (_) => const LightSensorService()), diff --git a/lib/data/caffeine_service.dart b/lib/data/caffeine_service.dart new file mode 100644 index 0000000..5d19b0f --- /dev/null +++ b/lib/data/caffeine_service.dart @@ -0,0 +1,15 @@ +import 'package:flutter/services.dart'; + +class CaffeineService { + static const _methodChannel = MethodChannel("com.vodemn.lightmeter/keepScreenOn"); + + const CaffeineService(); + + Future isKeepScreenOn() async { + return await _methodChannel.invokeMethod("isKeepScreenOn").then((value) => value!); + } + + Future keepScreenOn(bool keep) async { + await _methodChannel.invokeMethod("setKeepScreenOn", keep); + } +} diff --git a/lib/data/shared_prefs_service.dart b/lib/data/shared_prefs_service.dart index 6d39a13..d9861e7 100644 --- a/lib/data/shared_prefs_service.dart +++ b/lib/data/shared_prefs_service.dart @@ -14,7 +14,9 @@ class UserPreferencesService { static const _cameraEvCalibrationKey = "cameraEvCalibration"; static const _lightSensorEvCalibrationKey = "lightSensorEvCalibration"; + static const _caffeineKey = "caffeine"; static const _hapticsKey = "haptics"; + static const _themeTypeKey = "themeType"; static const _primaryColorKey = "primaryColor"; static const _dynamicColorKey = "dynamicColor"; @@ -32,7 +34,10 @@ class UserPreferencesService { EvSourceType get evSourceType => EvSourceType.values[_sharedPreferences.getInt(_evSourceTypeKey) ?? 0]; set evSourceType(EvSourceType value) => _sharedPreferences.setInt(_evSourceTypeKey, value.index); - bool get haptics => _sharedPreferences.getBool(_hapticsKey) ?? false; + bool get caffeine => _sharedPreferences.getBool(_caffeineKey) ?? false; + set caffeine(bool value) => _sharedPreferences.setBool(_caffeineKey, value); + + bool get haptics => _sharedPreferences.getBool(_hapticsKey) ?? true; set haptics(bool value) => _sharedPreferences.setBool(_hapticsKey, value); double get cameraEvCalibration => _sharedPreferences.getDouble(_cameraEvCalibrationKey) ?? 0.0; diff --git a/lib/interactors/metering_interactor.dart b/lib/interactors/metering_interactor.dart index bbac538..1948b97 100644 --- a/lib/interactors/metering_interactor.dart +++ b/lib/interactors/metering_interactor.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:app_settings/app_settings.dart'; +import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart'; import 'package:lightmeter/data/permissions_service.dart'; @@ -9,16 +10,22 @@ import 'package:permission_handler/permission_handler.dart'; class MeteringInteractor { final UserPreferencesService _userPreferencesService; + final CaffeineService _caffeineService; final HapticsService _hapticsService; final PermissionsService _permissionsService; final LightSensorService _lightSensorService; - const MeteringInteractor( + MeteringInteractor( this._userPreferencesService, + this._caffeineService, this._hapticsService, this._permissionsService, this._lightSensorService, - ); + ) { + if (_userPreferencesService.caffeine) { + _caffeineService.keepScreenOn(true); + } + } double get cameraEvCalibration => _userPreferencesService.cameraEvCalibration; double get lightSensorEvCalibration => _userPreferencesService.lightSensorEvCalibration; diff --git a/lib/interactors/settings_interactor.dart b/lib/interactors/settings_interactor.dart index d19edfe..db99a7a 100644 --- a/lib/interactors/settings_interactor.dart +++ b/lib/interactors/settings_interactor.dart @@ -1,12 +1,15 @@ +import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; class SettingsInteractor { final UserPreferencesService _userPreferencesService; + final CaffeineService _caffeineService; final HapticsService _hapticsService; const SettingsInteractor( this._userPreferencesService, + this._caffeineService, this._hapticsService, ); @@ -14,9 +17,21 @@ class SettingsInteractor { void setCameraEvCalibration(double value) => _userPreferencesService.cameraEvCalibration = value; double get lightSensorEvCalibration => _userPreferencesService.lightSensorEvCalibration; - void setLightSensorEvCalibration(double value) => _userPreferencesService.lightSensorEvCalibration = value; + void setLightSensorEvCalibration(double value) => + _userPreferencesService.lightSensorEvCalibration = value; + + bool get isCaffeineEnabled => _userPreferencesService.caffeine; + Future enableCaffeine(bool enable) async { + await _caffeineService.keepScreenOn(enable).then((value) { + _userPreferencesService.caffeine = enable; + }); + } bool get isHapticsEnabled => _userPreferencesService.haptics; + void enableHaptics(bool enable) { + _userPreferencesService.haptics = enable; + quickVibration(); + } /// Executes vibration if haptics are enabled in settings void quickVibration() { @@ -27,6 +42,4 @@ class SettingsInteractor { void responseVibration() { if (_userPreferencesService.haptics) _hapticsService.responseVibration(); } - - void enableHaptics(bool enable) => _userPreferencesService.haptics = enable; } diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index c8ae2cb..6d5dffc 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -34,6 +34,7 @@ "camera": "Camera", "lightSensor": "Light sensor", "general": "General", + "keepScreenOn": "Keep screen on", "haptics": "Haptics", "theme": "Theme", "chooseTheme": "Choose theme", diff --git a/lib/screens/metering/flow_metering.dart b/lib/screens/metering/flow_metering.dart index 162800f..7dbb05c 100644 --- a/lib/screens/metering/flow_metering.dart +++ b/lib/screens/metering/flow_metering.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart'; @@ -25,6 +26,7 @@ class _MeteringFlowState extends State { return Provider( create: (context) => MeteringInteractor( context.read(), + context.read(), context.read(), context.read(), context.read(), diff --git a/lib/screens/settings/components/caffeine/bloc_list_tile_caffeine.dart b/lib/screens/settings/components/caffeine/bloc_list_tile_caffeine.dart new file mode 100644 index 0000000..3a48e8d --- /dev/null +++ b/lib/screens/settings/components/caffeine/bloc_list_tile_caffeine.dart @@ -0,0 +1,15 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/interactors/settings_interactor.dart'; + +class CaffeineListTileBloc extends Cubit { + final SettingsInteractor _settingsInteractor; + + CaffeineListTileBloc( + this._settingsInteractor, + ) : super(_settingsInteractor.isCaffeineEnabled); + + void onCaffeineChanged(bool value) { + _settingsInteractor.enableCaffeine(value); + emit(value); + } +} diff --git a/lib/screens/settings/components/caffeine/provider_list_tile_caffeine.dart b/lib/screens/settings/components/caffeine/provider_list_tile_caffeine.dart new file mode 100644 index 0000000..479b1e7 --- /dev/null +++ b/lib/screens/settings/components/caffeine/provider_list_tile_caffeine.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/interactors/settings_interactor.dart'; + +import 'bloc_list_tile_caffeine.dart'; +import 'widget_list_tile_caffeine.dart'; + +class CaffeineListTileProvider extends StatelessWidget { + const CaffeineListTileProvider({super.key}); + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (context) => CaffeineListTileBloc(context.read()), + child: const CaffeineListTile(), + ); + } +} diff --git a/lib/screens/settings/components/caffeine/widget_list_tile_caffeine.dart b/lib/screens/settings/components/caffeine/widget_list_tile_caffeine.dart new file mode 100644 index 0000000..dfd7bbe --- /dev/null +++ b/lib/screens/settings/components/caffeine/widget_list_tile_caffeine.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:lightmeter/generated/l10n.dart'; + +import 'bloc_list_tile_caffeine.dart'; + +class CaffeineListTile extends StatelessWidget { + const CaffeineListTile({super.key}); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (context, state) => SwitchListTile( + secondary: const Icon(Icons.screen_lock_portrait), + title: Text(S.of(context).keepScreenOn), + value: state, + onChanged: context.read().onCaffeineChanged, + ), + ); + } +} diff --git a/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart b/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart index 11058da..df34562 100644 --- a/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart +++ b/lib/screens/settings/components/haptics/bloc_list_tile_haptics.dart @@ -8,11 +8,8 @@ class HapticsListTileBloc extends Cubit { this._settingsInteractor, ) : super(_settingsInteractor.isHapticsEnabled); - void onHapticsChange(bool value) { + void onHapticsChanged(bool value) { _settingsInteractor.enableHaptics(value); - if (value) { - _settingsInteractor.quickVibration(); - } emit(value); } } diff --git a/lib/screens/settings/components/haptics/widget_list_tile_haptics.dart b/lib/screens/settings/components/haptics/widget_list_tile_haptics.dart index d640106..673fa2c 100644 --- a/lib/screens/settings/components/haptics/widget_list_tile_haptics.dart +++ b/lib/screens/settings/components/haptics/widget_list_tile_haptics.dart @@ -14,7 +14,7 @@ class HapticsListTile extends StatelessWidget { secondary: const Icon(Icons.vibration), title: Text(S.of(context).haptics), value: state, - onChanged: context.read().onHapticsChange, + onChanged: context.read().onHapticsChanged, ), ); } diff --git a/lib/screens/settings/flow_settings.dart b/lib/screens/settings/flow_settings.dart index 9dcb803..9e757b4 100644 --- a/lib/screens/settings/flow_settings.dart +++ b/lib/screens/settings/flow_settings.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:lightmeter/data/caffeine_service.dart'; import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/interactors/settings_interactor.dart'; @@ -13,6 +14,7 @@ class SettingsFlow extends StatelessWidget { return Provider( create: (context) => SettingsInteractor( context.read(), + context.read(), context.read(), ), child: const SettingsScreen(), diff --git a/lib/screens/settings/screen_settings.dart b/lib/screens/settings/screen_settings.dart index 32714a5..02af05e 100644 --- a/lib/screens/settings/screen_settings.dart +++ b/lib/screens/settings/screen_settings.dart @@ -4,6 +4,7 @@ import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/res/dimens.dart'; import 'package:provider/provider.dart'; +import 'components/caffeine/provider_list_tile_caffeine.dart'; import 'components/calibration/widget_list_tile_calibration.dart'; import 'components/haptics/provider_list_tile_haptics.dart'; import 'components/primary_color/widget_list_tile_primary_color.dart'; @@ -61,6 +62,7 @@ class SettingsScreen extends StatelessWidget { SettingsSection( title: S.of(context).general, children: const [ + CaffeineListTileProvider(), HapticsListTileProvider(), ], ),