diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index aa8933d..a4562a9 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -7,6 +7,8 @@
+
+
+
diff --git a/android/app/src/main/kotlin/com/example/rogapp/LocationService.kt b/android/app/src/main/kotlin/com/example/rogapp/LocationService.kt
new file mode 100644
index 0000000..5f1ef6f
--- /dev/null
+++ b/android/app/src/main/kotlin/com/example/rogapp/LocationService.kt
@@ -0,0 +1,98 @@
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.os.IBinder
+import androidx.core.app.NotificationCompat
+
+class LocationService : Service() {
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+
+ override fun onCreate() {
+ super.onCreate()
+ // フォアグラウンドサービスの設定
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(
+ "location",
+ "Location",
+ NotificationManager.IMPORTANCE_LOW
+ )
+ val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.createNotificationChannel(channel)
+ }
+ val notification = NotificationCompat.Builder(this, "location")
+ .setContentTitle("Tracking location...")
+ .setContentText("Location: null")
+ .setSmallIcon(R.drawable.ic_launcher)
+ .setOngoing(true)
+ .build()
+ startForeground(1, notification)
+ }
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ // バックグラウンドでの位置情報取得処理を実装
+ val locationSettings = LocationSettings.Builder()
+ .setAccuracy(LocationAccuracy.HIGH)
+ .setDistanceFilter(100.0f)
+ .build()
+
+ try {
+ Geolocator.getPositionStream(locationSettings)
+ .catch { e ->
+ // エラーハンドリング
+ println("Location Error: $e")
+ null
+ }
+ .filterNotNull()
+ .filter { position ->
+ // GPS信号がlow以上の場合のみ記録
+ position.accuracy <= 100
+ }
+ .onEach { position ->
+ val lat = position.latitude
+ val lng = position.longitude
+ val timestamp = System.currentTimeMillis()
+
+ // データベースに位置情報を保存
+ addGPStoDB(lat, lng, timestamp)
+ }
+ .launchIn(CoroutineScope(Dispatchers.IO))
+ } catch (e: Exception) {
+ println("Error starting background tracking: $e")
+ }
+
+ return START_STICKY
+ }
+
+ private fun addGPStoDB(lat: Double, lng: Double, isCheckin: Int = 0) {
+ try {
+ val context = applicationContext
+ val preferences = context.getSharedPreferences("RogPreferences", Context.MODE_PRIVATE)
+ val teamName = preferences.getString("team_name", "") ?: ""
+ val eventCode = preferences.getString("event_code", "") ?: ""
+
+ if (teamName.isNotEmpty() && eventCode.isNotEmpty()) {
+ val gpsData = GpsData(
+ id = 0,
+ team_name = teamName,
+ event_code = eventCode,
+ lat = lat,
+ lon = lng,
+ is_checkin = isCheckin,
+ created_at = System.currentTimeMillis()
+ )
+
+ GlobalScope.launch {
+ val db = GpsDatabaseHelper.getInstance(context)
+ db.insertGps(gpsData)
+ }
+ }
+ } catch (e: Exception) {
+ println("Error adding GPS data to DB: $e")
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt b/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt
index 9c7fd0b..4b5d3a9 100644
--- a/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/rogapp/MainActivity.kt
@@ -2,5 +2,32 @@ package com.example.rogapp
import io.flutter.embedding.android.FlutterActivity
+import androidx.annotation.NonNull
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodChannel
+
class MainActivity: FlutterActivity() {
+ private val CHANNEL = "location"
+
+ override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+ MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
+ call, result ->
+ when (call.method) {
+ "startLocationService" -> {
+ val intent = Intent(this, LocationService::class.java)
+ startService(intent)
+ result.success(null)
+ }
+ "stopLocationService" -> {
+ val intent = Intent(this, LocationService::class.java)
+ stopService(intent)
+ result.success(null)
+ }
+ else -> {
+ result.notImplemented()
+ }
+ }
+ }
+ }
}
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 4fcd653..a1b8ae5 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -11,9 +11,9 @@ PODS:
- Flutter
- flutter_keyboard_visibility (0.0.1):
- Flutter
- - FMDB (2.7.5):
- - FMDB/standard (= 2.7.5)
- - FMDB/standard (2.7.5)
+ - FMDB (2.7.10):
+ - FMDB/standard (= 2.7.10)
+ - FMDB/standard (2.7.10)
- geolocator_apple (1.2.0):
- Flutter
- image_gallery_saver (2.0.2):
@@ -35,7 +35,7 @@ PODS:
- qr_code_scanner (0.2.0):
- Flutter
- MTBBarcodeScanner
- - ReachabilitySwift (5.0.0)
+ - ReachabilitySwift (5.2.1)
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
@@ -121,7 +121,7 @@ SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
flutter_compass: cbbd285cea1584c7ac9c4e0c3e1f17cbea55e855
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
- FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
+ FMDB: eae540775bf7d0c87a5af926ae37af69effe5a19
geolocator_apple: 9157311f654584b9bb72686c55fc02a97b73f461
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
@@ -132,7 +132,7 @@ SPEC CHECKSUMS:
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
pointer_interceptor_ios: 9280618c0b2eeb80081a343924aa8ad756c21375
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
- ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
+ ReachabilitySwift: 5ae15e16814b5f9ef568963fb2c87aeb49158c66
shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695
sqflite: 50a33e1d72bd59ee092a519a35d107502757ebed
url_launcher_ios: bbd758c6e7f9fd7b5b1d4cde34d2b95fcce5e812
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 8af03db..09cbe28 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -138,8 +138,8 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- CD1CB185AC38B6B245F9A672 /* [CP] Embed Pods Frameworks */,
- 4D62FB08D65E9D3D4D84B418 /* [CP] Copy Pods Resources */,
+ E16D8B21E5C6C68755A033DB /* [CP] Embed Pods Frameworks */,
+ 03349FDCCEEF7287E57A0F48 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -198,23 +198,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
- isa = PBXShellScriptBuildPhase;
- alwaysOutOfDate = 1;
- buildActionMask = 2147483647;
- files = (
- );
- inputPaths = (
- "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
- );
- name = "Thin Binary";
- outputPaths = (
- );
- runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
- };
- 4D62FB08D65E9D3D4D84B418 /* [CP] Copy Pods Resources */ = {
+ 03349FDCCEEF7287E57A0F48 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -231,6 +215,22 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
@@ -244,7 +244,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
- shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n";
};
9ACF47601C5E8A391157E87A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
@@ -268,7 +268,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- CD1CB185AC38B6B245F9A672 /* [CP] Embed Pods Frameworks */ = {
+ E16D8B21E5C6C68755A033DB /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index a1fd8ff..e6d4768 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -67,5 +67,13 @@
FLTEnableImpeller
+ UIBackgroundModes
+
+ location
+
+ NSLocationWhenInUseUsageDescription
+ This app needs access to location when open.
+ NSLocationAlwaysUsageDescription
+ This app needs access to location when in the background.
diff --git a/lib/main.dart b/lib/main.dart
index f1e9831..99a0fa4 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,8 +1,12 @@
import 'dart:async';
+import 'dart:io';
//import 'dart:convert';
//import 'dart:developer';
+import 'package:rogapp/model/gps_data.dart';
+import 'package:rogapp/utils/database_gps.dart';
import 'package:flutter/material.dart';
import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
+import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
//import 'package:vm_service/vm_service.dart';
//import 'package:dart_vm_info/dart_vm_info.dart';
@@ -23,6 +27,10 @@ import 'package:rogapp/services/error_service.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
//import 'dart:async';
//import 'package:get/get.dart';
+import 'package:flutter/services.dart';
+
+import 'package:permission_handler/permission_handler.dart';
+
Map deviceInfo = {};
@@ -114,6 +122,8 @@ void main() async {
//Get.put(LocationController());
+ requestLocationPermission();
+
// startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810
Get.put(SettingsController()); // これを追加
@@ -126,6 +136,21 @@ void main() async {
//runApp(const MyApp());
}
+Future requestLocationPermission() async {
+ try {
+ final status = await Permission.locationAlways.request();
+ if (status == PermissionStatus.granted) {
+ print('Location permission granted');
+ } else {
+ print('Location permission denied');
+ //await showLocationPermissionDeniedDialog(); // 追加
+ }
+ } catch (e) {
+ print('Error requesting location permission: $e');
+ }
+}
+
+
// メモリ使用量の解説:https://qiita.com/hukusuke1007/items/e4e987836412e9bc73b9
/*
@@ -192,6 +217,99 @@ void showMemoryWarningDialog() {
}
}
+StreamSubscription? positionStream;
+bool background=false;
+DateTime lastGPSCollectedTime=DateTime.now();
+String team_name="";
+String event_code="";
+
+Future startBackgroundTracking() async {
+ if (Platform.isIOS && background==false) {
+
+ final IndexController indexController = Get.find();
+ if(indexController.currentUser.length>0) {
+ team_name = indexController.currentUser[0]["user"]['team_name'];
+ event_code = indexController.currentUser[0]["user"]["event_code"];
+ }
+ background = true;
+ debugPrint("バックグラウンド処理を開始しました。");
+ final LocationSettings locationSettings = LocationSettings(
+ accuracy: LocationAccuracy.high,
+ distanceFilter: 100,
+ );
+
+ try {
+ positionStream = Geolocator.getPositionStream(locationSettings: locationSettings)
+ .listen((Position? position) async {
+ if (position != null) {
+ final lat = position.latitude;
+ final lng = position.longitude;
+ //final timestamp = DateTime.now();
+ final accuracy = position.accuracy;
+
+ // GPS信号強度がlowの場合はスキップ
+ if (accuracy > 100) {
+ debugPrint("GPS signal strength is low. Skipping data saving.");
+ return;
+ }
+
+ Duration difference = lastGPSCollectedTime.difference(DateTime.now())
+ .abs();
+ // 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過(普通に歩くスピード)
+ //debugPrint("時間差:${difference}");
+ if (difference.inSeconds >= 10 ) {
+ debugPrint("バックグラウンドでのGPS取得時の処理(10secおき) count=${difference.inSeconds}, time=${DateTime.now()}");
+
+ // DBにGPSデータを保存 pages/destination/destination_controller.dart
+ await addGPStoDB(lat, lng);
+
+ lastGPSCollectedTime = DateTime.now();
+ }
+ }
+ }, onError: (error) {
+ if (error is LocationServiceDisabledException) {
+ print('Location services are disabled');
+ } else if (error is PermissionDeniedException) {
+ print('Location permissions are denied');
+ } else {
+ print('Location Error: $error');
+ }
+ });
+ } catch (e) {
+ print('Error starting background tracking: $e');
+ }
+ }
+}
+
+Future addGPStoDB(double la, double ln) async {
+ //debugPrint("in addGPStoDB ${indexController.currentUser}");
+ GpsDatabaseHelper db = GpsDatabaseHelper.instance;
+ try {
+ GpsData gps_data = GpsData(
+ id: 0,
+ team_name: team_name,
+ event_code: event_code,
+ lat: la,
+ lon: ln,
+ is_checkin: 0,
+ created_at: DateTime.now().millisecondsSinceEpoch);
+ var res = await db.insertGps(gps_data);
+ //debugPrint("バックグラウンドでのGPS保存:");
+ } catch (err) {
+ print("errr ready gps ${err}");
+ return;
+ }
+}
+
+Future stopBackgroundTracking() async {
+ if (Platform.isIOS && background==true) {
+ background=false;
+ debugPrint("バックグラウンド処理:停止しました。");
+ await positionStream?.cancel();
+ positionStream = null;
+ }
+}
+
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@@ -209,8 +327,11 @@ class _MyAppState extends State with WidgetsBindingObserver {
restoreGame();
}
WidgetsBinding.instance.addObserver(this);
+
+ debugPrint("Start MyAppState...");
}
+
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
@@ -227,37 +348,69 @@ class _MyAppState extends State with WidgetsBindingObserver {
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
LocationController locationController = Get.find();
+ DestinationController destinationController = Get.find();
//DestinationController destinationController =
// Get.find();
switch (state) {
case AppLifecycleState.resumed:
+ // Foreground に戻った時の処理
+ debugPrint(" ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました");
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
+
+ // バックグラウンド処理を停止
+ if (Platform.isIOS && destinationController.isRunningBackgroundGPS) {
+ stopBackgroundTracking();
+ destinationController.isRunningBackgroundGPS=false;
+ destinationController.restartGPS();
+
+ } else if (Platform.isAndroid) {
+ const platform = MethodChannel('location');
+ platform.invokeMethod('stopLocationService');
+ }
break;
case AppLifecycleState.inactive:
- locationController.resumePositionStream();
- //print("INACTIVE");
+ // アプリが非アクティブになったときに発生します。
+ // これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。
+ debugPrint(" ==(Status Changed)==> PAUSED. 非アクティブ処理。");
+ //locationController.resumePositionStream();
+
+ // 追加: フロントエンドのGPS信号のlistenを停止
+ locationController.stopPositionStream();
+
+ if (Platform.isIOS ) { // iOSはバックグラウンドでもフロントの処理が生きている。
+ destinationController.isRunningBackgroundGPS=true;
+ startBackgroundTracking();
+ }else if(Platform.isAndroid){
+ const platform = MethodChannel('location');
+ platform.invokeMethod('startLocationService');
+ }
+ saveGameState();
break;
case AppLifecycleState.paused:
- locationController.resumePositionStream();
- //print("PAUSED");
+ // バックグラウンドに移行したときの処理
+ //locationController.resumePositionStream();
+ debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
saveGameState();
break;
case AppLifecycleState.detached:
- locationController.resumePositionStream();
- //print("DETACHED");
+ // アプリが終了する直前に発生します。この状態では、アプリはメモリから解放される予定です。
+ //locationController.resumePositionStream();
+ debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。");
saveGameState();
break;
case AppLifecycleState.hidden:
- locationController.resumePositionStream();
- //print("DETACHED");
+ // Web用の特殊な状態で、モバイルアプリでは発生しません。
+ //locationController.resumePositionStream();
+ debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた");
saveGameState();
break;
}
}
+
@override
Widget build(BuildContext context) {
return GetMaterialApp(
diff --git a/lib/pages/destination/destination_controller.dart b/lib/pages/destination/destination_controller.dart
index 51f0ddb..544f876 100644
--- a/lib/pages/destination/destination_controller.dart
+++ b/lib/pages/destination/destination_controller.dart
@@ -946,6 +946,7 @@ class DestinationController extends GetxController {
is_checkin: isCheckin,
created_at: DateTime.now().millisecondsSinceEpoch);
var res = await db.insertGps(gps_data);
+ //debugPrint("Saved GPS data into DB...");
}
} catch (err) {
print("errr ready gps ${err}");
@@ -1202,7 +1203,10 @@ class DestinationController extends GetxController {
}
// コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
- //
+ //
+ bool inError=false;
+ bool isRunningBackgroundGPS=false;
+
@override
void onInit() async {
super.onInit();
@@ -1225,7 +1229,53 @@ class DestinationController extends GetxController {
handleLocationUpdate(locationMarkerPosition);
//}
}, onError: (err) {
- print("Location Error: $err");
+ if(inError==false){
+ inError = true;
+ debugPrint("Location Error: $err");
+ // エラーが発生した場合、locationMarkerPositionStreamControllerにエラーを追加します。
+ locationController.locationMarkerPositionStreamController.addError(err);
+
+ // ここにエラー発生時の処理を追加します。
+ if (err is LocationServiceDisabledException) {
+ // 位置情報サービスが無効になっている場合の処理
+ print('Location services are disabled');
+ Get.snackbar(
+ 'エラー',
+ '位置情報サービスが無効になっています。設定画面から位置情報サービスを有効にして下さい。不明な場合にはエンジニアスタッフにお問い合わせください。',
+ backgroundColor: Colors.red,
+ colorText: Colors.white,
+ duration: Duration(seconds: 3),
+ );
+ inError = false;
+ } else if (err is PermissionDeniedException) {
+ // 位置情報の権限がない場合の処理
+ print('Location permissions are denied');
+ Get.snackbar(
+ 'エラー',
+ '位置情報サービスが許可されていません。設定画面から岐阜ナビの位置情報サービスを許可して下さい。不明な場合にはエンジニアスタッフにお問い合わせください。',
+ backgroundColor: Colors.red,
+ colorText: Colors.white,
+ duration: Duration(seconds: 3),
+ );
+ inError = false;
+ } else {
+ // その他のエラーの場合の処理
+ print('Location Error: $err');
+ Get.snackbar(
+ 'エラー',
+ '位置情報サービスに問題が発生しました。位置情報サービスを再起動していますので少しお待ちください。',
+ backgroundColor: Colors.red,
+ colorText: Colors.white,
+ duration: Duration(seconds: 3),
+ );
+
+ // GPSデータのListenを再開する処理を追加
+ if( isRunningBackgroundGPS==false && inError ) {
+ restartGPS();
+ }
+ }
+ }
+ //print("Location Error: $err");
});
startGame();
@@ -1233,6 +1283,14 @@ class DestinationController extends GetxController {
checkGPSDataReceived();
}
+ void restartGPS(){
+ // GPSデータのListenを再開する処理を追加
+ Future.delayed(Duration(seconds: 5), () {
+ locationController.startPositionStream();
+ inError=false;
+ });
+ }
+
// コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
//
@override
@@ -1248,18 +1306,23 @@ class DestinationController extends GetxController {
//
// 要検討:GPSデータの追加に失敗した場合のエラーハンドリングを追加することをお勧めします。
//
+ double prevLat = 0.0; // 直前の位置
+ double prevLon = 0.0;
+
void handleLocationUpdate(LocationMarkerPosition? position) async {
try {
//final DestinationController destinationController = Get.find();
//final signalStrength = locationController.getGpsSignalStrength();
okToUseGPS = false;
- double prevLat = currentLat; // 直前の位置
- double prevLon = currentLon;
if (position != null) {
currentLat = position.latitude;
currentLon = position.longitude;
+ if( prevLat==0.0 ){
+ prevLat = currentLat;
+ prevLon = currentLon;
+ }
lastValidGPSLocation = LatLng(currentLat, currentLon);
okToUseGPS = true;
lastGPSDataReceivedTime = DateTime.now();
@@ -1316,7 +1379,8 @@ class DestinationController extends GetxController {
Duration difference = lastGPSCollectedTime.difference(DateTime.now())
.abs();
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過(普通に歩くスピード)
- if (difference.inSeconds >= 10 && distanceToDest >= 10) {
+ //debugPrint("時間差:${difference.inSeconds}, 距離差:${distanceToDest}");
+ if (difference.inSeconds >= 10 || distanceToDest >= 30) {
// print(
// "^^^^^^^^ GPS data collected ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}, ^^^ ${position.latitude}, ${position.longitude}");
@@ -1333,11 +1397,14 @@ class DestinationController extends GetxController {
if (isInRog.value) {
await addGPStoDB(position.latitude, position.longitude);
lastGPSCollectedTime = DateTime.now();
+ prevLat = position.latitude;
+ prevLon = position.longitude;
+ debugPrint("フロントエンドでのGPS保存(時間差:${difference.inSeconds}, 距離差:${distanceToDest}) : Time=${lastGPSCollectedTime}");
}
}
}
} catch(e) {
- debugPrint("Error: ${e}");
+ debugPrint("handleLocationUpdate Error: ${e}");
} finally {
/* Akira , 2024-4-5
if (position != null &&
diff --git a/lib/pages/gps/gps_page.dart b/lib/pages/gps/gps_page.dart
index a8f17e5..088c21b 100644
--- a/lib/pages/gps/gps_page.dart
+++ b/lib/pages/gps/gps_page.dart
@@ -72,6 +72,7 @@ class _GpsPageState extends State {
],
)),
),
+ /*
Container(
color: Colors.transparent,
child: i.is_checkin == 1
@@ -93,6 +94,8 @@ class _GpsPageState extends State {
color: Colors.black,
))
: Container()),
+
+ */
],
);
}
diff --git a/lib/pages/index/index_controller.dart b/lib/pages/index/index_controller.dart
index 503a36a..9d91d8d 100644
--- a/lib/pages/index/index_controller.dart
+++ b/lib/pages/index/index_controller.dart
@@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:flutter_polyline_points/flutter_polyline_points.dart';
import 'package:geojson_vi/geojson_vi.dart';
+import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:rogapp/model/destination.dart';
@@ -120,11 +121,76 @@ class IndexController extends GetxController {
}
}
+ Future _checkLocationPermission() async {
+ if (Get.context == null) {
+ debugPrint('Get.context is null in _checkLocationPermission');
+ return;
+ }
+ LocationPermission permission = await Geolocator.checkPermission();
+ //permission = await Geolocator.requestPermission();
+ if (permission == LocationPermission.denied) {
+ debugPrint('GPS : Denied');
+ await showLocationPermissionDeniedDialog();
+ } else if (permission == LocationPermission.deniedForever) {
+ debugPrint('GPS : Denied forever');
+ await showLocationPermissionDeniedDialog();
+ }else if (permission == LocationPermission.whileInUse){
+ debugPrint('GPS : While-In-Use');
+ await showLocationPermissionDeniedDialog();
+
+ }else{
+ debugPrint("Permission is no problem....");
+ }
+ }
+
+
+ // 追加
+ Future showLocationPermissionDeniedDialog() async {
+ if (Get.context != null) {
+ print('Showing location permission denied dialog');
+ await showDialog(
+ context: Get.context!,
+ barrierDismissible: false,
+ builder: (BuildContext context) {
+ return WillPopScope(
+ onWillPop: () async => false,
+ child: AlertDialog(
+ title: Text('位置情報の許可が必要です'),
+ content: Text('設定>プライバシーとセキュリティ>位置情報サービス を開いて、岐阜ナビを探し、「位置情報の許可」を「常に」にして下さい。'),
+ actions: [
+ TextButton(
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ child: Text('OK'),
+ ),
+ ],
+ ),
+ );
+ },
+ );
+ } else {
+ print('Get.context is null in showLocationPermissionDeniedDialog');
+ // Get.contextがnullの場合の処理
+ print('Location permission denied, but context is null');
+ }
+ }
+
+
@override
void onInit() {
_connectivitySubscription =
_connectivity.onConnectivityChanged.listen(_updateConnectionStatus);
super.onInit();
+
+ print('IndexController onInit called'); // デバッグ用の出力を追加
+
+ }
+
+ void checkPermission()
+ {
+ debugPrint("MapControllerの初期化が完了したら、位置情報の許可をチェックする");
+ _checkLocationPermission();
}
@override
@@ -425,9 +491,11 @@ class IndexController extends GetxController {
// 2024-04-03 Akira .. Update the code . See ticket 2800.
//
// 2024-4-8 Akira : See 2809
- // IndexControllerクラスでは、Future.delayedの呼び出しをunawaitedで囲んで、非同期処理の結果を待たずに先に進むようにしました。これにより、メモリリークを防ぐことができます
+ // IndexControllerクラスでは、Future.delayedの呼び出しをunawaitedで囲んで、
+ // 非同期処理の結果を待たずに先に進むようにしました。これにより、メモリリークを防ぐことができます
//
- // 要検討:Future.delayedを使用して非同期処理を待たずに先に進むようにしていますが、これによってメモリリークが発生する可能性があります。非同期処理の結果を適切に処理することを検討してください。
+ // 要検討:Future.delayedを使用して非同期処理を待たずに先に進むようにしていますが、
+ // これによってメモリリークが発生する可能性があります。非同期処理の結果を適切に処理することを検討してください。
//
void loadLocationsBound() async {
if (isCustomAreaSelected.value == true) {
@@ -437,11 +505,6 @@ class IndexController extends GetxController {
// MapControllerの初期化が完了するまで待機
await waitForMapControllerReady();
- // Akira 追加:2024-4-6 #2800
- //await waitForMapControllerReady(); // MapControllerの初期化が完了するまで待機
- // Akira 追加:2024-4-6 #2800
- // ==> remove 2024-4-8
-
locations.clear();
String cat = currentCat.isNotEmpty ? currentCat[0] : "";
if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
@@ -458,14 +521,16 @@ class IndexController extends GetxController {
*/
LatLngBounds bounds = mapController.bounds!;
+ if (bounds == null) {
+ // MapControllerの初期化が完了していない場合は処理を行わない
+ return;
+ }
+
currentBound.clear();
currentBound.add(bounds);
isLoading.value = true; // ローディング状態をtrueに設定
-// unawaited( Future.delayed(const Duration(seconds: 1), () async {
-// remove
-
//print("bounds --- (${bounds.southWest.latitude},${bounds.southWest.longitude}),(${bounds.northWest.latitude},${bounds.northWest.longitude}),(${bounds.northEast.latitude},${bounds.northEast.longitude}),(${bounds.southEast.latitude},${bounds.southEast.longitude})");
// 要検討:APIからのレスポンスがnullの場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
@@ -481,6 +546,7 @@ class IndexController extends GetxController {
bounds.southEast.longitude,
cat
);
+ /*
if (value == null) {
// APIからのレスポンスがnullの場合
print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード");
@@ -491,8 +557,33 @@ class IndexController extends GetxController {
} // 追加
return;
}
+ */
isLoading.value = false; // ローディング状態をfalseに設定
+ if (value == null) {
+ // APIからのレスポンスがnullの場合
+ print("LocationService.loadLocationsBound からの回答がnullです");
+ } else {
+ if (value.features.isEmpty) {
+ if (showPopup == false) {
+ return;
+ }
+ Get.snackbar(
+ "Too many Points",
+ "please zoom in",
+ backgroundColor: Colors.yellow,
+ colorText: Colors.white,
+ icon: const Icon(Icons.assistant_photo_outlined, size: 40.0, color: Colors.blue),
+ snackPosition: SnackPosition.TOP,
+ duration: const Duration(seconds: 3),
+ );
+ showPopup = false;
+ }
+ if (value.features.isNotEmpty) {
+ locations.add(value);
+ }
+ }
+ /*
if (value != null && value.features.isEmpty) {
if (showPopup == false) {
return;
@@ -513,6 +604,7 @@ class IndexController extends GetxController {
if (value != null && value.features.isNotEmpty) {
locations.add(value);
}
+ */
} catch ( e) {
print("Error in loadLocationsBound: $e");
// エラーが発生した場合のリトライ処理や適切なエラーメッセージの表示を行う
diff --git a/lib/pages/index/index_page.dart b/lib/pages/index/index_page.dart
index bf55333..f2b1e8d 100644
--- a/lib/pages/index/index_page.dart
+++ b/lib/pages/index/index_page.dart
@@ -104,6 +104,7 @@ class IndexPage extends GetView {
// タップすることでGPS信号の強弱をシミュレーションできるようにする
// Akira 2024-4-5
//
+ /*
Obx(() {
if (locationController.isSimulationMode) {
return DropdownButton(
@@ -129,6 +130,8 @@ class IndexPage extends GetView {
);
}
}),
+
+ */
/*
Obx(() => locationController.isSimulationMode
? DropdownButton(
diff --git a/lib/utils/location_controller.dart b/lib/utils/location_controller.dart
index f43850f..0e261a5 100644
--- a/lib/utils/location_controller.dart
+++ b/lib/utils/location_controller.dart
@@ -49,7 +49,7 @@ class LocationController extends GetxController {
//===== Akira Added 2024-4-9 start
// GPSシミュレーション用の変数を追加 ===> 本番では false にする。
- bool isSimulationMode = true;
+ bool isSimulationMode = false;
// GPS信号強度をシミュレートするための変数
final Rx _simulatedSignalStrength = Rx('low');
@@ -93,6 +93,7 @@ class LocationController extends GetxController {
return 'low';
}
final accuracy = position.accuracy;
+ //debugPrint("getGpsSignalStrength : ${accuracy}");
if(isSimulationMode){
return _simulatedSignalStrength.value; // GPS信号強度シミュレーション
}else {
@@ -346,6 +347,7 @@ class LocationController extends GetxController {
}
}
+
void handleLocationUpdate(LocationMarkerPosition? position) async {
if (position != null) {
//debugPrint("position = ${position}");
diff --git a/lib/widgets/map_widget.dart b/lib/widgets/map_widget.dart
index 0c4d931..ba4a817 100644
--- a/lib/widgets/map_widget.dart
+++ b/lib/widgets/map_widget.dart
@@ -70,9 +70,12 @@ class _MapWidgetState extends State with WidgetsBindingObserver {
// MapControllerの初期化が完了するまで待機
WidgetsBinding.instance.addPostFrameCallback((_) {
+ debugPrint("MapControllerの初期化が完了");
setState(() {
indexController.isMapControllerReady.value = true;
});
+ // MapControllerの初期化が完了したら、IndexControllerのonInitを呼び出す
+ indexController.checkPermission();
});
late MapResetController mapResetController = MapResetController();