From e55674e1b9777803117b11b8fc16abae50f3d2ad Mon Sep 17 00:00:00 2001 From: Akira Date: Fri, 24 May 2024 07:21:28 +0900 Subject: [PATCH] Fixed Location Permission issue on Android - 1 --- android/app/src/main/AndroidManifest.xml | 1 + .../com/dvox/gifunavi/LocationService.kt | 99 ++++----- .../kotlin/com/dvox/gifunavi/MainActivity.kt | 25 ++- ios/Runner/AppDelegate.swift | 26 +++ lib/main.dart | 40 +++- .../destination/destination_controller.dart | 10 + lib/pages/home/home_page.dart | 5 + lib/pages/index/index_controller.dart | 4 + lib/pages/permission/permission.dart | 198 ++++++++++++------ lib/pages/permission/permission2.dart | 188 ----------------- lib/routes/app_pages.dart | 5 +- lib/services/location_service.dart | 14 ++ lib/utils/location_controller.dart | 14 ++ lib/utils/string_values.dart | 2 +- lib/widgets/current_position_widget.dart | 31 ++- lib/widgets/map_widget.dart | 4 +- lib/widgets/permission_handler_screen.dart | 31 +++ pubspec.lock | 8 + pubspec.yaml | 1 + 19 files changed, 376 insertions(+), 330 deletions(-) delete mode 100644 lib/pages/permission/permission2.dart create mode 100644 lib/widgets/permission_handler_screen.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a96c3a8..a6c0e54 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ + = Build.VERSION_CODES.O) { + val channel = NotificationChannel(CHANNEL_ID, "Location", NotificationManager.IMPORTANCE_DEFAULT) + val notificationManager = getSystemService(NotificationManager::class.java) + notificationManager?.createNotificationChannel(channel) + } + val notification = NotificationCompat.Builder(this, CHANNEL_ID) + .setContentTitle("Tracking location...") + .setContentText("Location: null") + .setSmallIcon(android.R.drawable.ic_menu_mylocation) + .setOngoing(true) + .build() + + startForeground(NOTIFICATION_ID, notification) } else { Log.d("LocationService", "GPS is disabled.") // GPSが無効の場合の処理を追加する(例: ユーザーにGPSを有効にするように促すなど) + stopSelf() // サービスを停止する } } else { - Log.d("LocationService", "Location permission is not granted.") - // 位置情報の権限が許可されていない場合の処理を追加する + Log.d("LocationService", "Location permission or Foreground service location permission is not granted.") + // 位置情報の権限またはフォアグラウンドサービスの位置情報の権限が許可されていない場合の処理を追加する + stopSelf() // サービスを停止する } + } - /* - // GPSデバイスが有効になっているか確認する - val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager - if (!locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { - Log.d("LocationService", "GPS is disabled.") - // GPSが無効の場合の処理を追加する(例: ユーザーにGPSを有効にするように促すなど) - }else{ - Log.d("LocationService", "GPS is enabled.") - } - */ - // フォアグラウンドサービスの設定 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val CHANNEL_ID = "location" - val channel = NotificationChannel(CHANNEL_ID, "Location", NotificationManager.IMPORTANCE_DEFAULT) - val notificationManager = getSystemService(NotificationManager::class.java) // この行を追加 - Log.d("LocationService", "Android: Foreground service.") - - notificationManager?.createNotificationChannel(channel) - } - val notification = NotificationCompat.Builder(this, "location") - .setContentTitle("Tracking location...") - .setContentText("Location: null") - .setSmallIcon(android.R.drawable.ic_menu_mylocation) // リソースが存在しないため0を設定 - .setOngoing(true) - .build() - Log.d("LocationService", "Android: Foreground service 2.") - - startForeground(1, notification) + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + return START_STICKY } override fun onDestroy() { @@ -170,8 +166,6 @@ class LocationService : Service() { private val locationCallback = object : LocationCallback() { override fun onLocationResult(locationResult: LocationResult) { val currentLocation = locationResult.lastLocation - //Log.d("LocationService", "Android: onLocationResult.") - if (currentLocation != null) { val accuracy = currentLocation.accuracy if (accuracy <= 30) { @@ -181,13 +175,12 @@ class LocationService : Service() { val lon = currentLocation.longitude val currentTime = System.currentTimeMillis() - // GPS データをデバッグ用に表示 + // GPSデータをデバッグ用に表示 val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) val formattedTime = sdf.format(Date(currentTime)) - //Log.d("LocationService", "Latitude: $lat, Longitude: $lon, Time: $formattedTime, Accuracy: $accuracy") - // GPS データをデータベースに保存 - GlobalScope.launch { + // GPSデータをデータベースに保存 + GlobalScope.launch(Dispatchers.IO) { addGPStoDB(lat, lon, currentTime) } @@ -203,36 +196,16 @@ class LocationService : Service() { } +/* override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { Log.d("LocationService", "Android: onStartCommand.") - /* onCreate でやってるので除外。 - // 位置情報の権限チェックとGPS有効化の確認を行う - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { - val locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager - if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) { - val locationRequest = LocationRequest.create().apply { - priority = LocationRequest.PRIORITY_HIGH_ACCURACY - interval = 10000 - fastestInterval = 5000 - } - fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null) - } else { - Log.d("LocationService", "GPS is disabled.") - // GPSが無効の場合の処理を追加する(例: ユーザーにGPSを有効にするように促すなど) - } - } else { - Log.d("LocationService", "Location permission is not granted.") - // 位置情報の権限が許可されていない場合の処理を追加する - } - - */ - // Foregroundサービスを開始 startForeground(NOTIFICATION_ID, createNotification()) return START_STICKY } +*/ private suspend fun addGPStoDB(lat: Double, lng: Double, timestamp: Long, isCheckin: Int = 0) { try { @@ -241,7 +214,6 @@ class LocationService : Service() { val teamName = preferences.getString("team_name", "") ?: "" val eventCode = preferences.getString("event_code", "") ?: "" - if (teamName.isNotEmpty() && eventCode.isNotEmpty()) { val gpsData = GpsData( id = 0, @@ -253,13 +225,12 @@ class LocationService : Service() { created_at = timestamp ) - val db = GpsDatabaseHelper.getInstance(context) - db.insertGps(gpsData) + gpsDatabaseHelper.insertGps(gpsData) Log.d("LocationService", "Android: addGPStoDB.") - } } catch (e: Exception) { Log.e("LocationService", "Error adding GPS data to DB", e) + // エラーメッセージをユーザーに表示するなどの処理を追加 } } } \ No newline at end of file diff --git a/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt b/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt index 1011834..5ce79ec 100644 --- a/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt +++ b/android/app/src/main/kotlin/com/dvox/gifunavi/MainActivity.kt @@ -36,6 +36,11 @@ class MainActivity: FlutterActivity() { stopLocationService() result.success(null) } + "isLocationServiceRunning" -> { + Log.d("MainActivity", "Android: called isLocationServiceRunnung.") + val isRunning = isServiceRunning(LocationService::class.java) + result.success(isRunning) + } else -> { result.notImplemented() } @@ -75,9 +80,8 @@ class MainActivity: FlutterActivity() { } private fun startLocationService() { - Log.d("MainActivity", "Android: startLocationService.") - - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && + ContextCompat.checkSelfPermission(this, Manifest.permission.FOREGROUND_SERVICE_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (!isServiceRunning(LocationService::class.java)) { val intent = Intent(this, LocationService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -91,16 +95,19 @@ class MainActivity: FlutterActivity() { Log.d("MainActivity", "Location service is already running.") } } else { - Log.d("MainActivity", "Location permission is not granted.") - // 位置情報の権限が許可されていない場合の処理を追加する - ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), PERMISSION_REQUEST_CODE) + Log.d("MainActivity", "Location permission or Foreground service location permission is not granted.") + // 位置情報の権限またはフォアグラウンドサービスの位置情報の権限が許可されていない場合の処理を追加する + // 例: ユーザーに権限の必要性を説明し、許可を求めるダイアログを表示するなど } } private fun stopLocationService() { - Log.d("MainActivity", "Android: stopLocationService.") - val intent = Intent(this, LocationService::class.java) - stopService(intent) + if (isServiceRunning(LocationService::class.java)) { + val intent = Intent(this, LocationService::class.java) + stopService(intent) + } else { + Log.d("MainActivity", "Location service is not running.") + } } private fun isServiceRunning(serviceClass: Class<*>): Boolean { diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 3763683..4f0540d 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -8,6 +8,32 @@ import Flutter didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) + + let controller : FlutterViewController = window?.rootViewController as! FlutterViewController + let locationServiceChannel = FlutterMethodChannel(name: "location", + binaryMessenger: controller.binaryMessenger) + locationServiceChannel.setMethodCallHandler { (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + if call.method == "isLocationServiceRunning" { + result(self.isLocationServiceRunning()) + } else { + result(FlutterMethodNotImplemented) + } + } + + locationManager = CLLocationManager() + locationManager?.delegate = self + locationManager?.requestAlwaysAuthorization() + locationManager?.startUpdatingLocation() + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + private func isLocationServiceRunning() -> Bool { + guard let locationManager = locationManager else { + return false + } + + let isRunning = locationManager.monitoredRegions.count > 0 || locationManager.location != nil + return isRunning + } } diff --git a/lib/main.dart b/lib/main.dart index 65e975b..079808c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:io'; //import 'dart:convert'; //import 'dart:developer'; import 'package:rogapp/model/gps_data.dart'; +import 'package:rogapp/pages/home/home_page.dart'; import 'package:rogapp/utils/database_gps.dart'; import 'package:flutter/material.dart'; import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart'; @@ -31,6 +32,8 @@ import 'package:flutter/services.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'pages/permission/permission.dart'; + Map deviceInfo = {}; @@ -129,7 +132,10 @@ void main() async { //Get.put(LocationController()); - requestLocationPermission(); + //await PermissionController.checkAndRequestPermissions(); + //requestLocationPermission(); + + // startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810 Get.put(SettingsController()); // これを追加 @@ -143,10 +149,11 @@ void main() async { */ runApp(const ProviderScope(child: MyApp())); - + //runApp(HomePage()); // MyApp()からHomePage()に変更 //runApp(const MyApp()); } +/* Future requestLocationPermission() async { try { final status = await Permission.locationAlways.request(); @@ -160,6 +167,7 @@ Future requestLocationPermission() async { print('Error requesting location permission: $e'); } } +*/ // メモリ使用量の解説:https://qiita.com/hukusuke1007/items/e4e987836412e9bc73b9 @@ -289,6 +297,16 @@ Future startBackgroundTracking() async { } catch (e) { print('Error starting background tracking: $e'); } + }else if (Platform.isAndroid && background == false) { + background = true; + debugPrint("バックグラウンド処理を開始しました。"); + + try { + // 位置情報の権限が許可されているかを確認 + await PermissionController.checkAndRequestPermissions(); + }catch(e){ + print('Error starting background tracking: $e'); + } } } @@ -321,8 +339,12 @@ Future stopBackgroundTracking() async { }else if(Platform.isAndroid && background==true){ background=false; debugPrint("バックグラウンド処理:停止しました。"); - await positionStream?.cancel(); - positionStream = null; + const platform = MethodChannel('location'); + try { + await platform.invokeMethod('stopLocationService'); + } on PlatformException catch (e) { + print("Failed to stop location service: '${e.message}'."); + } } } @@ -344,6 +366,12 @@ class _MyAppState extends State with WidgetsBindingObserver { } WidgetsBinding.instance.addObserver(this); + /* + WidgetsBinding.instance.addPostFrameCallback((_) async { + await PermissionController.checkAndRequestPermissions(); + }); + */ + debugPrint("Start MyAppState..."); } @@ -458,6 +486,7 @@ class _MyAppState extends State with WidgetsBindingObserver { @override Widget build(BuildContext context) { + return GetMaterialApp( translations: StringValues(), locale: const Locale('ja', 'JP'), @@ -480,4 +509,7 @@ class _MyAppState extends State with WidgetsBindingObserver { enableLog: true, ); } + + + } diff --git a/lib/pages/destination/destination_controller.dart b/lib/pages/destination/destination_controller.dart index 59f3dff..318b9fd 100644 --- a/lib/pages/destination/destination_controller.dart +++ b/lib/pages/destination/destination_controller.dart @@ -36,6 +36,8 @@ import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:rogapp/utils/const.dart'; import 'package:logger/logger.dart'; +import 'package:rogapp/pages/permission/permission.dart'; + // 目的地に関連する状態管理とロジックを担当するクラスです。 // class DestinationController extends GetxController { @@ -1265,6 +1267,12 @@ class DestinationController extends GetxController { void onInit() async { super.onInit(); + /* + WidgetsBinding.instance.addPostFrameCallback((_) async { + await PermissionController.checkAndRequestPermissions(); + }); + */ + startGPSCheckTimer(); // MapControllerの初期化完了を待機するフラグを設定 @@ -1715,6 +1723,7 @@ class DestinationController extends GetxController { } } + /* // 位置情報の許可を確認する関数です。 // void checkPermission() async { @@ -1726,6 +1735,7 @@ class DestinationController extends GetxController { permission = await Geolocator.requestPermission(); } } + */ // IDに基づいて目的地を取得する関数です。 // diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index ad4eec8..0513385 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -15,6 +15,11 @@ class _HomePageState extends State { @override void initState() { super.initState(); + /* + WidgetsBinding.instance.addPostFrameCallback((_) { + _checkLocationService(); // 非同期的に呼び出す + }); + */ _checkLocationService(); } diff --git a/lib/pages/index/index_controller.dart b/lib/pages/index/index_controller.dart index 6d77184..1c0f2ee 100644 --- a/lib/pages/index/index_controller.dart +++ b/lib/pages/index/index_controller.dart @@ -190,11 +190,15 @@ class IndexController extends GetxController with WidgetsBindingObserver { } + + +/* void checkPermission() { debugPrint("MapControllerの初期化が完了したら、位置情報の許可をチェックする"); _checkLocationPermission(); } +*/ @override void onClose() { diff --git a/lib/pages/permission/permission.dart b/lib/pages/permission/permission.dart index 034c8cb..a7d2cce 100644 --- a/lib/pages/permission/permission.dart +++ b/lib/pages/permission/permission.dart @@ -1,78 +1,156 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:rogapp/routes/app_pages.dart'; +import 'package:rogapp/services/location_service.dart'; +import 'dart:developer' as developer; -class PermissionHandlerScreen extends StatefulWidget { - const PermissionHandlerScreen({Key? key}) : super(key: key); - @override - _PermissionHandlerScreenState createState() => - _PermissionHandlerScreenState(); -} +class PermissionController { -class _PermissionHandlerScreenState extends State { - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - _checkPermissionStatus(); - }); + static Future checkLocationPermissions() async { + debugPrint("(gifunavi)== checkLocationPermissions =="); + final alwaysPermission = await Permission.locationAlways.status; + final whenInUsePermission = await Permission.locationWhenInUse.status; + final locationPermission = await Permission.location.status; + + return (alwaysPermission == PermissionStatus.granted || whenInUsePermission == PermissionStatus.granted) && + (locationPermission == PermissionStatus.granted); } - Future _checkPermissionStatus() async { - PermissionStatus status = await Permission.location.status; + static Future checkLocationBasicPermission() async { + debugPrint("(gifunavi)== checkLocationBasicPermission =="); + final locationPermission = await Permission.location.status; + return locationPermission == PermissionStatus.granted; + } - if (status.isGranted) { - if (context.mounted) { - Get.offNamed(AppPages.LOGIN); + static Future checkLocationWhenInUsePermission() async { + debugPrint("(gifunavi)== checkLocationWhenInUsePermission =="); + final whenInUsePermission = await Permission.locationWhenInUse.status; + return whenInUsePermission == PermissionStatus.granted; + } + + static Future checkLocationAlwaysPermission() async { + debugPrint("(gifunavi)== checkLocationAlwaysPermission =="); + final alwaysPermission = await Permission.locationAlways.status; + return alwaysPermission == PermissionStatus.granted; + } + + static bool isBasicPermission=false; + static Future requestLocationBasicPermissions() async { + debugPrint("(gifunavi)== requestLocationBasicPermissions =="); + try{ + if(!isBasicPermission){ + isBasicPermission=true; + final locationStatus = await Permission.location.request(); + + if (locationStatus != PermissionStatus.granted) { + showPermissionDeniedDialog(); + } } - } else { - if (context.mounted) { - _showPermissionRequestDialog(); + }catch (e, stackTrace){ + print('Exception: $e'); + print('Stack trace: $stackTrace'); + debugPrintStack(label: 'Exception occurred', stackTrace: stackTrace); + } + + } + + static bool isLocationServiceRunning = false; + static bool isRequestedWhenInUsePermission = false; + + static Future requestLocationWhenInUsePermissions() async { + debugPrint("(gifunavi)== requestLocationWhenInUsePermissions =="); + + try{ + if(!isRequestedWhenInUsePermission){ + isRequestedWhenInUsePermission=true; + final whenInUseStatus = await Permission.locationWhenInUse.request(); + + if (whenInUseStatus != PermissionStatus.granted) { + showPermissionDeniedDialog(); + }else{ + if( !isLocationServiceRunning ){ + isLocationServiceRunning=true; + const platform = MethodChannel('location'); + try { + await platform.invokeMethod('startLocationService'); // Location Service を開始する。 + } on PlatformException catch (e) { + debugPrint("Failed to start location service: '${e.message}'."); + } + } + } } + }catch (e, stackTrace){ + debugPrint('Exception: $e'); + debugPrint('Stack trace: $stackTrace'); + debugPrintStack(label: 'Exception occurred', stackTrace: stackTrace); + } + + } + + static bool isRequestedAlwaysPermission = false; + + static Future requestLocationAlwaysPermissions() async { + debugPrint("(gifunavi)== requestLocationAlwaysPermissions =="); + + try { + if( !isRequestedAlwaysPermission ){ + isRequestedAlwaysPermission=true; + final alwaysStatus = await Permission.locationAlways.request(); + + if (alwaysStatus != PermissionStatus.granted) { + showPermissionDeniedDialog(); + } + } + }catch (e, stackTrace){ + print('Exception: $e'); + print('Stack trace: $stackTrace'); + debugPrintStack(label: 'Exception occurred', stackTrace: stackTrace); + } + + } + + static Future checkAndRequestPermissions() async { + final hasPermissions = await checkLocationBasicPermission(); + if (!hasPermissions) { + await requestLocationBasicPermissions(); + } + + final hasWIUPermissions = await checkLocationWhenInUsePermission(); + if (!hasWIUPermissions) { + await requestLocationWhenInUsePermissions(); + } + + final hasAlwaysPermissions = await checkLocationAlwaysPermission(); + if (!hasAlwaysPermissions) { + await requestLocationAlwaysPermissions(); } } - void _showPermissionRequestDialog() { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('location_permission_required_title'.tr), - content: Text('location_permission_required_message'.tr), - actions: [ - TextButton( - child: Text('cancel'.tr), - onPressed: () { - Navigator.of(context).pop(); - Get.offNamed(AppPages.HOME); - }, - ), - TextButton( - child: Text('ok'.tr), - onPressed: () { - Navigator.of(context).pop(); - _requestLocationPermission(); - }, - ), - ], - ); - }, + static void showPermissionDeniedDialog() { + Get.dialog( + AlertDialog( + title: Text('location_permission_needed_title'.tr), + // 位置情報への許可が必要です + content: Text('location_permission_needed_main'.tr), + // 岐阜ロゲでは、位置情報を使用してスタート・チェックイン・ゴール等の通過照明及び移動手段の記録のために、位置情報のトラッキングを行なっています。このためバックグラウンドでもトラッキングができるように位置情報の権限が必要です。 + // 設定画面で、「岐阜ナビ」に対して、常に位置情報を許可するように設定してください。 + actions: [ + TextButton( + child: Text('キャンセル'), + onPressed: () => Get.back(), + ), + TextButton( + child: Text('設定'), + onPressed: () { + Get.back(); + openAppSettings(); + }, + ), + ], + ), ); } - void _requestLocationPermission() async { - final status = await Permission.location.request(); - if (status.isGranted) { - Get.offNamed(AppPages.LOGIN); - } else { - Get.offNamed(AppPages.HOME); - } - } - - @override - Widget build(BuildContext context) { - return Scaffold(body: Container()); - } } \ No newline at end of file diff --git a/lib/pages/permission/permission2.dart b/lib/pages/permission/permission2.dart deleted file mode 100644 index 5132c17..0000000 --- a/lib/pages/permission/permission2.dart +++ /dev/null @@ -1,188 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:rogapp/routes/app_pages.dart'; -import 'dart:io'; - -class PermissionHandlerScreen extends StatefulWidget { - const PermissionHandlerScreen({Key? key}) : super(key: key); - - @override - State createState() => - _PermissionHandlerScreenState(); -} - -class _PermissionHandlerScreenState extends State { - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - _checkPermissionStatus(); - }); - } - - Future _checkPermissionStatus() async { - PermissionStatus status = await Permission.location.status; - - if (status.isGranted) { - if (context.mounted) { - Get.toNamed(AppPages.LOGIN); - } - } else { - if (context.mounted) { - Get.toNamed(AppPages.HOME); - } - } - } - /* - _checkPermissionStatus() async { - PermissionStatus status = await Permission.location.status; - - if (status.isGranted == false) { - if (context.mounted) { - showAlert(context); - } - } else if (status.isPermanentlyDenied) { - await requestPermission(); - } else { - if (mounted) { - Get.toNamed(AppPages.LOGIN); - } - } - } -*/ - - void showAlert(BuildContext context) { - showDialog( - context: context, - builder: (_) => AlertDialog( - title: Text('location_permission_title'.tr), - content: SingleChildScrollView( - child: Text('location_permission_content'.tr), - - ), - actions: [ - ElevatedButton( - child: const Text('OK'), - onPressed: () { - Get.back(); - requestPermission(); - }, - ), - ], - )); - } - - // 要検討:位置情報の許可が拒否された場合、適切なエラーメッセージを表示することを検討してください。 - // - /* - Future requestPermission() async { - PermissionStatus permission = await Permission.location.status; - if (permission == PermissionStatus.permanentlyDenied) { - showPermanentAlert(); - } else { - PermissionStatus newPermission = await Permission.location.request(); - if (newPermission != PermissionStatus.granted) { - exit(0); - } else { - if (context.mounted) { - Get.toNamed(AppPages.LOGIN); - } - } - } - } -*/ - - Future requestPermission() async { - PermissionStatus permission = await Permission.location.request(); - if (permission == PermissionStatus.granted) { - if (context.mounted) { - Get.toNamed(AppPages.LOGIN); - } - } else if (permission == PermissionStatus.denied) { - await showLocationPermissionDeniedDialog(); - } else if (permission == PermissionStatus.permanentlyDenied) { - await showPermanentlyDeniedDialog(); - } - } - - Future showLocationPermissionDeniedDialog() async { - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('location_permission_denied_title'.tr), - content: Text('location_permission_denied_message'.tr), - actions: [ - TextButton( - child: Text('ok'.tr), - onPressed: () => Navigator.of(context).pop(), - ), - ], - ); - }, - ); - } - - Future showPermanentlyDeniedDialog() async { - await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: Text('location_permission_permanently_denied_title'.tr), - content: Text('location_permission_permanently_denied_message'.tr), - actions: [ - TextButton( - child: Text('open_settings'.tr), - onPressed: () { - Navigator.of(context).pop(); - openAppSettings(); - }, - ), - ], - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return const Scaffold( - body: Text(""), - ); - } - - // 要検討:ユーザーが位置情報の許可を拒否し続けた場合の対処方法を明確にすることをお勧めします。 - // - void showPermanentAlert() { - showDialog( - context: context, - builder: (_) => AlertDialog( - title: Text('location_disabled_title'.tr), - content: SingleChildScrollView( - child: Text('location_disabled_content'.tr), - - ), - actions: [ - ElevatedButton( - child: const Text('OK'), - onPressed: () async { - await openAppSettings().then( - (value) async { - if (value) { - if (await Permission - .location.status.isPermanentlyDenied == - true && - await Permission.location.status.isGranted == - false) { - requestPermission(); /* opens app settings until permission is granted */ - } - } - }, - ); - }, - ), - ], - )); - } -} diff --git a/lib/routes/app_pages.dart b/lib/routes/app_pages.dart index abfea04..65fcccf 100644 --- a/lib/routes/app_pages.dart +++ b/lib/routes/app_pages.dart @@ -26,6 +26,7 @@ import 'package:rogapp/pages/debug/debug_binding.dart'; import 'package:rogapp/pages/subperf/subperf_page.dart'; import 'package:rogapp/spa/spa_binding.dart'; import 'package:rogapp/spa/spa_page.dart'; +import 'package:rogapp/widgets/permission_handler_screen.dart'; part 'app_routes.dart'; @@ -42,6 +43,7 @@ class AppPages { static const DESTINATION_MAP = Routes.DESTINATION_MAP; static const HOME = Routes.HOME; static const PERMISSION = Routes.PERMISSION; + //static const PERMISSION = Routes.HOME; static const SEARCH = Routes.SEARCH; static const MAINPERF = Routes.MAINPERF; static const SUBPERF = Routes.SUBPERF; @@ -88,7 +90,7 @@ class AppPages { ), GetPage( name: Routes.PERMISSION, - page: () => const PermissionHandlerScreen(), + page: () => PermissionHandlerScreen(), ), GetPage( name: Routes.SEARCH, @@ -127,4 +129,5 @@ class AppPages { ), ]; + } diff --git a/lib/services/location_service.dart b/lib/services/location_service.dart index ea3c56e..07bfe04 100644 --- a/lib/services/location_service.dart +++ b/lib/services/location_service.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:flutter/services.dart'; import 'package:geojson_vi/geojson_vi.dart'; import 'package:get/get.dart'; import 'package:http/http.dart' as http; @@ -9,6 +10,7 @@ import 'package:rogapp/utils/const.dart'; class LocationService { + static Future loadLocationsFor( String perfecture, String cat) async { final IndexController indexController = Get.find(); @@ -187,4 +189,16 @@ class LocationService { } return null; } + + static const platform = MethodChannel('location'); + + static Future isLocationServiceRunning() async { + try { + final bool isRunning = await platform.invokeMethod('isLocationServiceRunning'); + return isRunning; + } catch (e) { + print("Failed to check if location service is running: $e"); + return false; + } + } } diff --git a/lib/utils/location_controller.dart b/lib/utils/location_controller.dart index caabf9d..1125715 100644 --- a/lib/utils/location_controller.dart +++ b/lib/utils/location_controller.dart @@ -6,6 +6,7 @@ import 'package:latlong2/latlong.dart'; //import 'package:rogapp/widgets/debug_widget.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:rogapp/pages/destination/destination_controller.dart'; +import 'package:rogapp/pages/permission/permission.dart'; // LocationControllerクラスは、GetxControllerを継承したクラスであり、位置情報の管理を担当しています。 // LocationControllerは以下の機能を提供しています。 @@ -188,6 +189,18 @@ class LocationController extends GetxController { // Check for location service and permissions before starting the stream // 位置情報サービスの有効性をチェックし、無効な場合はエラーハンドリングを行います。 // + + await PermissionController.checkAndRequestPermissions(); + + /* + bool isPermissionGranted = await PermissionController.checkLocationPermissions(); + if (!isPermissionGranted) { + PermissionController.showPermissionDeniedDialog(); + return; + } +*/ + + /* bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { // Use GetX's context to show a dialog @@ -262,6 +275,7 @@ class LocationController extends GetxController { ); return; } +*/ // 位置情報の設定を行います。z11 // Set up the location options diff --git a/lib/utils/string_values.dart b/lib/utils/string_values.dart index 1e707ac..88e35e4 100644 --- a/lib/utils/string_values.dart +++ b/lib/utils/string_values.dart @@ -391,7 +391,7 @@ class StringValues extends Translations{ 'location_permission_permanently_denied_message': '位置情報の許可が永久に拒否されました。位置情報の許可を付与するには、アプリ設定を開いてください。', 'open_settings': '設定を開く', 'location_permission_needed_title': '位置情報への許可が必要です', - 'location_permission_needed_main': '位置情報への許可が拒否されています。設定を開いて、位置情報の許可を「岐阜ナビ」に与えてください。', + 'location_permission_needed_main': '岐阜ロゲでは、位置情報を使用してスタート・チェックイン・ゴール等の通過照明及び移動手段の記録のために、位置情報のトラッキングを行なっています。このためバックグラウンドでもトラッキングができるように位置情報の権限が必要です。設定画面で、「岐阜ナビ」に対して、常に位置情報を許可するように設定してください。', 'open_settings': '設定を開く', 'location_permission_denied_title': '位置情報へのアクセスが拒否されています。', 'location_permission_denied_main': 'この岐阜ナビアプリは正常に動かすには位置情報への許可が必要です。「設定」画面で位置情報の許可を指定してください。', diff --git a/lib/widgets/current_position_widget.dart b/lib/widgets/current_position_widget.dart index 3e2fa66..5c79803 100644 --- a/lib/widgets/current_position_widget.dart +++ b/lib/widgets/current_position_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:rogapp/pages/destination/destination_controller.dart'; import 'package:rogapp/routes/app_pages.dart'; // これを追加 @@ -14,8 +15,34 @@ class _CurrentPositionState extends State { final DestinationController destinationController = Get.find(); - void _onLongPress() { - Get.toNamed(AppPages.SETTINGS); // これを追加 + void _onLongPress() async { + PermissionStatus status = await Permission.location.status; + if (!status.isGranted) { + await showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: Text('位置情報の許可が必要です'), + content: Text('現在位置を表示するには、位置情報の許可が必要です。「設定」からアプリの権限を許可してください。'), + actions: [ + TextButton( + child: Text('キャンセル'), + onPressed: () => Navigator.of(context).pop(), + ), + TextButton( + child: Text('設定'), + onPressed: () { + Navigator.of(context).pop(); + openAppSettings(); + }, + ), + ], + ); + }, + ); + } else { + Get.toNamed(AppPages.SETTINGS); + } } @override diff --git a/lib/widgets/map_widget.dart b/lib/widgets/map_widget.dart index 53799cd..6c9dc68 100644 --- a/lib/widgets/map_widget.dart +++ b/lib/widgets/map_widget.dart @@ -6,6 +6,7 @@ import 'package:flutter_polyline_points/flutter_polyline_points.dart'; import 'package:geojson_vi/geojson_vi.dart'; import 'package:get/get.dart'; import 'package:latlong2/latlong.dart'; +import 'package:rogapp/pages/permission/permission.dart'; import 'package:rogapp/pages/settings/settings_binding.dart'; import 'package:rogapp/model/destination.dart'; import 'package:rogapp/pages/destination/destination_controller.dart'; @@ -75,7 +76,8 @@ class _MapWidgetState extends State with WidgetsBindingObserver { indexController.isMapControllerReady.value = true; }); // MapControllerの初期化が完了したら、IndexControllerのonInitを呼び出す - indexController.checkPermission(); + //indexController.checkPermission(); + PermissionController.checkAndRequestPermissions(); }); late MapResetController mapResetController = MapResetController(); diff --git a/lib/widgets/permission_handler_screen.dart b/lib/widgets/permission_handler_screen.dart new file mode 100644 index 0000000..ba9bf27 --- /dev/null +++ b/lib/widgets/permission_handler_screen.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:rogapp/pages/permission/permission.dart'; + +class PermissionHandlerScreen extends StatefulWidget { + @override + _PermissionHandlerScreenState createState() => _PermissionHandlerScreenState(); +} + +class _PermissionHandlerScreenState extends State { + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) async { + await PermissionController.checkAndRequestPermissions(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('権限の確認'), + ), + body: Center( + child: Text('権限の確認中...'), + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 9b63280..6a4bf57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -760,6 +760,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2+1" + logging: + dependency: "direct main" + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index dd54895..1f44fb1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: flutter_map: ^6.0.1 geolocator: ^10.1.0 permission_handler: ^11.3.1 + logging: ^1.0.2 # flutter_dev_tools: ^0.0.2 # permission_handler: ^11.1.0 <== older # permission_handler 11.2.0 (11.3.1 available)