ML-24 Implement caffeine feature (#27)

* platform-specific code

* implemented caffeine flutter side

* haptics revision
This commit is contained in:
Vadim 2023-02-11 15:58:47 +03:00 committed by GitHub
parent a183a5433e
commit 07fd61fa1e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 163 additions and 19 deletions

View file

@ -1,6 +1,30 @@
package com.vodemn.lightmeter package com.vodemn.lightmeter
import android.view.WindowManager
import io.flutter.embedding.android.FlutterActivity 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()
}
}
}
} }

View file

@ -7,6 +7,26 @@ import Flutter
_ application: UIApplication, _ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool { ) -> 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) GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions) return super.application(application, didFinishLaunchingWithOptions: launchOptions)
} }

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:light_sensor/light_sensor.dart'; import 'package:light_sensor/light_sensor.dart';
import 'package:lightmeter/data/caffeine_service.dart';
import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/haptics_service.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
@ -37,6 +38,7 @@ class Application extends StatelessWidget {
providers: [ providers: [
Provider.value(value: env.copyWith(hasLightSensor: snapshot.data![1] as bool)), Provider.value(value: env.copyWith(hasLightSensor: snapshot.data![1] as bool)),
Provider(create: (_) => UserPreferencesService(snapshot.data![0] as SharedPreferences)), Provider(create: (_) => UserPreferencesService(snapshot.data![0] as SharedPreferences)),
Provider(create: (_) => const CaffeineService()),
Provider(create: (_) => const HapticsService()), Provider(create: (_) => const HapticsService()),
Provider(create: (_) => PermissionsService()), Provider(create: (_) => PermissionsService()),
Provider(create: (_) => const LightSensorService()), Provider(create: (_) => const LightSensorService()),

View file

@ -0,0 +1,15 @@
import 'package:flutter/services.dart';
class CaffeineService {
static const _methodChannel = MethodChannel("com.vodemn.lightmeter/keepScreenOn");
const CaffeineService();
Future<bool> isKeepScreenOn() async {
return await _methodChannel.invokeMethod<bool>("isKeepScreenOn").then((value) => value!);
}
Future<void> keepScreenOn(bool keep) async {
await _methodChannel.invokeMethod<bool>("setKeepScreenOn", keep);
}
}

View file

@ -14,7 +14,9 @@ class UserPreferencesService {
static const _cameraEvCalibrationKey = "cameraEvCalibration"; static const _cameraEvCalibrationKey = "cameraEvCalibration";
static const _lightSensorEvCalibrationKey = "lightSensorEvCalibration"; static const _lightSensorEvCalibrationKey = "lightSensorEvCalibration";
static const _caffeineKey = "caffeine";
static const _hapticsKey = "haptics"; static const _hapticsKey = "haptics";
static const _themeTypeKey = "themeType"; static const _themeTypeKey = "themeType";
static const _primaryColorKey = "primaryColor"; static const _primaryColorKey = "primaryColor";
static const _dynamicColorKey = "dynamicColor"; static const _dynamicColorKey = "dynamicColor";
@ -32,7 +34,10 @@ class UserPreferencesService {
EvSourceType get evSourceType => EvSourceType.values[_sharedPreferences.getInt(_evSourceTypeKey) ?? 0]; EvSourceType get evSourceType => EvSourceType.values[_sharedPreferences.getInt(_evSourceTypeKey) ?? 0];
set evSourceType(EvSourceType value) => _sharedPreferences.setInt(_evSourceTypeKey, value.index); 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); set haptics(bool value) => _sharedPreferences.setBool(_hapticsKey, value);
double get cameraEvCalibration => _sharedPreferences.getDouble(_cameraEvCalibrationKey) ?? 0.0; double get cameraEvCalibration => _sharedPreferences.getDouble(_cameraEvCalibrationKey) ?? 0.0;

View file

@ -1,6 +1,7 @@
import 'dart:io'; import 'dart:io';
import 'package:app_settings/app_settings.dart'; 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/haptics_service.dart';
import 'package:lightmeter/data/light_sensor_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart';
import 'package:lightmeter/data/permissions_service.dart'; import 'package:lightmeter/data/permissions_service.dart';
@ -9,16 +10,22 @@ import 'package:permission_handler/permission_handler.dart';
class MeteringInteractor { class MeteringInteractor {
final UserPreferencesService _userPreferencesService; final UserPreferencesService _userPreferencesService;
final CaffeineService _caffeineService;
final HapticsService _hapticsService; final HapticsService _hapticsService;
final PermissionsService _permissionsService; final PermissionsService _permissionsService;
final LightSensorService _lightSensorService; final LightSensorService _lightSensorService;
const MeteringInteractor( MeteringInteractor(
this._userPreferencesService, this._userPreferencesService,
this._caffeineService,
this._hapticsService, this._hapticsService,
this._permissionsService, this._permissionsService,
this._lightSensorService, this._lightSensorService,
); ) {
if (_userPreferencesService.caffeine) {
_caffeineService.keepScreenOn(true);
}
}
double get cameraEvCalibration => _userPreferencesService.cameraEvCalibration; double get cameraEvCalibration => _userPreferencesService.cameraEvCalibration;
double get lightSensorEvCalibration => _userPreferencesService.lightSensorEvCalibration; double get lightSensorEvCalibration => _userPreferencesService.lightSensorEvCalibration;

View file

@ -1,12 +1,15 @@
import 'package:lightmeter/data/caffeine_service.dart';
import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/haptics_service.dart';
import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart';
class SettingsInteractor { class SettingsInteractor {
final UserPreferencesService _userPreferencesService; final UserPreferencesService _userPreferencesService;
final CaffeineService _caffeineService;
final HapticsService _hapticsService; final HapticsService _hapticsService;
const SettingsInteractor( const SettingsInteractor(
this._userPreferencesService, this._userPreferencesService,
this._caffeineService,
this._hapticsService, this._hapticsService,
); );
@ -14,9 +17,21 @@ class SettingsInteractor {
void setCameraEvCalibration(double value) => _userPreferencesService.cameraEvCalibration = value; void setCameraEvCalibration(double value) => _userPreferencesService.cameraEvCalibration = value;
double get lightSensorEvCalibration => _userPreferencesService.lightSensorEvCalibration; 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<void> enableCaffeine(bool enable) async {
await _caffeineService.keepScreenOn(enable).then((value) {
_userPreferencesService.caffeine = enable;
});
}
bool get isHapticsEnabled => _userPreferencesService.haptics; bool get isHapticsEnabled => _userPreferencesService.haptics;
void enableHaptics(bool enable) {
_userPreferencesService.haptics = enable;
quickVibration();
}
/// Executes vibration if haptics are enabled in settings /// Executes vibration if haptics are enabled in settings
void quickVibration() { void quickVibration() {
@ -27,6 +42,4 @@ class SettingsInteractor {
void responseVibration() { void responseVibration() {
if (_userPreferencesService.haptics) _hapticsService.responseVibration(); if (_userPreferencesService.haptics) _hapticsService.responseVibration();
} }
void enableHaptics(bool enable) => _userPreferencesService.haptics = enable;
} }

View file

@ -34,6 +34,7 @@
"camera": "Camera", "camera": "Camera",
"lightSensor": "Light sensor", "lightSensor": "Light sensor",
"general": "General", "general": "General",
"keepScreenOn": "Keep screen on",
"haptics": "Haptics", "haptics": "Haptics",
"theme": "Theme", "theme": "Theme",
"chooseTheme": "Choose theme", "chooseTheme": "Choose theme",

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.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/haptics_service.dart';
import 'package:lightmeter/data/light_sensor_service.dart'; import 'package:lightmeter/data/light_sensor_service.dart';
import 'package:lightmeter/data/models/photography_values/photography_value.dart'; import 'package:lightmeter/data/models/photography_values/photography_value.dart';
@ -25,6 +26,7 @@ class _MeteringFlowState extends State<MeteringFlow> {
return Provider( return Provider(
create: (context) => MeteringInteractor( create: (context) => MeteringInteractor(
context.read<UserPreferencesService>(), context.read<UserPreferencesService>(),
context.read<CaffeineService>(),
context.read<HapticsService>(), context.read<HapticsService>(),
context.read<PermissionsService>(), context.read<PermissionsService>(),
context.read<LightSensorService>(), context.read<LightSensorService>(),

View file

@ -0,0 +1,15 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:lightmeter/interactors/settings_interactor.dart';
class CaffeineListTileBloc extends Cubit<bool> {
final SettingsInteractor _settingsInteractor;
CaffeineListTileBloc(
this._settingsInteractor,
) : super(_settingsInteractor.isCaffeineEnabled);
void onCaffeineChanged(bool value) {
_settingsInteractor.enableCaffeine(value);
emit(value);
}
}

View file

@ -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<SettingsInteractor>()),
child: const CaffeineListTile(),
);
}
}

View file

@ -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<CaffeineListTileBloc, bool>(
builder: (context, state) => SwitchListTile(
secondary: const Icon(Icons.screen_lock_portrait),
title: Text(S.of(context).keepScreenOn),
value: state,
onChanged: context.read<CaffeineListTileBloc>().onCaffeineChanged,
),
);
}
}

View file

@ -8,11 +8,8 @@ class HapticsListTileBloc extends Cubit<bool> {
this._settingsInteractor, this._settingsInteractor,
) : super(_settingsInteractor.isHapticsEnabled); ) : super(_settingsInteractor.isHapticsEnabled);
void onHapticsChange(bool value) { void onHapticsChanged(bool value) {
_settingsInteractor.enableHaptics(value); _settingsInteractor.enableHaptics(value);
if (value) {
_settingsInteractor.quickVibration();
}
emit(value); emit(value);
} }
} }

View file

@ -14,7 +14,7 @@ class HapticsListTile extends StatelessWidget {
secondary: const Icon(Icons.vibration), secondary: const Icon(Icons.vibration),
title: Text(S.of(context).haptics), title: Text(S.of(context).haptics),
value: state, value: state,
onChanged: context.read<HapticsListTileBloc>().onHapticsChange, onChanged: context.read<HapticsListTileBloc>().onHapticsChanged,
), ),
); );
} }

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lightmeter/data/caffeine_service.dart';
import 'package:lightmeter/data/haptics_service.dart'; import 'package:lightmeter/data/haptics_service.dart';
import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart';
import 'package:lightmeter/interactors/settings_interactor.dart'; import 'package:lightmeter/interactors/settings_interactor.dart';
@ -13,6 +14,7 @@ class SettingsFlow extends StatelessWidget {
return Provider( return Provider(
create: (context) => SettingsInteractor( create: (context) => SettingsInteractor(
context.read<UserPreferencesService>(), context.read<UserPreferencesService>(),
context.read<CaffeineService>(),
context.read<HapticsService>(), context.read<HapticsService>(),
), ),
child: const SettingsScreen(), child: const SettingsScreen(),

View file

@ -4,6 +4,7 @@ import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/res/dimens.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'components/caffeine/provider_list_tile_caffeine.dart';
import 'components/calibration/widget_list_tile_calibration.dart'; import 'components/calibration/widget_list_tile_calibration.dart';
import 'components/haptics/provider_list_tile_haptics.dart'; import 'components/haptics/provider_list_tile_haptics.dart';
import 'components/primary_color/widget_list_tile_primary_color.dart'; import 'components/primary_color/widget_list_tile_primary_color.dart';
@ -61,6 +62,7 @@ class SettingsScreen extends StatelessWidget {
SettingsSection( SettingsSection(
title: S.of(context).general, title: S.of(context).general,
children: const [ children: const [
CaffeineListTileProvider(),
HapticsListTileProvider(), HapticsListTileProvider(),
], ],
), ),