Files
rog_app/lib/utils/location_controller.dart

416 lines
17 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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:rogapp/widgets/debug_widget.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
import 'package:rogapp/pages/destination/destination_controller.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<Position?> currentPosition = Rx<Position?>(null);
// 現在の位置情報を保持するReactive変数です。Rx<Position?>型で宣言されています。
// Subscription to the position stream
StreamSubscription<Position>? positionStream;
// 位置情報のストリームを保持する変数です。StreamSubscription<Position>型で宣言されています。
LatLng? lastValidLocation;
DateTime lastGPSDataReceivedTime = DateTime.now(); // 最後にGPSデータを受け取った時刻
bool gpsDebugMode = false;
/*
// GPSシミュレーション用のメソッドを追加
void setSimulationMode(bool value) {
isSimulationMode = value;
}
// ====== Akira , GPS信号強度をシミュレート ==== ここから
//
//===== Akira Added 2024-4-9 start
// GPSシミュレーション用の変数を追加 ===> 本番では false にする。
bool isSimulationMode = false;
// GPS信号強度をシミュレートするための変数
final Rx<String> _simulatedSignalStrength = Rx<String>('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<String> get gpsSignalStrengthStream => latestSignalStrength.stream;
bool isRunningBackgroundGPS=false;
int activeEngineCount = 0;
// GPS信号の強弱を判断するメソッドを追加. 10m 以内強、30m以内中、それ以上
//
String getGpsSignalStrength(Position? position) {
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 <= 30) {
latestSignalStrength.value = "medium";
isGpsSignalWeak = false;
return 'medium';
} else {
latestSignalStrength.value = "low";
isGpsSignalWeak = true;
return 'low';
}
// }
}
// 現在位置を調整するメソッドを追加
LatLng? adjustCurrentLocation(Position? position) {
if (position == null) {
if( lastValidLocation!=null ) {
//debugPrint("=== adjustCurrentLocation (Position:Null and using LastValidLocation ${lastValidLocation})===");
return LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
}else {
print("=== adjustCurrentLocation (Position:Null and No LastValidLocation ... )===");
return null;
}
//return lastValidLocation ?? LatLng(0, 0);
}
final signalStrength = getGpsSignalStrength(position);
if (signalStrength == 'high' || signalStrength == 'medium') {
//debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )===");
lastValidLocation = LatLng(position.latitude, position.longitude);
}
return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
}
//===== Akira Added 2024-4-9 end
final locationMarkerPositionStreamController =
StreamController<LocationMarkerPosition?>.broadcast();
// 位置マーカーの位置情報を送信するためのStreamControllerです。
// StreamController<LocationMarkerPosition?>型で宣言されています。
bool isStreamPaused = false; // 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。
// 位置マーカーの位置情報のストリームを取得するゲッター関数です。
// locationMarkerPositionStreamController.streamを返します。
//
Stream<LocationMarkerPosition?> get locationMarkerPositionStream =>
locationMarkerPositionStreamController.stream;
// コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
// startPositionStreamメソッドを呼び出して、位置情報のストリームを開始します。
//
@override
void onInit() {
super.onInit();
// Start listening to location updates when the controller is initialized
startPositionStream();
}
// 位置情報のストリームを開始するメソッドです。
// 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。
// 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。
// 既存の位置情報のストリームをキャンセルします。
// Geolocator.getPositionStreamを使用して、新しい位置情報のストリームを開始します。
// ストリームから受信した位置情報をlocationMarkerPositionStreamControllerに追加します。
// エラーが発生した場合は、locationMarkerPositionStreamControllerにエラーを追加します。
// ストリームが一時停止中の場合は、ストリームを再開します。
//
// 2024-4-8 Akira : See 2809
// stopPositionStreamメソッドを追加して、既存のストリームをキャンセルするようにしました。また、ストリームが完了したらnullに設定し、エラー発生時にストリームをキャンセルするようにしました。
//
void startPositionStream() async {
// Check for location service and permissions before starting the stream
// 位置情報サービスの有効性をチェックし、無効な場合はエラーハンドリングを行います。
//
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
if (!serviceEnabled) {
// Use GetX's context to show a dialog
Get.dialog(
AlertDialog(
title: const Text('Location Services Disabled'),
content: const Text(
'Please enable location services to continue using the app.'),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () {
// Close the dialog
Get.back();
// Optionally, you can direct the user to the settings page
// Geolocator.openLocationSettings();
},
),
],
),
barrierDismissible: false, // User must tap button to close dialog
);
return;
}
// 位置情報の権限をチェックし、拒否されている場合はエラーハンドリングを行います。
//
LocationPermission permission = await Geolocator.checkPermission();
if (permission == LocationPermission.denied) {
permission = await Geolocator.requestPermission();
if (permission == LocationPermission.denied) {
// Show a dialog if permissions are still denied
Get.dialog(
AlertDialog(
title: const Text('Location Permission Denied'),
content: const Text(
'This app requires location permissions to function properly. Please enable location permissions in your device settings.'),
actions: <Widget>[
TextButton(
child: const Text('OK'),
onPressed: () {
// Close the dialog
Get.back();
// Optionally, direct the user to the app settings
// Geolocator.openAppSettings();
},
),
],
),
barrierDismissible: false,
);
return;
}
}
if (permission == LocationPermission.deniedForever) {
// Show a dialog if permissions are permanently denied
Get.dialog(
AlertDialog(
title: const Text('Location Permission Needed'),
content: const Text(
'Location permissions have been permanently denied. Please open app settings to enable location permissions.'),
actions: <Widget>[
TextButton(
child: const Text('Open Settings'),
onPressed: () {
// Close the dialog and open app settings
Get.back();
Geolocator.openAppSettings();
},
),
],
),
barrierDismissible: false,
);
return;
}
// 位置情報の設定を行います。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<DestinationController>();
// ロゲ開始前、終了後、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();
}
}