import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:geolocator/geolocator.dart'; import 'package:latlong2/latlong.dart'; //import 'package:gifunavi/widgets/debug_widget.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:gifunavi/pages/destination/destination_controller.dart'; import 'package:gifunavi/pages/permission/permission.dart'; // LocationControllerクラスは、GetxControllerを継承したクラスであり、位置情報の管理を担当しています。 // LocationControllerは以下の機能を提供しています。 // LocationControllerは、アプリ全体で位置情報を一元管理するための重要なコンポーネントです。 // 他のコンポーネントは、LocationControllerから位置情報を取得し、位置情報に関連する機能を実装することができます。 // // Features: // * 現在の位置情報を保持し、他のコンポーネントからアクセスできるようにします。 // * 位置情報のストリームを管理し、位置情報の更新を監視します。 // * 位置情報サービスの有効性と権限の確認を行い、適切な処理を行います。 // * 位置情報のストリームを開始、停止、再開する機能を提供します。 // * 位置マーカーの位置情報をStreamControllerを通じて他のコンポーネントに通知します。 // // Logic: // 1. startPositionStreamメソッドで、Geolocator.getPositionStreamを使用して位置情報のストリームを開始します。 // 2. ストリームから位置情報を受信すると、LocationMarkerPositionオブジェクトを作成し、locationMarkerPositionStreamControllerに追加します。 // 3. 位置情報が取得できなかった場合や、エラーが発生した場合は、適切な処理を行います。 // 4. stopPositionStreamメソッドで、位置情報のストリームを一時停止することができます。 // 5. resumePositionStreamメソッドで、一時停止中の位置情報のストリームを再開することができます。 // 6. onCloseメソッドで、コントローラーのクローズ時に位置情報のストリームをキャンセルします。 // class LocationController extends GetxController { // Reactive variable to hold the current position Rx currentPosition = Rx(null); // 現在の位置情報を保持するReactive変数です。Rx型で宣言されています。 // Subscription to the position stream StreamSubscription? positionStream; // 位置情報のストリームを保持する変数です。StreamSubscription型で宣言されています。 LatLng? lastValidLocation; DateTime lastGPSDataReceivedTime = DateTime.now(); // 最後にGPSデータを受け取った時刻 bool gpsDebugMode = true; /* // GPSシミュレーション用のメソッドを追加 void setSimulationMode(bool value) { isSimulationMode = value; } // ====== Akira , GPS信号強度をシミュレート ==== ここから // //===== Akira Added 2024-4-9 start // GPSシミュレーション用の変数を追加 ===> 本番では false にする。 bool isSimulationMode = false; // GPS信号強度をシミュレートするための変数 final Rx _simulatedSignalStrength = Rx('high'); // GPS信号強度をシミュレートするための関数 void setSimulatedSignalStrength(String strength) { if( strength!='real') { isSimulationMode = true; _simulatedSignalStrength.value = strength; latestSignalStrength.value = strength; }else{ isSimulationMode = false; _simulatedSignalStrength.value = strength; } } // シミュレートされた信号強度を取得するための関数 String getSimulatedSignalStrength() { //debugPrint("strength : ${_simulatedSignalStrength.value}"); return _simulatedSignalStrength.value; } */ // // ====== Akira , GPS信号強度をシミュレート ==== ここまで // GPS信号が弱い場合のフラグ. 本番では、false,high にする。 bool isGpsSignalWeak = false; RxString latestSignalStrength = 'high'.obs; //final _latestSignalStrength = 'low'.obs; // 初期値を設定 //String get latestSignalStrength => _latestSignalStrength.value; Stream get gpsSignalStrengthStream => latestSignalStrength.stream; bool isRunningBackgroundGPS=false; int activeEngineCount = 0; // GPS信号の強弱を判断するメソッドを追加. 10m 以内:強、30m以内:中、それ以上:弱 // String getGpsSignalStrength(Position? position) { if (isSimulationMode.value) { return getSimulatedSignalStrength(); } if (position == null) { //gpsDebugMode ? debugPrint("getGpsSignalStrength position is null.") : null; latestSignalStrength.value = "low"; isGpsSignalWeak = true; return 'low'; } final accuracy = position.accuracy; //gpsDebugMode ? debugPrint("getGpsSignalStrength : ${accuracy}") : null; /* if(isSimulationMode){ return _simulatedSignalStrength.value; // GPS信号強度シミュレーション }else { */ if (accuracy <= 10) { latestSignalStrength.value = "high"; isGpsSignalWeak = false; return 'high'; } else if (accuracy <= 50) { latestSignalStrength.value = "medium"; isGpsSignalWeak = false; return 'medium'; } else { latestSignalStrength.value = "low"; isGpsSignalWeak = true; return 'low'; } // } } // 現在位置を調整するメソッドを追加 LatLng? adjustCurrentLocation(Position? position) { if (position == null) { // positionがnullなら、lastValidLocationを使用する。 if( lastValidLocation!=null ) { debugPrint("== 現在位置なし。最後の位置を使用 =="); //debugPrint("=== adjustCurrentLocation (Position:Null and using LastValidLocation ${lastValidLocation})==="); return LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude); }else { debugPrint("== 現在位置なし。最後の位置も無し =="); //print("=== adjustCurrentLocation (Position:Null and No LastValidLocation ... )==="); return null; } //return lastValidLocation ?? LatLng(0, 0); } final signalStrength = getGpsSignalStrength(position); if (signalStrength == 'high' || signalStrength == 'medium') { debugPrint("== 信号強度 ${signalStrength} ==> 最新位置を使用 =="); //debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )==="); lastValidLocation = LatLng(position.latitude, position.longitude); }else{ debugPrint("== 信号強度 ${signalStrength} ==> 最後の位置を使用 =="); } return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude); } //===== Akira Added 2024-4-9 end final locationMarkerPositionStreamController = StreamController.broadcast(); // 位置マーカーの位置情報を送信するためのStreamControllerです。 // StreamController型で宣言されています。 bool isStreamPaused = false; // 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。 // 位置マーカーの位置情報のストリームを取得するゲッター関数です。 // locationMarkerPositionStreamController.streamを返します。 // Stream get locationMarkerPositionStream => locationMarkerPositionStreamController.stream; // コントローラーの初期化時に呼び出されるライフサイクルメソッドです。 // startPositionStreamメソッドを呼び出して、位置情報のストリームを開始します。 // @override void onInit() { super.onInit(); // Start listening to location updates when the controller is initialized _initLocationService(); } Future _initLocationService() async { try { bool serviceEnabled; LocationPermission permission; serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { return Future.error('Location services are disabled.'); } permission = await Geolocator.checkPermission(); if (permission == LocationPermission.denied) { permission = await Geolocator.requestPermission(); if (permission == LocationPermission.denied) { return Future.error('Location permissions are denied'); } } if (permission == LocationPermission.deniedForever) { return Future.error( 'Location permissions are permanently denied, we cannot request permissions.'); } startPositionStream(); } catch( e ){ print('Error initializing location service: $e'); } } // 位置情報のストリームを開始するメソッドです。 // 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。 // 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。 // 既存の位置情報のストリームをキャンセルします。 // Geolocator.getPositionStreamを使用して、新しい位置情報のストリームを開始します。 // ストリームから受信した位置情報をlocationMarkerPositionStreamControllerに追加します。 // エラーが発生した場合は、locationMarkerPositionStreamControllerにエラーを追加します。 // ストリームが一時停止中の場合は、ストリームを再開します。 // // 2024-4-8 Akira : See 2809 // stopPositionStreamメソッドを追加して、既存のストリームをキャンセルするようにしました。また、ストリームが完了したらnullに設定し、エラー発生時にストリームをキャンセルするようにしました。 // void startPositionStream() { positionStream = Geolocator.getPositionStream( locationSettings: const LocationSettings( accuracy: LocationAccuracy.high, distanceFilter: 5, //10, 10mから5mに変更 ), ).listen((Position position) { currentPosition.value = position; //debugPrint("== startPositionStream: ${position} =="); locationMarkerPositionStreamController.add( LocationMarkerPosition( latitude: position.latitude, longitude: position.longitude, accuracy: position.accuracy, ), ); }); } void startPositionStream_old() async { // Check for location service and permissions before starting the stream // 位置情報サービスの有効性をチェックし、無効な場合はエラーハンドリングを行います。 // bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); if (!serviceEnabled) { Get.snackbar('位置情報サービスが無効です', '設定から位置情報サービスを有効にしてください'); return; } //await PermissionController.checkAndRequestPermissions(); // 位置情報の設定を行います。z11 // Set up the location options const locationOptions = LocationSettings(accuracy: LocationAccuracy.medium, distanceFilter: 0); // 既存の位置情報のストリームをキャンセルします。 await positionStream?.cancel(); // 新しい位置情報のストリームを開始します。 // positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen( (Position? position) { //gpsDebugMode ? debugPrint("Position = ${position}"):null; final signalStrength = getGpsSignalStrength(position); if (signalStrength == 'low') { isGpsSignalWeak = true; //gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null; } else { isGpsSignalWeak = false; //gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null; } final DestinationController destinationController = Get.find(); // ロゲ開始前、終了後、GPS=low の場合は更新しない。 // if (isGpsSignalWeak == false) { //if (destinationController.isInRog.value && isGpsSignalWeak == false) { final adjustedLocation = adjustCurrentLocation(position); if (adjustedLocation != null) { final locationMarkerPosition = LocationMarkerPosition( latitude: adjustedLocation.latitude, longitude: adjustedLocation.longitude, accuracy: position?.accuracy ?? 0, ); handleLocationUpdate(locationMarkerPosition); //locationMarkerPositionStreamController.add(locationMarkerPosition); // 位置データ送信 } else { // 位置情報が取得できなかった場合、 // locationMarkerPositionStreamControllerにnullを追加します。 locationMarkerPositionStreamController.add(null); // null 送信? //forceUpdateLocation(Position? position); } //}else{ // debugPrint("GPS処理対象外"); } }, onError: (e) { // エラーが発生した場合、locationMarkerPositionStreamControllerにエラーを追加します。 locationMarkerPositionStreamController.addError(e); }, onDone: () { positionStream = null; // ストリームが完了したらnullに設定 }, cancelOnError: true // エラー発生時にストリームをキャンセル ); // Resume stream if it was paused previously // ストリームが一時停止中の場合、ストリームを再開します。 // if (isStreamPaused) { isStreamPaused = false; positionStream!.resume(); } } // Method to stop the position stream // 位置情報のストリームを停止するメソッドです。 // positionStreamが存在する場合、ストリームを一時停止します。 // isStreamPausedフラグをtrueに設定します。 // void stopPositionStream() async { if (positionStream != null) { // updated Akira 2024-4-8 await positionStream!.cancel(); positionStream = null; //positionStream!.pause(); //isStreamPaused = true; } } // Method to resume the position stream // 位置情報のストリームを再開するメソッドです。 // positionStreamが存在し、ストリームが一時停止中の場合、ストリームを再開します。 // isStreamPausedフラグをfalseに設定します。 // void resumePositionStream() { if (positionStream != null && isStreamPaused) { positionStream!.resume(); isStreamPaused = false; } } void handleLocationUpdate(LocationMarkerPosition? position) async { //debugPrint("locationController.handleLocationUpdate"); try { if (position != null) { double currentLat = position.latitude; double currentLon = position.longitude; //debugPrint("Flutter: Received GPS signal. Latitude: $currentLat, Longitude: $currentLon"); //debugPrint("position = ${position}"); /* currentPosition.value = position; final locationMarkerPosition = LocationMarkerPosition( latitude: position.latitude, longitude: position.longitude, accuracy: position.accuracy, ); */ lastGPSDataReceivedTime = DateTime.now(); // 最後にGPS信号を受け取った時刻 locationMarkerPositionStreamController.add(position); }else{ gpsDebugMode ? debugPrint("Flutter: No GPS signal received."):null; } } catch( e ) { debugPrint("Flutter: Error in handleLocationUpdate: $e"); } } // このメソッドは、現在の位置情報を locationMarkerPositionStreamController に送信します。 // void forceUpdateLocation(Position? position) { if (position != null) { final adjustedLocation = adjustCurrentLocation(position); if (adjustedLocation != null) { final locationMarkerPosition = LocationMarkerPosition( latitude: adjustedLocation.latitude, longitude: adjustedLocation.longitude, accuracy: position.accuracy, ); locationMarkerPositionStreamController.add(locationMarkerPosition); } } } // コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。 // positionStreamをキャンセルします。 // @override void onClose() { // Cancel the position stream subscription when the controller is closed positionStream?.cancel(); super.onClose(); } // シミュレーションモードのフラグ RxBool isSimulationMode = RxBool(false); // シミュレーションモードを切り替えるための関数 void setSimulationMode(bool value) { isSimulationMode.value = value; } // GPS信号強度をシミュレートするための変数 final Rx _simulatedSignalStrength = Rx('high'); // GPS信号強度をシミュレートするための関数 void setSimulatedSignalStrength(String strength) { _simulatedSignalStrength.value = strength; } // シミュレートされた信号強度を取得するための関数 String getSimulatedSignalStrength() { return _simulatedSignalStrength.value; } }