まだ#2800検証中

This commit is contained in:
2024-04-07 10:56:51 +09:00
parent cd5befddb5
commit a0c1efc522
6 changed files with 420 additions and 56 deletions

View File

@ -33,62 +33,116 @@ import 'package:shared_preferences/shared_preferences.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart';
// 目的地に関連する状態管理とロジックを担当するクラスです。
//
class DestinationController extends GetxController { class DestinationController extends GetxController {
late LocationSettings locationSettings; late LocationSettings locationSettings; // 位置情報の設定を保持する変数です。
Timer? _GPStimer; Timer? _GPStimer; // GPSタイマーを保持する変数です。
var destinationCount = 0.obs; var destinationCount = 0.obs; // 目的地の数を保持するReactive変数です。
List<Destination> destinations = <Destination>[].obs; List<Destination> destinations = <Destination>[].obs; // 目的地のリストを保持するObservable変数です。
double currentLat = 0.0; double currentLat = 0.0; // 現在の緯度と経度を保持する変数です。
double currentLon = 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 game_started = false;
static bool ready_for_goal = false; static bool ready_for_goal = false;
bool skip_10s = false; bool skip_10s = false; // 10秒間のスキップフラグを示す変数です。
List<Destination> currentSelectedDestinations = <Destination>[].obs; List<Destination> currentSelectedDestinations = <Destination>[].obs; // 現在選択されている目的地のリストを保持するObservable変数です。
var isInCheckin = false.obs; var isInCheckin = false.obs; // ゲームの状態を示すReactive変数です。
var isInRog = false.obs; var isInRog = false.obs;
var isAtStart = false.obs; var isAtStart = false.obs;
var isAtGoal = false.obs; var isAtGoal = false.obs;
var isPhotoShoot = 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<Rogaining> rogainings = <Rogaining>[].obs; //List<Rogaining> rogainings = <Rogaining>[].obs;
bool checkingIn = false; bool checkingIn = false; // チェックイン中かどうかを示すフラグです。
var isGpsSelected = true.obs; var isGpsSelected = true.obs; // GPSが選択されているかどうかを示すReactive変数です。
BuildContext? context; BuildContext? context; // ビルドコンテキストを保持する変数です。
List<String> gps = <String>["-- stating --"].obs; List<String> gps = <String>["-- stating --"].obs; // GPSと位置情報の許可に関する情報を保持するObservable変数です。
List<String> locationPermission = <String>[" -- starting -- "].obs; List<String> locationPermission = <String>[" -- starting -- "].obs;
var travelMode = 0.obs; var travelMode = 0.obs; // 移動モードを保持するReactive変数です。
bool skipGps = false; bool skipGps = false; // GPSをスキップするかどうかを示すフラグです。
Map<String, dynamic> matrix = {}; Map<String, dynamic> matrix = {}; // 行列データを保持する変数です。
final photos = <File>[].obs; final photos = <File>[].obs; // 写真のリストを保持するReactive変数です。
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>(); // IndexControllerのインスタンスを保持する変数です。
final LocationController locationController = Get.put(LocationController()); final LocationController locationController = Get.put(LocationController()); // LocationControllerのインスタンスを保持する変数です。
final DatabaseService dbService = DatabaseService(); final DatabaseService dbService = DatabaseService(); // DatabaseServiceのインスタンスを保持する変数です。
int _start = 0; int _start = 0; // 開始時刻を保持する変数です。
int chekcs = 0; int chekcs = 0; // チェックポイントの数を保持する変数です。
var rogainingCounted = false.obs; 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) { String getFormatedTime(DateTime datetime) {
return DateFormat('yyyy-MM-dd HH:mm:ss').format(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) { Destination festuretoDestination(GeoJSONFeature fs) {
GeoJSONMultiPoint mp = fs.geometry as GeoJSONMultiPoint; GeoJSONMultiPoint mp = fs.geometry as GeoJSONMultiPoint;
LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]); LatLng pt = LatLng(mp.coordinates[0][1], mp.coordinates[0][0]);
@ -121,6 +175,8 @@ class DestinationController extends GetxController {
tags: fs.properties!["tags"]); tags: fs.properties!["tags"]);
} }
// 指定された目的地の位置情報に基づいてタイマーを開始する関数です。
//
Future<void> startTimerLocation(GeoJSONFeature fs, double distance) async { Future<void> startTimerLocation(GeoJSONFeature fs, double distance) async {
//print("---- in startTimer ----"); //print("---- in startTimer ----");
// print("---- is in rog is $is_in_rog ----"); // print("---- is in rog is $is_in_rog ----");
@ -136,6 +192,12 @@ class DestinationController extends GetxController {
} }
} }
// 指定された目的地に対してタイマーを開始する関数です。
// 目的地の位置情報を取得し、チェックイン半径内にいるかどうかを確認します。
// 写真撮影モードの場合は、ボトムシートを表示して写真撮影を行います。
// 目的地がデータベースに存在しない場合は、新しい目的地としてデータベースに挿入します。
// 目的地に応じて、チェックイン、ゴール、買い物ポイントの処理を行います。
//
Future<void> startTimer(Destination d, double distance) async { Future<void> startTimer(Destination d, double distance) async {
//print("=== passed dest is ${d.location_id} ${d.checkedin} ===="); //print("=== passed dest is ${d.location_id} ${d.checkedin} ====");
skipGps = true; skipGps = true;
@ -173,8 +235,10 @@ class DestinationController extends GetxController {
// print( // print(
// "---- checked in as ${indexController.currentDestinationFeature[0].checkedin.toString()} ----"); // "---- checked in as ${indexController.currentDestinationFeature[0].checkedin.toString()} ----");
} else { } else {
skipGps = false; // GPS信号が弱い場合でも、チェックインやゴールの処理を続行する
return; // comment out by Akira, 2024-4-5
// skipGps = false;
// return;
} }
if (isPhotoShoot.value == true) { if (isPhotoShoot.value == true) {
@ -425,6 +489,9 @@ class DestinationController extends GetxController {
return; return;
} }
// ロゲイニングをリセットする関数です。
// ゲームの状態をリセットし、データベースからデータを削除します。
//
Future<void> resetRogaining({bool isgoal = false}) async { Future<void> resetRogaining({bool isgoal = false}) async {
//print("----- resetting --------"); //print("----- resetting --------");
@ -455,6 +522,8 @@ class DestinationController extends GetxController {
dbService.updateDatabase(); dbService.updateDatabase();
} }
// すべての目的地を削除する関数です。
//
void deleteAllDestinations() { void deleteAllDestinations() {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) { db.deleteAllDestinations().then((value) {
@ -462,6 +531,8 @@ class DestinationController extends GetxController {
}); });
} }
// カメラを開いて写真を撮影する関数です。
//
void openCamera(BuildContext context, Destination? destination) { void openCamera(BuildContext context, Destination? destination) {
photos.clear(); photos.clear();
Navigator.push( Navigator.push(
@ -478,6 +549,8 @@ class DestinationController extends GetxController {
))); )));
} }
// ルートポイントを取得する関数です。
//
void getRoutePoints() { void getRoutePoints() {
indexController.routePoints = []; indexController.routePoints = [];
indexController.routePointLenght.value = 0; indexController.routePointLenght.value = 0;
@ -488,6 +561,8 @@ class DestinationController extends GetxController {
}); });
} }
// 指定された緯度と経度に対応する目的地を取得する関数です。
//
Future<Destination?> getDestinationForLatLong(double lat, double long) async { Future<Destination?> getDestinationForLatLong(double lat, double long) async {
for (final d in destinations) { for (final d in destinations) {
if (lat == d.lat && long == d.lon) { if (lat == d.lat && long == d.lon) {
@ -497,6 +572,9 @@ class DestinationController extends GetxController {
return null; return null;
} }
// チェックインの呼び出しを行う関数です。
// 指定された目的地に対してチェックインの処理を行います。
//
Future<void> callforCheckin(Destination d) async { Future<void> callforCheckin(Destination d) async {
bool autoCheckin = d.auto_checkin == 0 ? false : true; bool autoCheckin = d.auto_checkin == 0 ? false : true;
//print("---- f- checkin ${d.sub_loc_id} ----"); //print("---- f- checkin ${d.sub_loc_id} ----");
@ -573,6 +651,8 @@ class DestinationController extends GetxController {
} }
} }
// GPSデータをデータベースに追加する関数です。
//
Future<void> addGPStoDB(double la, double ln, {isCheckin = 0}) async { Future<void> addGPStoDB(double la, double ln, {isCheckin = 0}) async {
//print("in addGPStoDB ${indexController.currentUser}"); //print("in addGPStoDB ${indexController.currentUser}");
try { try {
@ -594,6 +674,11 @@ class DestinationController extends GetxController {
} }
} }
// チェックインを確認する関数です。
// ゲームが開始されていない場合は、ゲームを開始します。
// 目的地のリストを走査し、現在位置がチェックイン半径内にある場合は、チェックインの処理を行います。
// GPSデータの送信を開始します。
//
Future<void> checkForCheckin() async { Future<void> checkForCheckin() async {
//print("--- Start of checkForCheckin function ---"); //print("--- Start of checkForCheckin function ---");
dbService.updateDatabase(); dbService.updateDatabase();
@ -631,6 +716,8 @@ class DestinationController extends GetxController {
} }
} }
// GPSデータをサーバーにプッシュする関数です。
//
Future<void> pushGPStoServer() async { Future<void> pushGPStoServer() async {
// print( // print(
// "^^^^^^^^ ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}"); // "^^^^^^^^ ${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 { void addToRogaining(double lat, double lon, int destinationId) async {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
List<Destination> d = await db.getDestinationById(destinationId); List<Destination> d = await db.getDestinationById(destinationId);
@ -660,12 +749,16 @@ class DestinationController extends GetxController {
saveGameState(); saveGameState();
} }
// 買い物ポイントをキャンセルする関数です。
//
Future<void> cancelBuyPoint(Destination destination) async { Future<void> cancelBuyPoint(Destination destination) async {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
await db.updateCancelBuyPoint(destination); await db.updateCancelBuyPoint(destination);
populateDestinations(); populateDestinations();
} }
// 指定されたパスの画像をギャラリーに保存する関数です。
//
_saveImageFromPath(String imagePath) async { _saveImageFromPath(String imagePath) async {
// Read the image file from the given path // Read the image file from the given path
File imageFile = File(imagePath); File imageFile = File(imagePath);
@ -676,6 +769,8 @@ class DestinationController extends GetxController {
//print("--- save result --- ${result}"); //print("--- save result --- ${result}");
} }
// 買い物ポイントを作成する関数です。 指定された目的地に対して買い物ポイントの処理を行います。
//
Future<void> makeBuyPoint(Destination destination, String imageurl) async { Future<void> makeBuyPoint(Destination destination, String imageurl) async {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
await db.updateBuyPoint(destination, imageurl); await db.updateBuyPoint(destination, imageurl);
@ -706,6 +801,8 @@ class DestinationController extends GetxController {
} }
} }
// チェックインを行う関数です。 指定された目的地に対してチェックインの処理を行います。
//
Future<void> makeCheckin( Future<void> makeCheckin(
Destination destination, bool action, String imageurl) async { Destination destination, bool action, String imageurl) async {
// print("~~~~ calling checkin function ~~~~"); // print("~~~~ calling checkin function ~~~~");
@ -754,11 +851,15 @@ class DestinationController extends GetxController {
dbService.updateDatabase(); dbService.updateDatabase();
} }
// チェックインを削除する関数です。
//
Future<void> removeCheckin(int cp) { Future<void> removeCheckin(int cp) {
dbService.updateDatabase(); dbService.updateDatabase();
return ExternalService().removeCheckin(cp); return ExternalService().removeCheckin(cp);
} }
// ゲームを開始する関数です。
//
Future<void> startGame() async { Future<void> startGame() async {
//print("------ starting game ------"); //print("------ starting game ------");
if (game_started == false) { if (game_started == false) {
@ -766,6 +867,8 @@ class DestinationController extends GetxController {
} }
} }
// コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
//
@override @override
void onInit() async { void onInit() async {
super.onInit(); super.onInit();
@ -781,11 +884,17 @@ class DestinationController extends GetxController {
startGame(); startGame();
} }
// コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
//
@override @override
void onClose() { void onClose() {
locationController.stopPositionStream(); locationController.stopPositionStream();
} }
// 位置情報の更新を処理する関数です。
// 現在位置とスタート地点との距離を計算します。
// 現在位置と前回の位置情報との距離と時間差を確認し、一定の条件を満たす場合はGPSデータをデータベースに追加します。
//
void handleLocationUpdate(LocationMarkerPosition? position) async { void handleLocationUpdate(LocationMarkerPosition? position) async {
try { try {
if (position != null) { if (position != null) {
@ -814,14 +923,23 @@ class DestinationController extends GetxController {
} }
} }
} finally { } finally {
/* Akira , 2024-4-5
if (position != null && if (position != null &&
(position.latitude != 0 && position.longitude != 0)) { (position.latitude != 0 && position.longitude != 0)) {
currentLat = position.latitude; currentLat = position.latitude;
currentLon = position.longitude; currentLon = position.longitude;
} }
*/
if (position != null) {
// 位置情報が取得できた場合、精度に関わらず最後の位置情報を更新
currentLat = position.latitude;
currentLon = position.longitude;
}
} }
} }
// スタート地点までの距離を計算する関数です。
//
double distanceToStart() { double distanceToStart() {
if (indexController.locations.isEmpty) { if (indexController.locations.isEmpty) {
return 1000000000; return 1000000000;
@ -853,6 +971,8 @@ class DestinationController extends GetxController {
return distanceToDest; return distanceToDest;
} }
// 強制チェックイン距離を取得する関数です。
//
int getForcedChckinDistance(Destination dest) { int getForcedChckinDistance(Destination dest) {
if (dest.checkin_radious == -1) { if (dest.checkin_radious == -1) {
return 10000000000000000; return 10000000000000000;
@ -883,11 +1003,15 @@ class DestinationController extends GetxController {
return _retValue; return _retValue;
} }
// ユーザートークンを読み取る関数です。
//
readUserToken() async { readUserToken() async {
final SharedPreferences prefs = await SharedPreferences.getInstance(); final SharedPreferences prefs = await SharedPreferences.getInstance();
indexController.userToken = prefs.getString("user_token"); indexController.userToken = prefs.getString("user_token");
} }
// コントローラーの準備完了時に呼び出されるライフサイクルメソッドです。
//
@override @override
void onReady() async { void onReady() async {
await readUserToken(); await readUserToken();
@ -917,6 +1041,8 @@ class DestinationController extends GetxController {
super.onReady(); super.onReady();
} }
// 地図の境界を修正する関数です。
//
void fixMapBound(String token) { void fixMapBound(String token) {
//String _token = indexController.currentUser[0]["token"]; //String _token = indexController.currentUser[0]["token"];
indexController.switchPage(AppPages.INDEX); 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() { void centerMapToCurrentLocation() {
assert(() { assert(() {
print("center is ${currentLon}, ${currentLon}"); print("center is ${currentLon}, ${currentLon}");
@ -948,6 +1111,8 @@ class DestinationController extends GetxController {
} }
} }
// 接続状態が変更されたときに呼び出される関数です。
//
void connectionChanged(String val) { void connectionChanged(String val) {
//print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val'); //print('----- %%%%%%%%%%%%%%%%%%%%% ----- $val');
Map<String, dynamic> res = {}; Map<String, dynamic> res = {};
@ -987,6 +1152,8 @@ class DestinationController extends GetxController {
} }
} }
// 位置情報の許可を確認する関数です。
//
void checkPermission() async { void checkPermission() async {
LocationPermission permission = await Geolocator.checkPermission(); LocationPermission permission = await Geolocator.checkPermission();
if (permission != LocationPermission.whileInUse || if (permission != LocationPermission.whileInUse ||
@ -997,6 +1164,8 @@ class DestinationController extends GetxController {
} }
} }
// IDに基づいて目的地を取得する関数です。
//
Destination? destinationById(int id) { Destination? destinationById(int id) {
Destination? d; Destination? d;
//print("--- target des - $id ----"); //print("--- target des - $id ----");
@ -1010,6 +1179,8 @@ class DestinationController extends GetxController {
return d; return d;
} }
// 目的地を削除する関数です。
//
void deleteDestination(Destination d) { void deleteDestination(Destination d) {
//int id = destinations[index].location_id!; //int id = destinations[index].location_id!;
//print("---- index ${destinations[index].location_id!}-----"); //print("---- index ${destinations[index].location_id!}-----");
@ -1026,6 +1197,8 @@ class DestinationController extends GetxController {
dbService.updateDatabase(); dbService.updateDatabase();
} }
// データベースからすべての目的地を削除する関数です。
//
void deleteDBDestinations() { void deleteDBDestinations() {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.deleteAllDestinations().then((value) { db.deleteAllDestinations().then((value) {
@ -1036,6 +1209,8 @@ class DestinationController extends GetxController {
// ---------- database ------------------/// // ---------- database ------------------///
// 目的地を追加する関数です。
//
void addDestinations(Destination dest) { void addDestinations(Destination dest) {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.getDestinationByLatLon(dest.lat!, dest.lon!).then((value) { db.getDestinationByLatLon(dest.lat!, dest.lon!).then((value) {
@ -1057,6 +1232,8 @@ class DestinationController extends GetxController {
dbService.updateDatabase(); dbService.updateDatabase();
} }
// 目的地の選択状態を切り替える関数です。
//
void toggleSelection(Destination dest) async { void toggleSelection(Destination dest) async {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
await db.toggleSelecttion(dest); await db.toggleSelecttion(dest);
@ -1076,6 +1253,8 @@ class DestinationController extends GetxController {
}); });
} }
// ダイアログを表示する関数です。
//
buildShowDialog(BuildContext context) { buildShowDialog(BuildContext context) {
return showDialog( return showDialog(
context: context, context: context,
@ -1087,6 +1266,8 @@ class DestinationController extends GetxController {
}); });
} }
// 現在地点からの目的地の行列を計算する関数です。
//
void destinationMatrixFromCurrentPoint(List<Destination> points) { void destinationMatrixFromCurrentPoint(List<Destination> points) {
//buildShowDialog(Get.context!); //buildShowDialog(Get.context!);
MatrixService.getDestinations(points).then((mat) { MatrixService.getDestinations(points).then((mat) {
@ -1112,6 +1293,8 @@ class DestinationController extends GetxController {
}); });
} }
// 目的地のリストを取得してObservable変数を更新する関数です。
//
void populateDestinations() { void populateDestinations() {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
destinations.clear(); destinations.clear();
@ -1126,6 +1309,8 @@ class DestinationController extends GetxController {
}); });
} }
// 目的地の順序を変更する関数です。
//
void makeOrder(Destination d, int dir) { void makeOrder(Destination d, int dir) {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
db.updateOrder(d, dir).then((value) { db.updateOrder(d, dir).then((value) {

View File

@ -16,6 +16,7 @@ import 'package:rogapp/services/location_service.dart';
import 'package:rogapp/utils/database_helper.dart'; import 'package:rogapp/utils/database_helper.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class IndexController extends GetxController { class IndexController extends GetxController {
List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs; List<GeoJSONFeatureCollection> locations = <GeoJSONFeatureCollection>[].obs;
List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs; List<GeoJSONFeature> currentFeature = <GeoJSONFeature>[].obs;
@ -42,8 +43,11 @@ class IndexController extends GetxController {
var isCustomAreaSelected = false.obs; var isCustomAreaSelected = false.obs;
RxBool isMapControllerReady = RxBool(false); // MapControllerの初期化状態を管理するフラグ
//final mapControllerReadyStream = Stream<bool>.value(false); // MapControllerの初期化状態を通知するためのストリーム
MapController mapController = MapController(); MapController mapController = MapController();
MapController rogMapController = MapController(); //MapController rogMapController = MapController();
String? userToken; String? userToken;
@ -166,8 +170,9 @@ class IndexController extends GetxController {
AuthService.login(email, password).then((value) { AuthService.login(email, password).then((value) {
print("------- logged in user details ######## $value ###### --------"); print("------- logged in user details ######## $value ###### --------");
if (value.isNotEmpty) { if (value.isNotEmpty) {
Navigator.pop(context); // Navigator.pop(context);
print("--------- user details login ----- $value"); print("--------- user details login ----- $value");
//await Future.delayed(const Duration(milliseconds: 500)); // Added Akira:2024-4-6, #2800
changeUser(value); changeUser(value);
} else { } else {
isLoading.value = false; isLoading.value = false;
@ -283,7 +288,7 @@ class IndexController extends GetxController {
}); });
} }
/* /* Old code
void loadLocationsBound() { void loadLocationsBound() {
if (isCustomAreaSelected.value == true) { if (isCustomAreaSelected.value == true) {
return; return;
@ -337,17 +342,33 @@ class IndexController extends GetxController {
} }
*/ */
// 2024-04-03 Akira .. Update the code . See ticket 2800. // 2024-04-03 Akira .. Update the code . See ticket 2800.
// //
void loadLocationsBound() { void loadLocationsBound() async {
if (isCustomAreaSelected.value == true) { if (isCustomAreaSelected.value == true) {
return; return;
} }
// Akira 追加:2024-4-6 #2800
await waitForMapControllerReady(); // MapControllerの初期化が完了するまで待機
// Akira 追加:2024-4-6 #2800
locations.clear(); locations.clear();
String cat = currentCat.isNotEmpty ? currentCat[0] : ""; String cat = currentCat.isNotEmpty ? currentCat[0] : "";
if (currentCat.isNotEmpty && currentCat[0] == "-all-") { if (currentCat.isNotEmpty && currentCat[0] == "-all-") {
cat = ""; cat = "";
} }
/*
// Akira Add 2024-4-6
if( mapController.controller == null ) {
print("操作が完了する前にMapControllerまたはウィジェットが破棄されました。");
isLoading.value = true; // ローディング状態をtrueに設定
return;
}
//
*/
LatLngBounds bounds = mapController.bounds!; LatLngBounds bounds = mapController.bounds!;
currentBound.clear(); currentBound.clear();
currentBound.add(bounds); currentBound.add(bounds);
@ -370,24 +391,14 @@ class IndexController extends GetxController {
cat cat
); );
if ( value == null ) { 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の場合 // APIからのレスポンスがnullの場合
DestinationController destinationController = Get.find<DestinationController>(); // 追加 print("LocationService.loadLocationsBound からの回答がnullのため、マップをリロード");
final tk = currentUser[0]["token"]; // 追加 //DestinationController destinationController = Get.find<DestinationController>(); // 追加
if (tk != null) { // 追加 //final tk = currentUser[0]["token"]; // 追加
destinationController.fixMapBound(tk); // 追加 //if (tk != null) { // 追加
} // 追加 // destinationController.fixMapBound(tk); // 追加
// return; //} // 追加
return;
} }
isLoading.value = false; // ローディング状態をfalseに設定 isLoading.value = false; // ローディング状態をfalseに設定
@ -412,6 +423,18 @@ class IndexController extends GetxController {
}); });
} }
//===Akira 追加:2024-4-6 #2800
Future<void> 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) { void setBound(LatLngBounds bounds) {
currentBound.clear(); currentBound.clear();
currentBound.add(bounds); currentBound.add(bounds);

View File

@ -11,19 +11,41 @@ import 'package:rogapp/utils/database_gps.dart';
import 'package:rogapp/utils/database_helper.dart'; import 'package:rogapp/utils/database_helper.dart';
import 'package:rogapp/widgets/list_widget.dart'; import 'package:rogapp/widgets/list_widget.dart';
import 'package:rogapp/widgets/map_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<IndexController>を継承したStatelessWidgetです。このクラスは、アプリのメインページを表すウィジェットです。
//
class IndexPage extends GetView<IndexController> { class IndexPage extends GetView<IndexController> {
IndexPage({Key? key}) : super(key: key); IndexPage({Key? key}) : super(key: key);
// IndexControllerとDestinationControllerのインスタンスを取得しています。
//
final LocationController locationController = Get.find<LocationController>();
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>();
final DestinationController destinationController = final DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
// buildメソッドは、ウィジェットのUIを構築するメソッドです。
// ここでは、WillPopScopeウィジェットを使用して、端末の戻るボタンが押された際の動作を制御しています。
//
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return WillPopScope( return WillPopScope(
onWillPop: () async => false, onWillPop: () async => false,
child: Scaffold( child: Scaffold(
//
// Scaffoldウィジェットを使用して、アプリのメインページのレイアウトを構築しています。
//
drawer: DrawerPage(), drawer: DrawerPage(),
appBar: AppBar( appBar: AppBar(
title: Text("add_location".tr), title: Text("add_location".tr),
@ -35,6 +57,10 @@ class IndexPage extends GetView<IndexController> {
// }, // },
// icon: const Icon(Icons.ten_k_sharp)), // icon: const Icon(Icons.ten_k_sharp)),
//
// AppBarには、タイトルとアクションアイコンが含まれています。
// アクションアイコンには、GPSデータの表示、履歴の表示、マップの更新、検索などの機能が含まれています。
//
IconButton( IconButton(
onPressed: () async { onPressed: () async {
// GpsDatabaseHelper db = GpsDatabaseHelper.instance; // GpsDatabaseHelper db = GpsDatabaseHelper.instance;
@ -75,6 +101,28 @@ class IndexPage extends GetView<IndexController> {
), ),
), ),
//CatWidget(indexController: indexController,), //CatWidget(indexController: indexController,),
//
// デバッグ時のみリロードボタンの横にGPS信号レベルの設定ボタンを設置し、
// タップすることでGPS信号の強弱をシミュレーションできるようにする
// Akira 2024-4-5
//
Obx(
() => destinationController.isSimulationMode.value
? DropdownButton<String>(
value: locationController.getSimulatedSignalStrength(),
onChanged: (value) {
locationController.setSimulatedSignalStrength(value!);
},
items: ['high', 'medium', 'low']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
: Container(),
),
], ],
), ),
// bottomNavigationBar: BottomAppBar( // bottomNavigationBar: BottomAppBar(
@ -139,11 +187,18 @@ class IndexPage extends GetView<IndexController> {
// ], // ],
// ), // ),
// ), // ),
//
// マップモードとリストモードを切り替えるためのボタンです。
//
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: () { onPressed: () {
indexController.toggleMode(); indexController.toggleMode();
}, },
elevation: 1.0, elevation: 1.0,
//
// Obxウィジェットを使用して、indexController.mode.valueの値に基づいて、MapWidgetまたはListWidgetを表示しています。
//
child: Obx( child: Obx(
() => indexController.mode.value == 0 () => indexController.mode.value == 0
? const Image(image: AssetImage('assets/images/list2.png')) ? const Image(image: AssetImage('assets/images/list2.png'))
@ -151,6 +206,9 @@ class IndexPage extends GetView<IndexController> {
), ),
), ),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
//
// bodyには、SafeAreaウィジェットを使用して、画面の安全な領域内にUIを構築しています。
//
body: SafeArea( body: SafeArea(
child: Column( child: Column(
children: [ children: [

View File

@ -5,28 +5,78 @@ import 'package:geolocator/geolocator.dart';
import 'package:rogapp/widgets/debug_widget.dart'; import 'package:rogapp/widgets/debug_widget.dart';
import 'package:flutter_map_location_marker/flutter_map_location_marker.dart'; import 'package:flutter_map_location_marker/flutter_map_location_marker.dart';
// LocationControllerクラスは、GetxControllerを継承したクラスであり、位置情報の管理を担当しています。
// LocationControllerは以下の機能を提供しています。
// LocationControllerは、アプリ全体で位置情報を一元管理するための重要なコンポーネントです。
// 他のコンポーネントは、LocationControllerから位置情報を取得し、位置情報に関連する機能を実装することができます。
//
// * 現在の位置情報を保持し、他のコンポーネントからアクセスできるようにします。
// * 位置情報のストリームを管理し、位置情報の更新を監視します。
// * 位置情報サービスの有効性と権限の確認を行い、適切な処理を行います。
// * 位置情報のストリームを開始、停止、再開する機能を提供します。
// * 位置マーカーの位置情報をStreamControllerを通じて他のコンポーネントに通知します。
//
class LocationController extends GetxController { class LocationController extends GetxController {
// Reactive variable to hold the current position // Reactive variable to hold the current position
Rx<Position?> currentPosition = Rx<Position?>(null); Rx<Position?> currentPosition = Rx<Position?>(null);
// 現在の位置情報を保持するReactive変数です。Rx<Position?>型で宣言されています。
// Subscription to the position stream // Subscription to the position stream
StreamSubscription<Position>? positionStream; StreamSubscription<Position>? positionStream;
// 位置情報のストリームを保持する変数です。StreamSubscription<Position>型で宣言されています。
final locationMarkerPositionStreamController = final locationMarkerPositionStreamController =
StreamController<LocationMarkerPosition?>.broadcast(); StreamController<LocationMarkerPosition?>.broadcast();
// 位置マーカーの位置情報を送信するためのStreamControllerです。
// StreamController<LocationMarkerPosition?>型で宣言されています。
bool isStreamPaused = false; bool isStreamPaused = false; // 位置情報のストリームが一時停止中かどうかを示すフラグです。bool型で宣言されています。
// ====== Akira , GPS信号強度をシミュレート ==== ここから
//
// GPS信号強度をシミュレートするための変数
Rx<String> _simulatedSignalStrength = Rx<String>('high');
// GPS信号強度をシミュレートするための関数
void setSimulatedSignalStrength(String strength) {
_simulatedSignalStrength.value = strength;
}
// シミュレートされた信号強度を取得するための関数
String getSimulatedSignalStrength() {
return _simulatedSignalStrength.value;
}
//
// ====== Akira , GPS信号強度をシミュレート ==== ここから
// 位置マーカーの位置情報のストリームを取得するゲッター関数です。
// locationMarkerPositionStreamController.streamを返します。
//
Stream<LocationMarkerPosition?> get locationMarkerPositionStream => Stream<LocationMarkerPosition?> get locationMarkerPositionStream =>
locationMarkerPositionStreamController.stream; locationMarkerPositionStreamController.stream;
// コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
// startPositionStreamメソッドを呼び出して、位置情報のストリームを開始します。
//
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
// Start listening to location updates when the controller is initialized // Start listening to location updates when the controller is initialized
startPositionStream(); startPositionStream();
} }
// 位置情報のストリームを開始するメソッドです。
// 位置情報サービスが有効か確認し、無効な場合はダイアログを表示します。
// 位置情報の権限を確認し、必要な権限がない場合は権限をリクエストします。
// 既存の位置情報のストリームをキャンセルします。
// Geolocator.getPositionStreamを使用して、新しい位置情報のストリームを開始します。
// ストリームから受信した位置情報をlocationMarkerPositionStreamControllerに追加します。
// エラーが発生した場合は、locationMarkerPositionStreamControllerにエラーを追加します。
// ストリームが一時停止中の場合は、ストリームを再開します。
//
void startPositionStream() async { void startPositionStream() async {
// Check for location service and permissions before starting the stream // Check for location service and permissions before starting the stream
bool serviceEnabled = await Geolocator.isLocationServiceEnabled(); bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
@ -138,6 +188,10 @@ class LocationController extends GetxController {
} }
// Method to stop the position stream // Method to stop the position stream
// 位置情報のストリームを停止するメソッドです。
// positionStreamが存在する場合、ストリームを一時停止します。
// isStreamPausedフラグをtrueに設定します。
//
void stopPositionStream() { void stopPositionStream() {
if (positionStream != null) { if (positionStream != null) {
positionStream!.pause(); positionStream!.pause();
@ -146,6 +200,10 @@ class LocationController extends GetxController {
} }
// Method to resume the position stream // Method to resume the position stream
// 位置情報のストリームを再開するメソッドです。
// positionStreamが存在し、ストリームが一時停止中の場合、ストリームを再開します。
// isStreamPausedフラグをfalseに設定します。
//
void resumePositionStream() { void resumePositionStream() {
if (positionStream != null && isStreamPaused) { if (positionStream != null && isStreamPaused) {
positionStream!.resume(); positionStream!.resume();
@ -153,6 +211,9 @@ class LocationController extends GetxController {
} }
} }
// コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
// positionStreamをキャンセルします。
//
@override @override
void onClose() { void onClose() {
// Cancel the position stream subscription when the controller is closed // Cancel the position stream subscription when the controller is closed

View File

@ -18,6 +18,10 @@ import 'package:rogapp/widgets/bottom_sheet_controller.dart';
import 'package:rogapp/widgets/debug_widget.dart'; import 'package:rogapp/widgets/debug_widget.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
// BottomSheetNewは、StatelessWidgetを継承したクラスで、目的地の詳細情報を表示するボトムシートのUIを構築します。
// コンストラクタでは、destination目的地オブジェクトとisAlreadyCheckedInすでにチェックイン済みかどうかのフラグを受け取ります。
// buildメソッドでは、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。
//
class BottomSheetNew extends GetView<BottomSheetController> { class BottomSheetNew extends GetView<BottomSheetController> {
BottomSheetNew( BottomSheetNew(
{this.isAlreadyCheckedIn = false, Key? key, required this.destination}) {this.isAlreadyCheckedIn = false, Key? key, required this.destination})
@ -26,9 +30,12 @@ class BottomSheetNew extends GetView<BottomSheetController> {
final IndexController indexController = Get.find<IndexController>(); final IndexController indexController = Get.find<IndexController>();
final DestinationController destinationController = final DestinationController destinationController =
Get.find<DestinationController>(); Get.find<DestinationController>();
final Destination destination; final Destination destination; // 目的地オブジェクト
final bool isAlreadyCheckedIn; final bool isAlreadyCheckedIn; // すでにチェックイン済みかどうかのフラグ
// 目的地の画像を取得するためのメソッドです。
// indexController.rogModeの値に基づいて、適切な画像を返します。画像が見つからない場合は、デフォルトの画像を返します。
//
Image getImage() { Image getImage() {
String serverUrl = ConstValues.currentServer(); String serverUrl = ConstValues.currentServer();
@ -95,10 +102,16 @@ class BottomSheetNew extends GetView<BottomSheetController> {
} }
} }
// URLを開くためのメソッドです。
// url_launcherパッケージを使用して、指定されたURLを開きます。
//
void _launchURL(url) async { void _launchURL(url) async {
if (!await launchUrl(url)) throw 'Could not launch $url'; if (!await launchUrl(url)) throw 'Could not launch $url';
} }
// 指定されたlocationidが目的地リストに含まれているかどうかを確認するメソッドです。
// destinationController.destinationsリストを走査し、locationidが一致する目的地があるかどうかを返します。
//
bool isInDestination(String locationid) { bool isInDestination(String locationid) {
int lid = int.parse(locationid); int lid = int.parse(locationid);
if (destinationController.destinations if (destinationController.destinations
@ -110,6 +123,10 @@ class BottomSheetNew extends GetView<BottomSheetController> {
} }
} }
// アクションボタン(チェックイン、ゴールなど)を表示するためのメソッドです。
// 現在の状態に基づいて、適切なボタンを返します。
// ボタンがタップされたときの処理も含まれています。
//
Widget getActionButton(BuildContext context, Destination destination) { Widget getActionButton(BuildContext context, Destination destination) {
assert(() { assert(() {
print("getActionButton ${destinationController.rogainingCounted.value}"); print("getActionButton ${destinationController.rogainingCounted.value}");
@ -130,7 +147,8 @@ class BottomSheetNew extends GetView<BottomSheetController> {
LatLng(cdest.lat!, cdest.lon!)); LatLng(cdest.lat!, cdest.lon!));
if (destinationController.rogainingCounted.value == true && if (destinationController.rogainingCounted.value == true &&
destinationController.distanceToStart() <= 500 && // destinationController.distanceToStart() <= 500 && ... GPS信号が弱い時でもOKとする。
(destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) &&
destination.cp == -1 && destination.cp == -1 &&
DestinationController.ready_for_goal == true) { DestinationController.ready_for_goal == true) {
//goal //goal
@ -171,6 +189,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
onPressed: () async { onPressed: () async {
// Check conditions to show confirmation dialog // Check conditions to show confirmation dialog
if (destinationController.isInRog.value == false && if (destinationController.isInRog.value == false &&
(destinationController.distanceToStart() <= 500 || destinationController.isGpsSignalWeak() ) && //追加 Akira 2024-4-5
destination.cp == -1 && destination.cp == -1 &&
destinationController.rogainingCounted.value == false) { destinationController.rogainingCounted.value == false) {
print("counted ${destinationController.rogainingCounted.value}"); print("counted ${destinationController.rogainingCounted.value}");
@ -240,6 +259,7 @@ class BottomSheetNew extends GetView<BottomSheetController> {
return Container(); return Container();
} }
// 継承元のbuild をオーバーライドし、detailsSheetメソッドを呼び出して、目的地の詳細情報を表示します。
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
//print("to start ${destinationController.distanceToStart()}"); //print("to start ${destinationController.distanceToStart()}");
@ -253,6 +273,9 @@ class BottomSheetNew extends GetView<BottomSheetController> {
return detailsSheet(context); return detailsSheet(context);
} }
// 指定された目的地がすでにチェックイン済みかどうかを確認するメソッドです。
// DatabaseHelperを使用して、目的地の位置情報に基づいてデータベースを検索し、結果を返します。
//
Future<bool> isDestinationCheckedIn(Destination d) async { Future<bool> isDestinationCheckedIn(Destination d) async {
DatabaseHelper db = DatabaseHelper.instance; DatabaseHelper db = DatabaseHelper.instance;
List<Destination> ds = await db.getDestinationByLatLon(d.lat!, d.lon!); List<Destination> ds = await db.getDestinationByLatLon(d.lat!, d.lon!);
@ -261,6 +284,10 @@ class BottomSheetNew extends GetView<BottomSheetController> {
} }
// show add location details // show add location details
// 目的地の詳細情報を表示するためのUIを構築するメソッドです。
// 目的地の画像、名前、住所、電話番号、Webサイト、備考などの情報を表示します。
// また、アクションボタンや「ここへ行く」ボタンも表示されます。
//
SingleChildScrollView detailsSheet(BuildContext context) { SingleChildScrollView detailsSheet(BuildContext context) {
Destination cdest = destinationController Destination cdest = destinationController
.festuretoDestination(indexController.currentFeature[0]); .festuretoDestination(indexController.currentFeature[0]);
@ -592,6 +619,10 @@ class BottomSheetNew extends GetView<BottomSheetController> {
); );
} }
// 「行きたい」ボタンを表示するためのUIを構築するメソッドです。
// 目的地が選択されているかどうかに基づいて、適切なアイコンとテキストを表示します。
// ボタンがタップされたときの処理も含まれています。
//
Future<Widget> wantToGo(BuildContext context) async { Future<Widget> wantToGo(BuildContext context) async {
bool selected = false; bool selected = false;
// print( // print(
@ -798,6 +829,9 @@ class BottomSheetNew extends GetView<BottomSheetController> {
); );
} }
// 目的地の詳細情報住所、電話番号、Webサイトなどを表示するためのUIを構築するメソッドです。
// ラベルとテキストを受け取り、適切なアイコンとともに表示します。
//
Widget getDetails(BuildContext context, String label, String text, Widget getDetails(BuildContext context, String label, String text,
{bool isurl = false}) { {bool isurl = false}) {
return Row( return Row(

View File

@ -210,6 +210,9 @@ class _MapWidgetState extends State<MapWidget> {
options: MapOptions( options: MapOptions(
maxZoom: 18.4, maxZoom: 18.4,
onMapReady: () { onMapReady: () {
// print("Map controller ready!!"); ... working corretly
indexController.isMapControllerReady.value = true; // Added Akira,2024-4-6 for #2800
subscription = indexController.mapController.mapEventStream subscription = indexController.mapController.mapEventStream
.listen((MapEvent mapEvent) { .listen((MapEvent mapEvent) {
if (mapEvent is MapEventMoveStart) { if (mapEvent is MapEventMoveStart) {