implemeted permissions check (wip)

This commit is contained in:
Vadim 2022-11-27 23:22:56 +03:00
parent f420ddc186
commit 7460f12211
16 changed files with 331 additions and 64 deletions

View file

@ -32,4 +32,7 @@
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
</application> </application>
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="true" />
</manifest> </manifest>

53
ios/Podfile Normal file
View file

@ -0,0 +1,53 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
# Start of the permission_handler configuration
target.build_configurations.each do |config|
# Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
'$(inherited)',
'PERMISSION_CAMERA=1',
## dart: PermissionGroup.sensors
# 'PERMISSION_SENSORS=1',
]
end
end
end

View file

@ -1,49 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true />
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Lightmeter</string> <string>Lightmeter</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>lightmeter</string> <string>lightmeter</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true />
<key>UIApplicationSupportsIndirectInputEvents</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<true/> <true />
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
<string>LaunchScreen</string> <string>LaunchScreen</string>
<key>UIMainStoryboardFile</key> <key>UIMainStoryboardFile</key>
<string>Main</string> <string>Main</string>
<key>UISupportedInterfaceOrientations</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
</array> </array>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations~ipad</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string> <string>UIInterfaceOrientationLandscapeRight</string>
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false />
</dict> <key>NSCameraUsageDescription</key>
<string>Provide camera permissions in order to make measurements</string>
</dict>
</plist> </plist>

View file

@ -0,0 +1,7 @@
import 'package:permission_handler/permission_handler.dart';
class PermissionsService {
Future<PermissionStatus> checkCameraPermission() async => await Permission.camera.status;
Future<PermissionStatus> requestCameraPermission() async => Permission.camera.request();
}

View file

@ -27,6 +27,11 @@ class MessageLookup extends MessageLookupByLibrary {
"haptics": MessageLookupByLibrary.simpleMessage("Haptics"), "haptics": MessageLookupByLibrary.simpleMessage("Haptics"),
"keepsScreenOn": "keepsScreenOn":
MessageLookupByLibrary.simpleMessage("Keeps screen on"), MessageLookupByLibrary.simpleMessage("Keeps screen on"),
"openSettings": MessageLookupByLibrary.simpleMessage("Open settings"),
"permissionNeeded":
MessageLookupByLibrary.simpleMessage("Permission needed"),
"permissionNeededMessage": MessageLookupByLibrary.simpleMessage(
"To use Lightmeter, turn on Camera permissions."),
"settings": MessageLookupByLibrary.simpleMessage("Settings"), "settings": MessageLookupByLibrary.simpleMessage("Settings"),
"slowestExposurePair": MessageLookupByLibrary.simpleMessage("Slowest") "slowestExposurePair": MessageLookupByLibrary.simpleMessage("Slowest")
}; };

View file

@ -50,6 +50,36 @@ class S {
return Localizations.of<S>(context, S); return Localizations.of<S>(context, S);
} }
/// `Permission needed`
String get permissionNeeded {
return Intl.message(
'Permission needed',
name: 'permissionNeeded',
desc: '',
args: [],
);
}
/// `To use Lightmeter, turn on Camera permissions.`
String get permissionNeededMessage {
return Intl.message(
'To use Lightmeter, turn on Camera permissions.',
name: 'permissionNeededMessage',
desc: '',
args: [],
);
}
/// `Open settings`
String get openSettings {
return Intl.message(
'Open settings',
name: 'openSettings',
desc: '',
args: [],
);
}
/// `Fastest` /// `Fastest`
String get fastestExposurePair { String get fastestExposurePair {
return Intl.message( return Intl.message(

View file

@ -1,5 +1,8 @@
{ {
"@@locale": "en", "@@locale": "en",
"permissionNeeded": "Permission needed",
"permissionNeededMessage": "To use Lightmeter, turn on Camera permissions.",
"openSettings": "Open settings",
"fastestExposurePair": "Fastest", "fastestExposurePair": "Fastest",
"slowestExposurePair": "Slowest", "slowestExposurePair": "Slowest",
"settings": "Settings", "settings": "Settings",

View file

@ -2,14 +2,16 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:lightmeter/data/permissions_service.dart';
import 'package:lightmeter/screens/settings/settings_page_route_builder.dart'; import 'package:lightmeter/screens/settings/settings_page_route_builder.dart';
import 'package:provider/provider.dart';
import 'generated/l10n.dart'; import 'generated/l10n.dart';
import 'models/photography_value.dart'; import 'models/photography_value.dart';
import 'res/dimens.dart'; import 'res/dimens.dart';
import 'res/theme.dart'; import 'res/theme.dart';
import 'screens/metering/metering_bloc.dart'; import 'screens/metering/metering_bloc.dart';
import 'screens/metering/metering_screen.dart'; import 'screens/permissions_check/flow_permissions_check.dart';
import 'utils/stop_type_provider.dart'; import 'utils/stop_type_provider.dart';
void main() { void main() {
@ -57,23 +59,27 @@ class _ApplicationState extends State<Application> with TickerProviderStateMixin
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StopTypeProvider( return Provider(
child: BlocProvider( create: (context) => PermissionsService(),
create: (context) => MeteringBloc(context.read<StopType>()), child: StopTypeProvider(
child: MaterialApp( child: BlocProvider(
theme: ThemeData( create: (context) => MeteringBloc(context.read<StopType>()),
useMaterial3: true, child: MaterialApp(
colorScheme: lightColorScheme, theme: ThemeData(
useMaterial3: true,
colorScheme: lightColorScheme,
),
navigatorObservers: [_settingsRouteObserver],
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
home: const PermissionsCheckFlow(),
//home: MeteringScreen(animationController: _animationController),
), ),
navigatorObservers: [_settingsRouteObserver],
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
home: MeteringScreen(animationController: _animationController),
), ),
), ),
); );

View file

@ -8,8 +8,10 @@ class Dimens {
static const double grid8 = 8; static const double grid8 = 8;
static const double grid16 = 16; static const double grid16 = 16;
static const double grid24 = 24; static const double grid24 = 24;
static const double grid168 = 168;
static const double paddingM = 16; static const double paddingM = 16;
static const double paddingL = 24;
static const Duration durationS = Duration(milliseconds: 100); static const Duration durationS = Duration(milliseconds: 100);
static const Duration durationSM = Duration(milliseconds: 150); static const Duration durationSM = Duration(milliseconds: 150);

View file

@ -0,0 +1,46 @@
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;
}
});
}
}

View file

@ -0,0 +1,11 @@
abstract class PermissionsCheckEvent {
const PermissionsCheckEvent();
}
class PermissionsDeniedEvent extends PermissionsCheckEvent {
const PermissionsDeniedEvent();
}
class PermissionsGrantedEvent extends PermissionsCheckEvent {
const PermissionsGrantedEvent();
}

View file

@ -0,0 +1,18 @@
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(),
);
}
}

View file

@ -0,0 +1,65 @@
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 'package:lightmeter/screens/settings/settings_screen.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).pushReplacement(MaterialPageRoute(builder: (_) => SettingsScreen()));
}
},
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),
),
],
),
);
},
),
),
),
),
);
}
}

View file

@ -0,0 +1,15 @@
abstract class PermissionsCheckState {
const PermissionsCheckState();
}
class LoadingState extends PermissionsCheckState {
const LoadingState();
}
class PermissionsGrantedState extends PermissionsCheckState {
const PermissionsGrantedState();
}
class PermissionsDeniedState extends PermissionsCheckState {
const PermissionsDeniedState();
}

View file

@ -17,7 +17,7 @@ class SettingsScreen extends StatelessWidget {
SliverAppBar( SliverAppBar(
pinned: true, pinned: true,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
expandedHeight: 160.0, expandedHeight: Dimens.grid168,
flexibleSpace: FlexibleSpaceBar( flexibleSpace: FlexibleSpaceBar(
centerTitle: false, centerTitle: false,
titlePadding: const EdgeInsets.all(Dimens.paddingM), titlePadding: const EdgeInsets.all(Dimens.paddingM),

View file

@ -14,6 +14,7 @@ dependencies:
sdk: flutter sdk: flutter
intl: ^0.17.0 intl: ^0.17.0
material_color_utilities: ^0.2.0 material_color_utilities: ^0.2.0
permission_handler: 10.2.0
provider: ^6.0.4 provider: ^6.0.4
dev_dependencies: dev_dependencies: