From a0c1efc522be4d74b59447b8a6f22c02d16d580c Mon Sep 17 00:00:00 2001 From: AKIRA Date: Sun, 7 Apr 2024 10:56:51 +0900 Subject: [PATCH] =?UTF-8?q?=E3=81=BE=E3=81=A0#2800=E6=A4=9C=E8=A8=BC?= =?UTF-8?q?=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../destination/destination_controller.dart | 241 ++++++++++++++++-- lib/pages/index/index_controller.dart | 65 +++-- lib/pages/index/index_page.dart | 58 +++++ lib/utils/location_controller.dart | 69 ++++- lib/widgets/bottom_sheet_new.dart | 40 ++- lib/widgets/map_widget.dart | 3 + 6 files changed, 420 insertions(+), 56 deletions(-) diff --git a/lib/pages/destination/destination_controller.dart b/lib/pages/destination/destination_controller.dart index 9fefede..2dd5132 100644 --- a/lib/pages/destination/destination_controller.dart +++ b/lib/pages/destination/destination_controller.dart @@ -33,62 +33,116 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; +// 目的地に関連する状態管理とロジックを担当するクラスです。 +// class DestinationController extends GetxController { - late LocationSettings locationSettings; + late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。 - Timer? _GPStimer; + Timer? _GPStimer; // GPSタイマーを保持する変数です。 - var destinationCount = 0.obs; - List destinations = [].obs; - double currentLat = 0.0; + var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。 + List destinations = [].obs; // 目的地のリストを保持するObservable変数です。 + double currentLat = 0.0; // 現在の緯度と経度を保持する変数です。 double currentLon = 0.0; - DateTime lastGPSCollectedTime = DateTime.now(); + DateTime lastGPSCollectedTime = DateTime.now(); // 最後にGPSデータが収集された時刻を保持する変数です。 - bool shouldShowBottomSheet = true; + bool shouldShowBottomSheet = true; // ボトムシートを表示すべきかどうかを示すフラグです。 - static bool gps_push_started = false; + static bool gps_push_started = false; // ゲームの状態を示す静的変数です。 static bool game_started = false; static bool ready_for_goal = false; - bool skip_10s = false; + bool skip_10s = false; // 10秒間のスキップフラグを示す変数です。 - List currentSelectedDestinations = [].obs; + List currentSelectedDestinations = [].obs; // 現在選択されている目的地のリストを保持するObservable変数です。 - var isInCheckin = false.obs; + var isInCheckin = false.obs; // ゲームの状態を示すReactive変数です。 var isInRog = false.obs; var isAtStart = false.obs; var isAtGoal = false.obs; var isPhotoShoot = false.obs; - DateTime lastGoalAt = DateTime.now().subtract(const Duration(days: 1)); + + DateTime lastGoalAt = DateTime.now().subtract(const Duration(days: 1)); // 最後にゴールした時刻を保持する変数です。 //List rogainings = [].obs; - bool checkingIn = false; - var isGpsSelected = true.obs; - BuildContext? context; + bool checkingIn = false; // チェックイン中かどうかを示すフラグです。 + var isGpsSelected = true.obs; // GPSが選択されているかどうかを示すReactive変数です。 + BuildContext? context; // ビルドコンテキストを保持する変数です。 - List gps = ["-- stating --"].obs; + List gps = ["-- stating --"].obs; // GPSと位置情報の許可に関する情報を保持するObservable変数です。 List locationPermission = [" -- starting -- "].obs; - var travelMode = 0.obs; + var travelMode = 0.obs; // 移動モードを保持するReactive変数です。 - bool skipGps = false; + bool skipGps = false; // GPSをスキップするかどうかを示すフラグです。 - Map matrix = {}; + Map matrix = {}; // 行列データを保持する変数です。 - final photos = [].obs; + final photos = [].obs; // 写真のリストを保持するReactive変数です。 - final IndexController indexController = Get.find(); - final LocationController locationController = Get.put(LocationController()); - final DatabaseService dbService = DatabaseService(); + final IndexController indexController = Get.find(); // IndexControllerのインスタンスを保持する変数です。 + final LocationController locationController = Get.put(LocationController()); // LocationControllerのインスタンスを保持する変数です。 + final DatabaseService dbService = DatabaseService(); // DatabaseServiceのインスタンスを保持する変数です。 - int _start = 0; - int chekcs = 0; - var rogainingCounted = false.obs; + int _start = 0; // 開始時刻を保持する変数です。 + int chekcs = 0; // チェックポイントの数を保持する変数です。 + var rogainingCounted = false.obs; // ロゲイニングがカウントされたかどうかを示すReactive変数です。 + //==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5 + // + + bool kDebugMode = true; + + // シミュレーションモードのフラグ + RxBool isSimulationMode = RxBool(false); + + // シミュレーションモードを切り替えるための関数 + void toggleSimulationMode(bool value) { + isSimulationMode.value = value; + } + + // + // GPS信号の強弱を判断するメソッドを追加します。 + // + String getGpsSignalStrength() { + // デバッグモードかつシミュレーションモードの場合は、シミュレートされた信号強度を返す + if (kDebugMode && isSimulationMode.value) { + return locationController.getSimulatedSignalStrength(); + } + + // 通常モードの場合は、実際の信号強度を返す + final accuracy = locationController.currentPosition.value?.accuracy ?? double.infinity; + if (accuracy <= 10) { + return 'high'; + } else if (accuracy <= 30) { + return 'medium'; + } else { + return 'low'; + } + } + + // + //==== Akira .. GPS信号シミュレーション用 ======= ここまで + + // 日時をフォーマットされた文字列に変換する関数です。 + // String getFormatedTime(DateTime datetime) { return DateFormat('yyyy-MM-dd HH:mm:ss').format(datetime); } + // 追加:Akira 2024-4-5 + // GPS信号の精度が一定値以上の場合、GPS信号が弱いと判断する + // + bool isGpsSignalWeak() { + final accuracy = locationController.currentPosition.value?.accuracy; + if (accuracy == null) { + return true; // 位置情報が取得できていない場合、GPS信号が弱いと見なす + } + return accuracy > 50; + //return locationController.currentPosition.value?.accuracy ?? double.infinity > 50; + } + + // Destination festuretoDestination(GeoJSONFeature fs) { GeoJSONMultiPoint mp = fs.geometry as GeoJSONMultiPoint; LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]); @@ -121,6 +175,8 @@ class DestinationController extends GetxController { tags: fs.properties!["tags"]); } + // 指定された目的地の位置情報に基づいてタイマーを開始する関数です。 + // Future startTimerLocation(GeoJSONFeature fs, double distance) async { //print("---- in startTimer ----"); // print("---- is in rog is $is_in_rog ----"); @@ -136,6 +192,12 @@ class DestinationController extends GetxController { } } + // 指定された目的地に対してタイマーを開始する関数です。 + // 目的地の位置情報を取得し、チェックイン半径内にいるかどうかを確認します。 + // 写真撮影モードの場合は、ボトムシートを表示して写真撮影を行います。 + // 目的地がデータベースに存在しない場合は、新しい目的地としてデータベースに挿入します。 + // 目的地に応じて、チェックイン、ゴール、買い物ポイントの処理を行います。 + // Future startTimer(Destination d, double distance) async { //print("=== passed dest is ${d.location_id} ${d.checkedin} ===="); skipGps = true; @@ -173,8 +235,10 @@ class DestinationController extends GetxController { // print( // "---- checked in as ${indexController.currentDestinationFeature[0].checkedin.toString()} ----"); } else { - skipGps = false; - return; + // GPS信号が弱い場合でも、チェックインやゴールの処理を続行する + // comment out by Akira, 2024-4-5 + // skipGps = false; + // return; } if (isPhotoShoot.value == true) { @@ -425,6 +489,9 @@ class DestinationController extends GetxController { return; } + // ロゲイニングをリセットする関数です。 + // ゲームの状態をリセットし、データベースからデータを削除します。 + // Future resetRogaining({bool isgoal = false}) async { //print("----- resetting --------"); @@ -455,6 +522,8 @@ class DestinationController extends GetxController { dbService.updateDatabase(); } + // すべての目的地を削除する関数です。 + // void deleteAllDestinations() { DatabaseHelper db = DatabaseHelper.instance; db.deleteAllDestinations().then((value) { @@ -462,6 +531,8 @@ class DestinationController extends GetxController { }); } + // カメラを開いて写真を撮影する関数です。 + // void openCamera(BuildContext context, Destination? destination) { photos.clear(); Navigator.push( @@ -478,6 +549,8 @@ class DestinationController extends GetxController { ))); } + // ルートポイントを取得する関数です。 + // void getRoutePoints() { indexController.routePoints = []; indexController.routePointLenght.value = 0; @@ -488,6 +561,8 @@ class DestinationController extends GetxController { }); } + // 指定された緯度と経度に対応する目的地を取得する関数です。 + // Future getDestinationForLatLong(double lat, double long) async { for (final d in destinations) { if (lat == d.lat && long == d.lon) { @@ -497,6 +572,9 @@ class DestinationController extends GetxController { return null; } + // チェックインの呼び出しを行う関数です。 + // 指定された目的地に対してチェックインの処理を行います。 + // Future callforCheckin(Destination d) async { bool autoCheckin = d.auto_checkin == 0 ? false : true; //print("---- f- checkin ${d.sub_loc_id} ----"); @@ -573,6 +651,8 @@ class DestinationController extends GetxController { } } + // GPSデータをデータベースに追加する関数です。 + // Future addGPStoDB(double la, double ln, {isCheckin = 0}) async { //print("in addGPStoDB ${indexController.currentUser}"); try { @@ -594,6 +674,11 @@ class DestinationController extends GetxController { } } + // チェックインを確認する関数です。 + // ゲームが開始されていない場合は、ゲームを開始します。 + // 目的地のリストを走査し、現在位置がチェックイン半径内にある場合は、チェックインの処理を行います。 + // GPSデータの送信を開始します。 + // Future checkForCheckin() async { //print("--- Start of checkForCheckin function ---"); dbService.updateDatabase(); @@ -631,6 +716,8 @@ class DestinationController extends GetxController { } } + // GPSデータをサーバーにプッシュする関数です。 + // Future pushGPStoServer() async { // print( // "^^^^^^^^ ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}"); @@ -647,6 +734,8 @@ class DestinationController extends GetxController { } } + // ロゲイニングにデータを追加する関数です。 + // void addToRogaining(double lat, double lon, int destinationId) async { DatabaseHelper db = DatabaseHelper.instance; List d = await db.getDestinationById(destinationId); @@ -660,12 +749,16 @@ class DestinationController extends GetxController { saveGameState(); } + // 買い物ポイントをキャンセルする関数です。 + // Future cancelBuyPoint(Destination destination) async { DatabaseHelper db = DatabaseHelper.instance; await db.updateCancelBuyPoint(destination); populateDestinations(); } + // 指定されたパスの画像をギャラリーに保存する関数です。 + // _saveImageFromPath(String imagePath) async { // Read the image file from the given path File imageFile = File(imagePath); @@ -676,6 +769,8 @@ class DestinationController extends GetxController { //print("--- save result --- ${result}"); } + // 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。 + // Future makeBuyPoint(Destination destination, String imageurl) async { DatabaseHelper db = DatabaseHelper.instance; await db.updateBuyPoint(destination, imageurl); @@ -706,6 +801,8 @@ class DestinationController extends GetxController { } } + // チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。 + // Future makeCheckin( Destination destination, bool action, String imageurl) async { // print("~~~~ calling checkin function ~~~~"); @@ -754,11 +851,15 @@ class DestinationController extends GetxController { dbService.updateDatabase(); } + // チェックインを削除する関数です。 + // Future removeCheckin(int cp) { dbService.updateDatabase(); return ExternalService().removeCheckin(cp); } + // ゲームを開始する関数です。 + // Future startGame() async { //print("------ starting game ------"); if (game_started == false) { @@ -766,6 +867,8 @@ class DestinationController extends GetxController { } } + // コントローラーの初期化時に呼び出されるライフサイクルメソッドです。 + // @override void onInit() async { super.onInit(); @@ -781,11 +884,17 @@ class DestinationController extends GetxController { startGame(); } + // コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。 + // @override void onClose() { locationController.stopPositionStream(); } + // 位置情報の更新を処理する関数です。 + // 現在位置とスタート地点との距離を計算します。 + // 現在位置と前回の位置情報との距離と時間差を確認し、一定の条件を満たす場合はGPSデータをデータベースに追加します。 + // void handleLocationUpdate(LocationMarkerPosition? position) async { try { if (position != null) { @@ -814,14 +923,23 @@ class DestinationController extends GetxController { } } } finally { + /* Akira , 2024-4-5 if (position != null && (position.latitude != 0 && position.longitude != 0)) { currentLat = position.latitude; currentLon = position.longitude; } + */ + if (position != null) { + // 位置情報が取得できた場合、精度に関わらず最後の位置情報を更新 + currentLat = position.latitude; + currentLon = position.longitude; + } } } + // スタート地点までの距離を計算する関数です。 + // double distanceToStart() { if (indexController.locations.isEmpty) { return 1000000000; @@ -853,6 +971,8 @@ class DestinationController extends GetxController { return distanceToDest; } + // 強制チェックイン距離を取得する関数です。 + // int getForcedChckinDistance(Destination dest) { if (dest.checkin_radious == -1) { return 10000000000000000; @@ -883,11 +1003,15 @@ class DestinationController extends GetxController { return _retValue; } + // ユーザートークンを読み取る関数です。 + // readUserToken() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); indexController.userToken = prefs.getString("user_token"); } + // コントローラーの準備完了時に呼び出されるライフサイクルメソッドです。 + // @override void onReady() async { await readUserToken(); @@ -917,6 +1041,8 @@ class DestinationController extends GetxController { super.onReady(); } + // 地図の境界を修正する関数です。 + // void fixMapBound(String token) { //String _token = indexController.currentUser[0]["token"]; indexController.switchPage(AppPages.INDEX); @@ -937,6 +1063,43 @@ class DestinationController extends GetxController { }); } + +/* + void fixMapBound(String token) { + indexController.switchPage(AppPages.INDEX); + LocationService.getLocationsExt(token).then((value) { + if (value != null) { + LatLngBounds bnds = LatLngBounds( + LatLng(value[1], value[0]), + LatLng(value[3], value[2]), + ); + if (indexController.isMapControllerReady.value) { + indexController.mapController.fitBounds( + bnds, + ); + indexController.currentBound.clear(); + indexController.currentBound.add(bnds); + indexController.loadLocationsBound(); + centerMapToCurrentLocation(); + } else { + // MapControllerが初期化されるまで待機し、その後fitBoundsを実行 + WidgetsBinding.instance.addPostFrameCallback((_) { + indexController.mapController.fitBounds( + bnds, + ); + indexController.currentBound.clear(); + indexController.currentBound.add(bnds); + indexController.loadLocationsBound(); + centerMapToCurrentLocation(); + }); + } + } + }); + } +*/ + + // 地図を現在位置に中央揃えする関数です。 + // void centerMapToCurrentLocation() { assert(() { print("center is ${currentLon}, ${currentLon}"); @@ -948,6 +1111,8 @@ class DestinationController extends GetxController { } } + // 接続状態が変更されたときに呼び出される関数です。 + // void connectionChanged(String val) { //print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val'); Map res = {}; @@ -987,6 +1152,8 @@ class DestinationController extends GetxController { } } + // 位置情報の許可を確認する関数です。 + // void checkPermission() async { LocationPermission permission = await Geolocator.checkPermission(); if (permission != LocationPermission.whileInUse || @@ -997,6 +1164,8 @@ class DestinationController extends GetxController { } } + // IDに基づいて目的地を取得する関数です。 + // Destination? destinationById(int id) { Destination? d; //print("--- target des - $id ----"); @@ -1010,6 +1179,8 @@ class DestinationController extends GetxController { return d; } + // 目的地を削除する関数です。 + // void deleteDestination(Destination d) { //int id = destinations[index].location_id!; //print("---- index ${destinations[index].location_id!}-----"); @@ -1026,6 +1197,8 @@ class DestinationController extends GetxController { dbService.updateDatabase(); } + // データベースからすべての目的地を削除する関数です。 + // void deleteDBDestinations() { DatabaseHelper db = DatabaseHelper.instance; db.deleteAllDestinations().then((value) { @@ -1036,6 +1209,8 @@ class DestinationController extends GetxController { // ---------- database ------------------/// + // 目的地を追加する関数です。 + // void addDestinations(Destination dest) { DatabaseHelper db = DatabaseHelper.instance; db.getDestinationByLatLon(dest.lat!, dest.lon!).then((value) { @@ -1057,6 +1232,8 @@ class DestinationController extends GetxController { dbService.updateDatabase(); } + // 目的地の選択状態を切り替える関数です。 + // void toggleSelection(Destination dest) async { DatabaseHelper db = DatabaseHelper.instance; await db.toggleSelecttion(dest); @@ -1076,6 +1253,8 @@ class DestinationController extends GetxController { }); } + // ダイアログを表示する関数です。 + // buildShowDialog(BuildContext context) { return showDialog( context: context, @@ -1087,6 +1266,8 @@ class DestinationController extends GetxController { }); } + // 現在地点からの目的地の行列を計算する関数です。 + // void destinationMatrixFromCurrentPoint(List points) { //buildShowDialog(Get.context!); MatrixService.getDestinations(points).then((mat) { @@ -1112,6 +1293,8 @@ class DestinationController extends GetxController { }); } + // 目的地のリストを取得してObservable変数を更新する関数です。 + // void populateDestinations() { DatabaseHelper db = DatabaseHelper.instance; destinations.clear(); @@ -1126,6 +1309,8 @@ class DestinationController extends GetxController { }); } + // 目的地の順序を変更する関数です。 + // void makeOrder(Destination d, int dir) { DatabaseHelper db = DatabaseHelper.instance; db.updateOrder(d, dir).then((value) { diff --git a/lib/pages/index/index_controller.dart b/lib/pages/index/index_controller.dart index c3df902..e27f8ee 100644 --- a/lib/pages/index/index_controller.dart +++ b/lib/pages/index/index_controller.dart @@ -16,6 +16,7 @@ import 'package:rogapp/services/location_service.dart'; import 'package:rogapp/utils/database_helper.dart'; import 'package:shared_preferences/shared_preferences.dart'; + class IndexController extends GetxController { List locations = [].obs; List currentFeature = [].obs; @@ -42,8 +43,11 @@ class IndexController extends GetxController { var isCustomAreaSelected = false.obs; + RxBool isMapControllerReady = RxBool(false); // MapControllerの初期化状態を管理するフラグ + //final mapControllerReadyStream = Stream.value(false); // MapControllerの初期化状態を通知するためのストリーム + MapController mapController = MapController(); - MapController rogMapController = MapController(); + //MapController rogMapController = MapController(); String? userToken; @@ -166,8 +170,9 @@ class IndexController extends GetxController { AuthService.login(email, password).then((value) { print("------- logged in user details ######## $value ###### --------"); if (value.isNotEmpty) { - Navigator.pop(context); + // Navigator.pop(context); print("--------- user details login ----- $value"); + //await Future.delayed(const Duration(milliseconds: 500)); // Added Akira:2024-4-6, #2800 changeUser(value); } else { isLoading.value = false; @@ -283,7 +288,7 @@ class IndexController extends GetxController { }); } -/* +/* Old code void loadLocationsBound() { if (isCustomAreaSelected.value == true) { return; @@ -337,17 +342,33 @@ class IndexController extends GetxController { } */ + // 2024-04-03 Akira .. Update the code . See ticket 2800. // - void loadLocationsBound() { + void loadLocationsBound() async { if (isCustomAreaSelected.value == true) { return; } + + // Akira 追加:2024-4-6 #2800 + await waitForMapControllerReady(); // MapControllerの初期化が完了するまで待機 + // Akira 追加:2024-4-6 #2800 + locations.clear(); String cat = currentCat.isNotEmpty ? currentCat[0] : ""; if (currentCat.isNotEmpty && currentCat[0] == "-all-") { cat = ""; } +/* + // Akira Add 2024-4-6 + if( mapController.controller == null ) { + print("操作が完了する前にMapControllerまたはウィジェットが破棄されました。"); + isLoading.value = true; // ローディング状態をtrueに設定 + return; + } + // +*/ + LatLngBounds bounds = mapController.bounds!; currentBound.clear(); currentBound.add(bounds); @@ -370,24 +391,14 @@ class IndexController extends GetxController { cat ); if ( value == null ) { - // エラーハンドリング: APIからのレスポンスがnullの場合 - /* - Get.snackbar( - "Error", - "Failed to load locations. Please try again. === 1", - icon: const Icon(Icons.error, size: 40.0, color: Colors.red), - snackPosition: SnackPosition.TOP, - duration: const Duration(seconds: 3), - backgroundColor: Colors.yellow, - ); - */ // APIからのレスポンスがnullの場合 - DestinationController destinationController = Get.find(); // 追加 - final tk = currentUser[0]["token"]; // 追加 - if (tk != null) { // 追加 - destinationController.fixMapBound(tk); // 追加 - } // 追加 - // return; + print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード"); + //DestinationController destinationController = Get.find(); // 追加 + //final tk = currentUser[0]["token"]; // 追加 + //if (tk != null) { // 追加 + // destinationController.fixMapBound(tk); // 追加 + //} // 追加 + return; } isLoading.value = false; // ローディング状態をfalseに設定 @@ -412,6 +423,18 @@ class IndexController extends GetxController { }); } + + //===Akira 追加:2024-4-6 #2800 + Future waitForMapControllerReady() async { + if (!isMapControllerReady.value) { + await Future.doWhile(() async { + await Future.delayed(const Duration(milliseconds: 500)); + return !isMapControllerReady.value; + }); + } + } + //===Akira 追加:2024-4-6 #2800 + void setBound(LatLngBounds bounds) { currentBound.clear(); currentBound.add(bounds); diff --git a/lib/pages/index/index_page.dart b/lib/pages/index/index_page.dart index e94084d..2bd588a 100644 --- a/lib/pages/index/index_page.dart +++ b/lib/pages/index/index_page.dart @@ -11,19 +11,41 @@ import 'package:rogapp/utils/database_gps.dart'; import 'package:rogapp/utils/database_helper.dart'; import 'package:rogapp/widgets/list_widget.dart'; import 'package:rogapp/widgets/map_widget.dart'; +import 'package:rogapp/utils/location_controller.dart'; +// index_page.dartファイルの主な内容です。 +// このファイルは、アプリのメインページのUIを構築し、各機能へのナビゲーションを提供しています。 +// また、IndexControllerとDestinationControllerを使用して、状態管理と各種機能の実装を行っています。 +// +// MapWidgetとListWidgetは、それぞれ別のファイルで定義されているウィジェットであり、マップモードとリストモードの表示を担当しています。 +// +// 全体的に、index_page.dartはアプリのメインページの構造を定義し、他のコンポーネントやページへの橋渡しを行っているファイルです。 +// + + +// IndexPageクラスは、GetViewを継承したStatelessWidgetです。このクラスは、アプリのメインページを表すウィジェットです。 +// class IndexPage extends GetView { IndexPage({Key? key}) : super(key: key); + // IndexControllerとDestinationControllerのインスタンスを取得しています。 + // + final LocationController locationController = Get.find(); final IndexController indexController = Get.find(); final DestinationController destinationController = Get.find(); + // buildメソッドは、ウィジェットのUIを構築するメソッドです。 + // ここでは、WillPopScopeウィジェットを使用して、端末の戻るボタンが押された際の動作を制御しています。 + // @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async => false, child: Scaffold( + // + // Scaffoldウィジェットを使用して、アプリのメインページのレイアウトを構築しています。 + // drawer: DrawerPage(), appBar: AppBar( title: Text("add_location".tr), @@ -35,6 +57,10 @@ class IndexPage extends GetView { // }, // icon: const Icon(Icons.ten_k_sharp)), + // + // AppBarには、タイトルとアクションアイコンが含まれています。 + // アクションアイコンには、GPSデータの表示、履歴の表示、マップの更新、検索などの機能が含まれています。 + // IconButton( onPressed: () async { // GpsDatabaseHelper db = GpsDatabaseHelper.instance; @@ -75,6 +101,28 @@ class IndexPage extends GetView { ), ), //CatWidget(indexController: indexController,), + // + // デバッグ時のみリロードボタンの横にGPS信号レベルの設定ボタンを設置し、 + // タップすることでGPS信号の強弱をシミュレーションできるようにする + // Akira 2024-4-5 + // + Obx( + () => destinationController.isSimulationMode.value + ? DropdownButton( + value: locationController.getSimulatedSignalStrength(), + onChanged: (value) { + locationController.setSimulatedSignalStrength(value!); + }, + items: ['high', 'medium', 'low'] + .map>((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + ) + : Container(), + ), ], ), // bottomNavigationBar: BottomAppBar( @@ -139,11 +187,18 @@ class IndexPage extends GetView { // ], // ), // ), + + // + // マップモードとリストモードを切り替えるためのボタンです。 + // floatingActionButton: FloatingActionButton( onPressed: () { indexController.toggleMode(); }, elevation: 1.0, + // + // Obxウィジェットを使用して、indexController.mode.valueの値に基づいて、MapWidgetまたはListWidgetを表示しています。 + // child: Obx( () => indexController.mode.value == 0 ? const Image(image: AssetImage('assets/images/list2.png')) @@ -151,6 +206,9 @@ class IndexPage extends GetView { ), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + // + // bodyには、SafeAreaウィジェットを使用して、画面の安全な領域内にUIを構築しています。 + // body: SafeArea( child: Column( children: [ diff --git a/lib/utils/location_controller.dart b/lib/utils/location_controller.dart index 2c1c383..8108f88 100644 --- a/lib/utils/location_controller.dart +++ b/lib/utils/location_controller.dart @@ -5,28 +5,78 @@ import 'package:geolocator/geolocator.dart'; import 'package:rogapp/widgets/debug_widget.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; +// LocationControllerクラスは、GetxControllerを継承したクラスであり、位置情報の管理を担当しています。 +// LocationControllerは以下の機能を提供しています。 +// LocationControllerは、アプリ全体で位置情報を一元管理するための重要なコンポーネントです。 +// 他のコンポーネントは、LocationControllerから位置情報を取得し、位置情報に関連する機能を実装することができます。 +// +// * 現在の位置情報を保持し、他のコンポーネントからアクセスできるようにします。 +// * 位置情報のストリームを管理し、位置情報の更新を監視します。 +// * 位置情報サービスの有効性と権限の確認を行い、適切な処理を行います。 +// * 位置情報のストリームを開始、停止、再開する機能を提供します。 +// * 位置マーカーの位置情報をStreamControllerを通じて他のコンポーネントに通知します。 +// class LocationController extends GetxController { // Reactive variable to hold the current position - Rx currentPosition = Rx(null); + Rx currentPosition = Rx(null); + // 現在の位置情報を保持するReactive変数です。Rx型で宣言されています。 // Subscription to the position stream - StreamSubscription? positionStream; + StreamSubscription? positionStream; + // 位置情報のストリームを保持する変数です。StreamSubscription型で宣言されています。 final locationMarkerPositionStreamController = StreamController.broadcast(); + // 位置マーカーの位置情報を送信するためのStreamControllerです。 + // StreamController型で宣言されています。 - bool isStreamPaused = false; + bool isStreamPaused = false; // 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。 + // ====== Akira , GPS信号強度をシミュレート ==== ここから + // + + // GPS信号強度をシミュレートするための変数 + Rx _simulatedSignalStrength = Rx('high'); + + // GPS信号強度をシミュレートするための関数 + void setSimulatedSignalStrength(String strength) { + _simulatedSignalStrength.value = strength; + } + + // シミュレートされた信号強度を取得するための関数 + String getSimulatedSignalStrength() { + return _simulatedSignalStrength.value; + } + + // + // ====== Akira , GPS信号強度をシミュレート ==== ここから + + // 位置マーカーの位置情報のストリームを取得するゲッター関数です。 + // locationMarkerPositionStreamController.streamを返します。 + // Stream get locationMarkerPositionStream => locationMarkerPositionStreamController.stream; + // コントローラーの初期化時に呼び出されるライフサイクルメソッドです。 + // startPositionStreamメソッドを呼び出して、位置情報のストリームを開始します。 + // @override void onInit() { super.onInit(); // Start listening to location updates when the controller is initialized - startPositionStream(); + startPositionStream(); + } + // 位置情報のストリームを開始するメソッドです。 + // 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。 + // 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。 + // 既存の位置情報のストリームをキャンセルします。 + // Geolocator.getPositionStreamを使用して、新しい位置情報のストリームを開始します。 + // ストリームから受信した位置情報をlocationMarkerPositionStreamControllerに追加します。 + // エラーが発生した場合は、locationMarkerPositionStreamControllerにエラーを追加します。 + // ストリームが一時停止中の場合は、ストリームを再開します。 + // void startPositionStream() async { // Check for location service and permissions before starting the stream bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); @@ -138,6 +188,10 @@ class LocationController extends GetxController { } // Method to stop the position stream + // 位置情報のストリームを停止するメソッドです。 + // positionStreamが存在する場合、ストリームを一時停止します。 + // isStreamPausedフラグをtrueに設定します。 + // void stopPositionStream() { if (positionStream != null) { positionStream!.pause(); @@ -146,6 +200,10 @@ class LocationController extends GetxController { } // Method to resume the position stream + // 位置情報のストリームを再開するメソッドです。 + // positionStreamが存在し、ストリームが一時停止中の場合、ストリームを再開します。 + // isStreamPausedフラグをfalseに設定します。 + // void resumePositionStream() { if (positionStream != null && isStreamPaused) { positionStream!.resume(); @@ -153,6 +211,9 @@ class LocationController extends GetxController { } } + // コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。 + // positionStreamをキャンセルします。 + // @override void onClose() { // Cancel the position stream subscription when the controller is closed diff --git a/lib/widgets/bottom_sheet_new.dart b/lib/widgets/bottom_sheet_new.dart index 15a2221..77507c1 100644 --- a/lib/widgets/bottom_sheet_new.dart +++ b/lib/widgets/bottom_sheet_new.dart @@ -18,6 +18,10 @@ import 'package:rogapp/widgets/bottom_sheet_controller.dart'; import 'package:rogapp/widgets/debug_widget.dart'; import 'package:url_launcher/url_launcher.dart'; +// BottomSheetNewは、StatelessWidgetを継承したクラスで、目的地の詳細情報を表示するボトムシートのUIを構築します。 +// コンストラクタでは、destination(目的地オブジェクト)とisAlreadyCheckedIn(すでにチェックイン済みかどうかのフラグ)を受け取ります。 +// buildメソッドでは、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。 +// class BottomSheetNew extends GetView { BottomSheetNew( {this.isAlreadyCheckedIn = false, Key? key, required this.destination}) @@ -26,9 +30,12 @@ class BottomSheetNew extends GetView { final IndexController indexController = Get.find(); final DestinationController destinationController = Get.find(); - final Destination destination; - final bool isAlreadyCheckedIn; + final Destination destination; // 目的地オブジェクト + final bool isAlreadyCheckedIn; // すでにチェックイン済みかどうかのフラグ + // 目的地の画像を取得するためのメソッドです。 + // indexController.rogModeの値に基づいて、適切な画像を返します。画像が見つからない場合は、デフォルトの画像を返します。 + // Image getImage() { String serverUrl = ConstValues.currentServer(); @@ -95,10 +102,16 @@ class BottomSheetNew extends GetView { } } + // URLを開くためのメソッドです。 + // url_launcherパッケージを使用して、指定されたURLを開きます。 + // void _launchURL(url) async { if (!await launchUrl(url)) throw 'Could not launch $url'; } + // 指定されたlocationidが目的地リストに含まれているかどうかを確認するメソッドです。 + // destinationController.destinationsリストを走査し、locationidが一致する目的地があるかどうかを返します。 + // bool isInDestination(String locationid) { int lid = int.parse(locationid); if (destinationController.destinations @@ -110,6 +123,10 @@ class BottomSheetNew extends GetView { } } + // アクションボタン(チェックイン、ゴールなど)を表示するためのメソッドです。 + // 現在の状態に基づいて、適切なボタンを返します。 + // ボタンがタップされたときの処理も含まれています。 + // Widget getActionButton(BuildContext context, Destination destination) { assert(() { print("getActionButton ${destinationController.rogainingCounted.value}"); @@ -130,7 +147,8 @@ class BottomSheetNew extends GetView { LatLng(cdest.lat!, cdest.lon!)); if (destinationController.rogainingCounted.value == true && - destinationController.distanceToStart() <= 500 && + // destinationController.distanceToStart() <= 500 && ... GPS信号が弱い時でもOKとする。 + (destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) && destination.cp == -1 && DestinationController.ready_for_goal == true) { //goal @@ -171,6 +189,7 @@ class BottomSheetNew extends GetView { onPressed: () async { // Check conditions to show confirmation dialog if (destinationController.isInRog.value == false && + (destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) && //追加 Akira 2024-4-5 destination.cp == -1 && destinationController.rogainingCounted.value == false) { print("counted ${destinationController.rogainingCounted.value}"); @@ -240,6 +259,7 @@ class BottomSheetNew extends GetView { return Container(); } + // 継承元のbuild をオーバーライドし、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。 @override Widget build(BuildContext context) { //print("to start ${destinationController.distanceToStart()}"); @@ -253,6 +273,9 @@ class BottomSheetNew extends GetView { return detailsSheet(context); } + // 指定された目的地がすでにチェックイン済みかどうかを確認するメソッドです。 + // DatabaseHelperを使用して、目的地の位置情報に基づいてデータベースを検索し、結果を返します。 + // Future isDestinationCheckedIn(Destination d) async { DatabaseHelper db = DatabaseHelper.instance; List ds = await db.getDestinationByLatLon(d.lat!, d.lon!); @@ -261,6 +284,10 @@ class BottomSheetNew extends GetView { } // show add location details + // 目的地の詳細情報を表示するためのUIを構築するメソッドです。 + // 目的地の画像、名前、住所、電話番号、Webサイト、備考などの情報を表示します。 + // また、アクションボタンや「ここへ行く」ボタンも表示されます。 + // SingleChildScrollView detailsSheet(BuildContext context) { Destination cdest = destinationController .festuretoDestination(indexController.currentFeature[0]); @@ -592,6 +619,10 @@ class BottomSheetNew extends GetView { ); } + // 「行きたい」ボタンを表示するためのUIを構築するメソッドです。 + // 目的地が選択されているかどうかに基づいて、適切なアイコンとテキストを表示します。 + // ボタンがタップされたときの処理も含まれています。 + // Future wantToGo(BuildContext context) async { bool selected = false; // print( @@ -798,6 +829,9 @@ class BottomSheetNew extends GetView { ); } + // 目的地の詳細情報(住所、電話番号、Webサイトなど)を表示するためのUIを構築するメソッドです。 + // ラベルとテキストを受け取り、適切なアイコンとともに表示します。 + // Widget getDetails(BuildContext context, String label, String text, {bool isurl = false}) { return Row( diff --git a/lib/widgets/map_widget.dart b/lib/widgets/map_widget.dart index 238e41e..bc83327 100644 --- a/lib/widgets/map_widget.dart +++ b/lib/widgets/map_widget.dart @@ -210,6 +210,9 @@ class _MapWidgetState extends State { options: MapOptions( maxZoom: 18.4, onMapReady: () { + // print("Map controller ready!!"); ... working corretly + indexController.isMapControllerReady.value = true; // Added Akira,2024-4-6 for #2800 + subscription = indexController.mapController.mapEventStream .listen((MapEvent mapEvent) { if (mapEvent is MapEventMoveStart) {