mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 15:30:59 +00:00
fixed permission handling
This commit is contained in:
parent
f75afbb6c2
commit
edd7a2f66f
18 changed files with 187 additions and 181 deletions
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
|
@ -8,6 +8,7 @@
|
||||||
"name": "dev (android)",
|
"name": "dev (android)",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "dart",
|
"type": "dart",
|
||||||
|
//"flutterMode": "profile",
|
||||||
"args": [
|
"args": [
|
||||||
"--flavor",
|
"--flavor",
|
||||||
"dev",
|
"dev",
|
||||||
|
|
|
@ -1,17 +1,22 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:app_settings/app_settings.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/shared_prefs_service.dart';
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
|
|
||||||
class MeteringInteractor {
|
class MeteringInteractor {
|
||||||
final UserPreferencesService _userPreferencesService;
|
final UserPreferencesService _userPreferencesService;
|
||||||
final HapticsService _hapticsService;
|
final HapticsService _hapticsService;
|
||||||
|
final PermissionsService _permissionsService;
|
||||||
final LightSensorService _lightSensorService;
|
final LightSensorService _lightSensorService;
|
||||||
|
|
||||||
const MeteringInteractor(
|
const MeteringInteractor(
|
||||||
this._userPreferencesService,
|
this._userPreferencesService,
|
||||||
this._hapticsService,
|
this._hapticsService,
|
||||||
|
this._permissionsService,
|
||||||
this._lightSensorService,
|
this._lightSensorService,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -30,6 +35,22 @@ class MeteringInteractor {
|
||||||
if (_userPreferencesService.haptics) _hapticsService.responseVibration();
|
if (_userPreferencesService.haptics) _hapticsService.responseVibration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> checkCameraPermission() async {
|
||||||
|
return _permissionsService
|
||||||
|
.checkCameraPermission()
|
||||||
|
.then((value) => value == PermissionStatus.granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> requestPermission() async {
|
||||||
|
return _permissionsService
|
||||||
|
.requestCameraPermission()
|
||||||
|
.then((value) => value == PermissionStatus.granted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void openAppSettings() {
|
||||||
|
AppSettings.openAppSettings();
|
||||||
|
}
|
||||||
|
|
||||||
void enableHaptics(bool enable) => _userPreferencesService.haptics = enable;
|
void enableHaptics(bool enable) => _userPreferencesService.haptics = enable;
|
||||||
|
|
||||||
Future<bool> hasAmbientLightSensor() async {
|
Future<bool> hasAmbientLightSensor() async {
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
"nd": "ND",
|
"nd": "ND",
|
||||||
"ndFilterFactor": "Neutral density filter factor",
|
"ndFilterFactor": "Neutral density filter factor",
|
||||||
"noExposurePairs": "There are no exposure pairs for the selected settings.",
|
"noExposurePairs": "There are no exposure pairs for the selected settings.",
|
||||||
|
"noCamerasDetected": "Seems like your device doesn't have any cameras connected",
|
||||||
|
"noCameraPermission": "Camera permission is not granted.",
|
||||||
|
"otherCameraError": "An error occurred when connecting to the camera.",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"select": "Select",
|
"select": "Select",
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:exif/exif.dart';
|
||||||
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/interactors/metering_interactor.dart';
|
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
import 'package:lightmeter/screens/metering/components/shared/ev_source_base/bloc_base_ev_source.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
import 'package:lightmeter/screens/metering/communication/bloc_communication_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
|
import 'package:lightmeter/screens/metering/communication/event_communication_metering.dart'
|
||||||
|
@ -43,12 +44,14 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
|
_observer = _WidgetsBindingObserver(_appLifecycleStateObserver);
|
||||||
WidgetsBinding.instance.addObserver(_observer);
|
WidgetsBinding.instance.addObserver(_observer);
|
||||||
|
|
||||||
|
on<RequestPermissionEvent>(_onRequestPermission);
|
||||||
|
on<OpenAppSettingsEvent>(_onOpenAppSettings);
|
||||||
on<InitializeEvent>(_onInitialize);
|
on<InitializeEvent>(_onInitialize);
|
||||||
on<ZoomChangedEvent>(_onZoomChanged);
|
on<ZoomChangedEvent>(_onZoomChanged);
|
||||||
on<ExposureOffsetChangedEvent>(_onExposureOffsetChanged);
|
on<ExposureOffsetChangedEvent>(_onExposureOffsetChanged);
|
||||||
on<ExposureOffsetResetEvent>(_onExposureOffsetResetEvent);
|
on<ExposureOffsetResetEvent>(_onExposureOffsetResetEvent);
|
||||||
|
|
||||||
add(const InitializeEvent());
|
add(const RequestPermissionEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -71,10 +74,33 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onRequestPermission(_, Emitter emit) async {
|
||||||
|
final hasPermission = await _meteringInteractor.requestPermission();
|
||||||
|
if (!hasPermission) {
|
||||||
|
emit(const CameraErrorState(CameraErrorType.permissionNotGranted));
|
||||||
|
} else {
|
||||||
|
add(const InitializeEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onOpenAppSettings(_, Emitter emit) async {
|
||||||
|
_meteringInteractor.openAppSettings();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _onInitialize(_, Emitter emit) async {
|
Future<void> _onInitialize(_, Emitter emit) async {
|
||||||
emit(const CameraLoadingState());
|
emit(const CameraLoadingState());
|
||||||
|
final hasPermission = await _meteringInteractor.checkCameraPermission();
|
||||||
|
if (!hasPermission) {
|
||||||
|
emit(const CameraErrorState(CameraErrorType.permissionNotGranted));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final cameras = await availableCameras();
|
final cameras = await availableCameras();
|
||||||
|
if (cameras.isEmpty) {
|
||||||
|
emit(const CameraErrorState(CameraErrorType.noCamerasDetected));
|
||||||
|
return;
|
||||||
|
}
|
||||||
_cameraController = CameraController(
|
_cameraController = CameraController(
|
||||||
cameras.firstWhere(
|
cameras.firstWhere(
|
||||||
(camera) => camera.lensDirection == CameraLensDirection.back,
|
(camera) => camera.lensDirection == CameraLensDirection.back,
|
||||||
|
@ -111,7 +137,7 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
|
|
||||||
_emitActiveState(emit);
|
_emitActiveState(emit);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
emit(const CameraErrorState());
|
emit(const CameraErrorState(CameraErrorType.other));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +200,7 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
add(const InitializeEvent());
|
add(const InitializeEvent());
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.paused:
|
case AppLifecycleState.paused:
|
||||||
case AppLifecycleState.inactive:
|
case AppLifecycleState.detached:
|
||||||
_cameraController?.dispose();
|
_cameraController?.dispose();
|
||||||
_cameraController = null;
|
_cameraController = null;
|
||||||
break;
|
break;
|
||||||
|
@ -186,11 +212,16 @@ class CameraContainerBloc extends EvSourceBlocBase<CameraContainerEvent, CameraC
|
||||||
/// This is needed only because we cannot use `with` with mixins
|
/// This is needed only because we cannot use `with` with mixins
|
||||||
class _WidgetsBindingObserver with WidgetsBindingObserver {
|
class _WidgetsBindingObserver with WidgetsBindingObserver {
|
||||||
final ValueChanged<AppLifecycleState> onLifecycleStateChanged;
|
final ValueChanged<AppLifecycleState> onLifecycleStateChanged;
|
||||||
|
AppLifecycleState? _prevState;
|
||||||
|
|
||||||
_WidgetsBindingObserver(this.onLifecycleStateChanged);
|
_WidgetsBindingObserver(this.onLifecycleStateChanged);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
|
if (_prevState == AppLifecycleState.inactive && state == AppLifecycleState.resumed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_prevState = state;
|
||||||
onLifecycleStateChanged(state);
|
onLifecycleStateChanged(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||||
|
|
||||||
|
class CameraControlsPlaceholder extends StatelessWidget {
|
||||||
|
final CameraErrorType error;
|
||||||
|
final VoidCallback onReset;
|
||||||
|
|
||||||
|
const CameraControlsPlaceholder({
|
||||||
|
required this.error,
|
||||||
|
required this.onReset,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: onReset,
|
||||||
|
icon: Icon(error == CameraErrorType.permissionNotGranted ? Icons.settings : Icons.sync),
|
||||||
|
),
|
||||||
|
const SizedBox(height: Dimens.grid8),
|
||||||
|
Text(
|
||||||
|
error.toStringLocalized(context),
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodyMedium
|
||||||
|
?.copyWith(color: Theme.of(context).colorScheme.onBackground),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +1,19 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||||
|
|
||||||
class CameraViewPlaceholder extends StatelessWidget {
|
class CameraViewPlaceholder extends StatelessWidget {
|
||||||
const CameraViewPlaceholder({super.key});
|
final CameraErrorType? error;
|
||||||
|
|
||||||
|
const CameraViewPlaceholder({required this.error, super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Card(
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.borderRadiusM)),
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(Dimens.borderRadiusM)),
|
||||||
child: const Center(child: Icon(Icons.no_photography)),
|
child: Center(
|
||||||
|
child: error != null ? const Icon(Icons.no_photography) : const CircularProgressIndicator(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,10 +2,22 @@ abstract class CameraContainerEvent {
|
||||||
const CameraContainerEvent();
|
const CameraContainerEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class RequestPermissionEvent extends CameraContainerEvent {
|
||||||
|
const RequestPermissionEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
class OpenAppSettingsEvent extends CameraContainerEvent {
|
||||||
|
const OpenAppSettingsEvent();
|
||||||
|
}
|
||||||
|
|
||||||
class InitializeEvent extends CameraContainerEvent {
|
class InitializeEvent extends CameraContainerEvent {
|
||||||
const InitializeEvent();
|
const InitializeEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ReinitializeEvent extends CameraContainerEvent {
|
||||||
|
const ReinitializeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
class ZoomChangedEvent extends CameraContainerEvent {
|
class ZoomChangedEvent extends CameraContainerEvent {
|
||||||
final double value;
|
final double value;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/generated/l10n.dart';
|
||||||
|
|
||||||
|
enum CameraErrorType { noCamerasDetected, permissionNotGranted, other }
|
||||||
|
|
||||||
|
extension CameraErrorTypeString on CameraErrorType {
|
||||||
|
String toStringLocalized(BuildContext context) {
|
||||||
|
switch (this) {
|
||||||
|
case CameraErrorType.noCamerasDetected:
|
||||||
|
return S.of(context).noCamerasDetected;
|
||||||
|
case CameraErrorType.permissionNotGranted:
|
||||||
|
return S.of(context).noCameraPermission;
|
||||||
|
default:
|
||||||
|
return S.of(context).otherCameraError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ class CameraContainerProvider extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return BlocProvider(
|
return BlocProvider(
|
||||||
|
lazy: false,
|
||||||
create: (context) => CameraContainerBloc(
|
create: (context) => CameraContainerBloc(
|
||||||
context.read<MeteringInteractor>(),
|
context.read<MeteringInteractor>(),
|
||||||
context.read<MeteringCommunicationBloc>(),
|
context.read<MeteringCommunicationBloc>(),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||||
|
|
||||||
abstract class CameraContainerState {
|
abstract class CameraContainerState {
|
||||||
const CameraContainerState();
|
const CameraContainerState();
|
||||||
|
@ -36,5 +37,7 @@ class CameraActiveState extends CameraContainerState {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CameraErrorState extends CameraContainerState {
|
class CameraErrorState extends CameraContainerState {
|
||||||
const CameraErrorState();
|
final CameraErrorType error;
|
||||||
|
|
||||||
|
const CameraErrorState(this.error);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@ import 'package:lightmeter/data/models/photography_values/nd_value.dart';
|
||||||
import 'package:lightmeter/platform_config.dart';
|
import 'package:lightmeter/platform_config.dart';
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
import 'package:lightmeter/res/dimens.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart';
|
import 'package:lightmeter/screens/metering/components/camera_container/components/camera_view/widget_camera_view.dart';
|
||||||
|
import 'package:lightmeter/screens/metering/components/camera_container/models/camera_error_type.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
import 'package:lightmeter/screens/metering/components/shared/metering_top_bar/widget_top_bar_metering.dart';
|
||||||
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
|
import 'package:lightmeter/screens/metering/components/shared/readings_container/widget_container_readings.dart';
|
||||||
|
|
||||||
import 'bloc_container_camera.dart';
|
import 'bloc_container_camera.dart';
|
||||||
import 'components/camera_controls/widget_camera_controls.dart';
|
import 'components/camera_controls/widget_camera_controls.dart';
|
||||||
|
import 'components/camera_controls_placeholder/widget_placeholder_camera_controls.dart';
|
||||||
import 'components/camera_view_placeholder/widget_placeholder_camera_view.dart';
|
import 'components/camera_view_placeholder/widget_placeholder_camera_view.dart';
|
||||||
import 'event_container_camera.dart';
|
import 'event_container_camera.dart';
|
||||||
import 'state_container_camera.dart';
|
import 'state_container_camera.dart';
|
||||||
|
@ -78,10 +80,17 @@ class _CameraViewBuilder extends StatelessWidget {
|
||||||
return AspectRatio(
|
return AspectRatio(
|
||||||
aspectRatio: PlatformConfig.cameraPreviewAspectRatio,
|
aspectRatio: PlatformConfig.cameraPreviewAspectRatio,
|
||||||
child: BlocBuilder<CameraContainerBloc, CameraContainerState>(
|
child: BlocBuilder<CameraContainerBloc, CameraContainerState>(
|
||||||
buildWhen: (previous, current) => current is CameraInitializedState,
|
buildWhen: (previous, current) =>
|
||||||
builder: (context, state) => state is CameraInitializedState
|
current is CameraLoadingState ||
|
||||||
? Center(child: CameraView(controller: state.controller))
|
current is CameraInitializedState ||
|
||||||
: const CameraViewPlaceholder(),
|
current is CameraErrorState,
|
||||||
|
builder: (context, state) {
|
||||||
|
if (state is CameraInitializedState) {
|
||||||
|
return Center(child: CameraView(controller: state.controller));
|
||||||
|
} else {
|
||||||
|
return CameraViewPlaceholder(error: state is CameraErrorState ? state.error : null);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -95,23 +104,40 @@ class _CameraControlsBuilder extends StatelessWidget {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
padding: const EdgeInsets.symmetric(vertical: Dimens.paddingM),
|
||||||
child: BlocBuilder<CameraContainerBloc, CameraContainerState>(
|
child: BlocBuilder<CameraContainerBloc, CameraContainerState>(
|
||||||
builder: (context, state) => AnimatedSwitcher(
|
builder: (context, state) {
|
||||||
duration: Dimens.durationS,
|
late final Widget child;
|
||||||
child: state is CameraActiveState
|
if (state is CameraActiveState) {
|
||||||
? CameraControls(
|
child = CameraControls(
|
||||||
exposureOffsetRange: state.exposureOffsetRange,
|
exposureOffsetRange: state.exposureOffsetRange,
|
||||||
exposureOffsetValue: state.currentExposureOffset,
|
exposureOffsetValue: state.currentExposureOffset,
|
||||||
onExposureOffsetChanged: (value) {
|
onExposureOffsetChanged: (value) {
|
||||||
context.read<CameraContainerBloc>().add(ExposureOffsetChangedEvent(value));
|
context.read<CameraContainerBloc>().add(ExposureOffsetChangedEvent(value));
|
||||||
},
|
},
|
||||||
zoomRange: state.zoomRange,
|
zoomRange: state.zoomRange,
|
||||||
zoomValue: state.currentZoom,
|
zoomValue: state.currentZoom,
|
||||||
onZoomChanged: (value) {
|
onZoomChanged: (value) {
|
||||||
context.read<CameraContainerBloc>().add(ZoomChangedEvent(value));
|
context.read<CameraContainerBloc>().add(ZoomChangedEvent(value));
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
: const SizedBox.shrink(),
|
} else if (state is CameraErrorState) {
|
||||||
),
|
child = CameraControlsPlaceholder(
|
||||||
|
error: state.error,
|
||||||
|
onReset: () {
|
||||||
|
context.read<CameraContainerBloc>().add(
|
||||||
|
state.error == CameraErrorType.permissionNotGranted
|
||||||
|
? const OpenAppSettingsEvent()
|
||||||
|
: const InitializeEvent());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
child = const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnimatedSwitcher(
|
||||||
|
duration: Dimens.durationS,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.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';
|
||||||
|
import 'package:lightmeter/data/permissions_service.dart';
|
||||||
import 'package:lightmeter/data/shared_prefs_service.dart';
|
import 'package:lightmeter/data/shared_prefs_service.dart';
|
||||||
import 'package:lightmeter/interactors/metering_interactor.dart';
|
import 'package:lightmeter/interactors/metering_interactor.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
@ -25,6 +26,7 @@ class _MeteringFlowState extends State<MeteringFlow> {
|
||||||
create: (context) => MeteringInteractor(
|
create: (context) => MeteringInteractor(
|
||||||
context.read<UserPreferencesService>(),
|
context.read<UserPreferencesService>(),
|
||||||
context.read<HapticsService>(),
|
context.read<HapticsService>(),
|
||||||
|
context.read<PermissionsService>(),
|
||||||
context.read<LightSensorService>(),
|
context.read<LightSensorService>(),
|
||||||
),
|
),
|
||||||
child: MultiBlocProvider(
|
child: MultiBlocProvider(
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:lightmeter/data/permissions_service.dart';
|
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
|
||||||
|
|
||||||
import 'event_permissions_check.dart';
|
|
||||||
import 'state_permissions_check.dart';
|
|
||||||
|
|
||||||
class PermissionsCheckBloc extends Bloc<PermissionsCheckEvent, PermissionsCheckState> {
|
|
||||||
final PermissionsService _permissionsService;
|
|
||||||
|
|
||||||
PermissionsCheckBloc(this._permissionsService) : super(const LoadingState()) {
|
|
||||||
on<PermissionsGrantedEvent>((event, emit) => emit(const PermissionsGrantedState()));
|
|
||||||
on<PermissionsDeniedEvent>((event, emit) => emit(const PermissionsDeniedState()));
|
|
||||||
_checkAndRequestPermissions();
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _checkAndRequestPermissions() async {
|
|
||||||
_permissionsService.checkCameraPermission().then((value) {
|
|
||||||
switch (value) {
|
|
||||||
case PermissionStatus.permanentlyDenied:
|
|
||||||
case PermissionStatus.restricted:
|
|
||||||
add(const PermissionsDeniedEvent());
|
|
||||||
break;
|
|
||||||
case PermissionStatus.denied:
|
|
||||||
_permissionsService.requestCameraPermission().then((value) {
|
|
||||||
switch (value) {
|
|
||||||
case PermissionStatus.permanentlyDenied:
|
|
||||||
case PermissionStatus.restricted:
|
|
||||||
case PermissionStatus.denied:
|
|
||||||
add(const PermissionsDeniedEvent());
|
|
||||||
break;
|
|
||||||
case PermissionStatus.limited:
|
|
||||||
case PermissionStatus.granted:
|
|
||||||
add(const PermissionsGrantedEvent());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case PermissionStatus.limited:
|
|
||||||
case PermissionStatus.granted:
|
|
||||||
add(const PermissionsGrantedEvent());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
abstract class PermissionsCheckEvent {
|
|
||||||
const PermissionsCheckEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PermissionsDeniedEvent extends PermissionsCheckEvent {
|
|
||||||
const PermissionsDeniedEvent();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PermissionsGrantedEvent extends PermissionsCheckEvent {
|
|
||||||
const PermissionsGrantedEvent();
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:lightmeter/data/permissions_service.dart';
|
|
||||||
import 'package:lightmeter/screens/permissions_check/screen_permissions_check.dart';
|
|
||||||
|
|
||||||
import 'bloc_permissions_check.dart';
|
|
||||||
|
|
||||||
class PermissionsCheckFlow extends StatelessWidget {
|
|
||||||
const PermissionsCheckFlow({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return BlocProvider(
|
|
||||||
create: (context) => PermissionsCheckBloc(context.read<PermissionsService>()),
|
|
||||||
child: const PermissionsCheckScreen(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:lightmeter/generated/l10n.dart';
|
|
||||||
import 'package:lightmeter/res/dimens.dart';
|
|
||||||
|
|
||||||
import 'bloc_permissions_check.dart';
|
|
||||||
import 'state_permissions_check.dart';
|
|
||||||
|
|
||||||
class PermissionsCheckScreen extends StatelessWidget {
|
|
||||||
const PermissionsCheckScreen({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
|
||||||
body: SafeArea(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(Dimens.paddingM * 2),
|
|
||||||
child: Center(
|
|
||||||
child: BlocConsumer<PermissionsCheckBloc, PermissionsCheckState>(
|
|
||||||
listener: (context, state) {
|
|
||||||
if (state is PermissionsGrantedState) {
|
|
||||||
Navigator.of(context).pushReplacementNamed("metering");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
builder: (context, state) {
|
|
||||||
return AnimatedSwitcher(
|
|
||||||
duration: Dimens.durationS,
|
|
||||||
child: state is LoadingState
|
|
||||||
? const CircularProgressIndicator()
|
|
||||||
: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
S.of(context).permissionNeeded,
|
|
||||||
style: Theme.of(context).textTheme.headlineLarge,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: Dimens.grid16),
|
|
||||||
Text(
|
|
||||||
S.of(context).permissionNeededMessage,
|
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
),
|
|
||||||
const SizedBox(height: Dimens.grid24),
|
|
||||||
ElevatedButton(
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
||||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
|
||||||
),
|
|
||||||
onPressed: () {},
|
|
||||||
child: Text(S.of(context).openSettings),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
abstract class PermissionsCheckState {
|
|
||||||
const PermissionsCheckState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class LoadingState extends PermissionsCheckState {
|
|
||||||
const LoadingState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PermissionsGrantedState extends PermissionsCheckState {
|
|
||||||
const PermissionsGrantedState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class PermissionsDeniedState extends PermissionsCheckState {
|
|
||||||
const PermissionsDeniedState();
|
|
||||||
}
|
|
|
@ -7,6 +7,7 @@ environment:
|
||||||
sdk: ">=2.18.0 <3.0.0"
|
sdk: ">=2.18.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
|
app_settings: 4.2.0
|
||||||
camera: 0.10.0+4
|
camera: 0.10.0+4
|
||||||
exif: 3.1.2
|
exif: 3.1.2
|
||||||
dynamic_color: 1.5.4
|
dynamic_color: 1.5.4
|
||||||
|
|
Loading…
Reference in a new issue