サイドメニューの改善、ログイン直後にマップが表示されない問題、屋内でGPS信号が受信できない場合のポップアップ追加
This commit is contained in:
@ -693,6 +693,11 @@ class BuyPointCamera extends StatelessWidget {
|
||||
|
||||
|
||||
class QRCodeScannerPage extends StatefulWidget {
|
||||
|
||||
QRCodeScannerPage({Key? key, required this.destination}) : super(key: key);
|
||||
|
||||
Destination destination;
|
||||
|
||||
@override
|
||||
_QRCodeScannerPageState createState() => _QRCodeScannerPageState();
|
||||
}
|
||||
|
||||
@ -104,6 +104,13 @@ class DestinationController extends GetxController {
|
||||
// ゴール地点でのロジックの制御:rogainingCountedがtrueの場合、つまりポイントがカウントされている場合にのみ、ゴール処理を実行できます。
|
||||
// UI の更新:rogainingCountedの状態に基づいて、適切なメッセージやボタンを表示することができます。
|
||||
|
||||
bool isMapControllerReady = false;
|
||||
|
||||
LatLng lastValidGPSLocation = LatLng(0, 0);
|
||||
DateTime lastGPSDataReceivedTime = DateTime.now();
|
||||
DateTime lastPopupShownTime = DateTime.now().subtract(Duration(minutes: 10));
|
||||
bool isPopupShown = false;
|
||||
bool hasReceivedGPSData = false;
|
||||
|
||||
/*
|
||||
//==== Akira .. GPS信号シミュレーション用 ===== ここから、2024-4-5
|
||||
@ -149,6 +156,48 @@ class DestinationController extends GetxController {
|
||||
//==== Akira .. GPS信号シミュレーション用 ======= ここまで
|
||||
*/
|
||||
|
||||
void showGPSDataNotReceivedPopup() {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
title: Text('GPS信号が受信できません'),
|
||||
content: Text('GPS信号が受信できる場所に移動してください。'),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Get.back(),
|
||||
child: Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 最後に有効なGPSデータを受け取ってから10分以上経過している場合にのみメッセージを表示するようにします。
|
||||
//
|
||||
void checkGPSDataReceived() {
|
||||
if (!hasReceivedGPSData) {
|
||||
// GPS信号を全く受信していない。
|
||||
if (!isPopupShown) {
|
||||
// ポップアップしていない。
|
||||
showGPSDataNotReceivedPopup();
|
||||
lastPopupShownTime = DateTime.now();
|
||||
isPopupShown = true;
|
||||
}
|
||||
} else {
|
||||
if (DateTime.now().difference(lastGPSDataReceivedTime).inSeconds >= 600) {
|
||||
// 前回GPS信号を受信してから10分経過。
|
||||
if (!isPopupShown && DateTime.now().difference(lastPopupShownTime).inMinutes >= 3) {
|
||||
// 前回ポップアップしてから3分経過してなければ
|
||||
showGPSDataNotReceivedPopup();
|
||||
lastPopupShownTime = DateTime.now();
|
||||
isPopupShown = true;
|
||||
}
|
||||
} else {
|
||||
isPopupShown = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 日時をフォーマットされた文字列に変換する関数です。
|
||||
//
|
||||
String getFormatedTime(DateTime datetime) {
|
||||
@ -1014,12 +1063,27 @@ class DestinationController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
Timer? gpsCheckTimer; // 一定間隔でGPSデータの受信状態をチェックするタイマー
|
||||
|
||||
void startGPSCheckTimer() {
|
||||
gpsCheckTimer = Timer.periodic(Duration(seconds: 5), (timer) {
|
||||
checkGPSDataReceived();
|
||||
});
|
||||
}
|
||||
|
||||
// コントローラーの初期化時に呼び出されるライフサイクルメソッドです。
|
||||
//
|
||||
@override
|
||||
void onInit() async {
|
||||
super.onInit();
|
||||
|
||||
startGPSCheckTimer();
|
||||
|
||||
// MapControllerの初期化完了を待機するフラグを設定
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
isMapControllerReady = true;
|
||||
});
|
||||
|
||||
// 要検討:エラーメッセージを表示するなどの適切な処理を追加することを検討してください。
|
||||
//
|
||||
// locationController からデバイスの受け取るGPS情報を取得し、
|
||||
@ -1035,13 +1099,17 @@ class DestinationController extends GetxController {
|
||||
});
|
||||
|
||||
startGame();
|
||||
|
||||
checkGPSDataReceived();
|
||||
}
|
||||
|
||||
// コントローラーのクローズ時に呼び出されるライフサイクルメソッドです。
|
||||
//
|
||||
@override
|
||||
void onClose() {
|
||||
gpsCheckTimer?.cancel();
|
||||
locationController.stopPositionStream();
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
// 位置情報の更新を処理する関数です。
|
||||
@ -1062,11 +1130,25 @@ class DestinationController extends GetxController {
|
||||
if (position != null) {
|
||||
currentLat = position.latitude;
|
||||
currentLon = position.longitude;
|
||||
lastValidGPSLocation = LatLng(currentLat, currentLon);
|
||||
okToUseGPS = true;
|
||||
lastGPSDataReceivedTime = DateTime.now();
|
||||
hasReceivedGPSData = true;
|
||||
|
||||
} else {
|
||||
checkGPSDataReceived();
|
||||
|
||||
// 信号強度が低い場合、最後に取得した高いまたは中程度の位置情報を使用
|
||||
// 但し、最初から高精度のものがない場合、どうするか?
|
||||
//
|
||||
// GPSデータが受信できない場合、最後に有効なGPSデータを使用
|
||||
position = LocationMarkerPosition(
|
||||
latitude: lastValidGPSLocation.latitude,
|
||||
longitude: lastValidGPSLocation.longitude,
|
||||
accuracy: 0,
|
||||
);
|
||||
|
||||
/*
|
||||
if (lastValidLat != 0.0 && lastValidLon != 0.0) {
|
||||
currentLat = lastValidLat;
|
||||
currentLon = lastValidLon;
|
||||
@ -1085,6 +1167,7 @@ class DestinationController extends GetxController {
|
||||
backgroundColor: Colors.yellow,
|
||||
);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
if (okToUseGPS && position != null) {
|
||||
@ -1103,7 +1186,7 @@ class DestinationController extends GetxController {
|
||||
Duration difference = lastGPSCollectedTime.difference(DateTime.now())
|
||||
.abs();
|
||||
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過(普通に歩くスピード)
|
||||
if (difference.inSeconds >= 10 || distanceToDest >= 10) {
|
||||
if (difference.inSeconds >= 10 && distanceToDest >= 10) {
|
||||
// print(
|
||||
// "^^^^^^^^ GPS data collected ${DateFormat('kk:mm:ss \n EEE d MMM').format(DateTime.now())}, ^^^ ${position.latitude}, ${position.longitude}");
|
||||
|
||||
@ -1259,21 +1342,29 @@ class DestinationController extends GetxController {
|
||||
void fixMapBound(String token) {
|
||||
//String _token = indexController.currentUser[0]["token"];
|
||||
indexController.switchPage(AppPages.INDEX);
|
||||
LocationService.getLocationsExt(token).then((value) {
|
||||
if (value != null) {
|
||||
//print("--- loc ext is - $value ----");
|
||||
LatLngBounds bnds = LatLngBounds(
|
||||
LatLng(value[1], value[0]), LatLng(value[3], value[2]));
|
||||
//print("--- bnds is - $bnds ----");
|
||||
indexController.mapController.fitBounds(
|
||||
bnds,
|
||||
);
|
||||
indexController.currentBound.clear();
|
||||
indexController.currentBound.add(bnds);
|
||||
indexController.loadLocationsBound();
|
||||
centerMapToCurrentLocation();
|
||||
}
|
||||
});
|
||||
|
||||
if (isMapControllerReady) {
|
||||
LocationService.getLocationsExt(token).then((value) {
|
||||
if (value != null) {
|
||||
//print("--- loc ext is - $value ----");
|
||||
LatLngBounds bnds = LatLngBounds(
|
||||
LatLng(value[1], value[0]), LatLng(value[3], value[2]));
|
||||
//print("--- bnds is - $bnds ----");
|
||||
indexController.mapController.fitBounds(
|
||||
bnds,
|
||||
);
|
||||
indexController.currentBound.clear();
|
||||
indexController.currentBound.add(bnds);
|
||||
indexController.loadLocationsBound();
|
||||
centerMapToCurrentLocation();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// MapControllerの初期化が完了していない場合は、遅延して再試行
|
||||
Future.delayed(Duration(milliseconds: 100), () {
|
||||
fixMapBound(token);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@ import 'package:rogapp/routes/app_pages.dart';
|
||||
import 'package:rogapp/services/auth_service.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
// SafeAreaウィジェットを使用して、画面の安全領域内にメニューを表示しています。
|
||||
// Columnウィジェットを使用して、メニューアイテムを縦に並べています。
|
||||
//
|
||||
class DrawerPage extends StatelessWidget {
|
||||
DrawerPage({Key? key}) : super(key: key);
|
||||
|
||||
@ -13,9 +16,21 @@ class DrawerPage extends StatelessWidget {
|
||||
|
||||
// 要検討:URLの起動に失敗した場合のエラーハンドリングが不十分です。適切なエラーメッセージを表示するなどの処理を追加してください。
|
||||
//
|
||||
/*
|
||||
void _launchURL(url) async {
|
||||
if (!await launchUrl(url)) throw 'Could not launch $url';
|
||||
}
|
||||
*/
|
||||
|
||||
void _launchURL(String urlString) async {
|
||||
Uri url = Uri.parse(urlString);
|
||||
if (await canLaunchUrl(url)) {
|
||||
await launchUrl(url);
|
||||
} else {
|
||||
throw 'Could not launch $url';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -26,6 +41,9 @@ class DrawerPage extends StatelessWidget {
|
||||
// space to fit everything.
|
||||
child: Column(
|
||||
children: [
|
||||
// 最初のアイテムは、ユーザーのログイン状態に応じて表示が変わります。
|
||||
// ユーザーがログインしていない場合は、"drawer_title".trというテキストを表示します。
|
||||
// ユーザーがログインしている場合は、ユーザーのメールアドレスを表示します。
|
||||
Container(
|
||||
height: 100,
|
||||
color: Colors.amber,
|
||||
@ -47,6 +65,9 @@ class DrawerPage extends StatelessWidget {
|
||||
),
|
||||
)),
|
||||
),
|
||||
// 次に、IndexControllerのcurrentUserリストが空かどうかに応じて、ログインまたはログアウトのアイテムを表示します。
|
||||
// currentUserリストが空の場合は、"login".trというテキストのログインアイテムを表示し、タップするとAppPages.LOGINにナビゲートします。
|
||||
// currentUserリストが空でない場合は、"logout".trというテキストのログアウトアイテムを表示し、タップするとindexController.logout()を呼び出してログアウトし、AppPages.LOGINにナビゲートします。
|
||||
Obx(() => indexController.currentUser.isEmpty
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.login),
|
||||
@ -63,6 +84,8 @@ class DrawerPage extends StatelessWidget {
|
||||
Get.toNamed(AppPages.LOGIN);
|
||||
},
|
||||
)),
|
||||
// パスワード変更のアイテムは、ユーザーがログインしている場合にのみ表示されます。
|
||||
// "change_password".trというテキストを表示し、タップするとAppPages.CHANGE_PASSWORDにナビゲートします。
|
||||
indexController.currentUser.isNotEmpty
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.password),
|
||||
@ -75,6 +98,8 @@ class DrawerPage extends StatelessWidget {
|
||||
width: 0,
|
||||
height: 0,
|
||||
),
|
||||
// サインアップのアイテムは、ユーザーがログインしていない場合にのみ表示されます。
|
||||
// "sign_up".trというテキストを表示し、タップするとAppPages.REGISTERにナビゲートします。
|
||||
indexController.currentUser.isEmpty
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.person),
|
||||
@ -87,6 +112,8 @@ class DrawerPage extends StatelessWidget {
|
||||
width: 0,
|
||||
height: 0,
|
||||
),
|
||||
// リセットのアイテムは、ユーザーがログインしている場合にのみ表示されます。
|
||||
// タップすると、確認ダイアログを表示し、ユーザーがリセットを確認するとDestinationControllerのresetRogaining()メソッドを呼び出してゲームデータをリセットします。
|
||||
indexController.currentUser.isNotEmpty
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.password),
|
||||
@ -95,7 +122,7 @@ class DrawerPage extends StatelessWidget {
|
||||
// 要検討:リセット操作の確認メッセージをローカライズすることを検討してください。
|
||||
//
|
||||
Get.defaultDialog(
|
||||
title: "よろしいですか、リセットしますか?",
|
||||
title: "リセットしますがよろしいですか?",
|
||||
middleText: "これにより、すべてのゲーム データが削除され、すべての状態が削除されます",
|
||||
textConfirm: "確認する",
|
||||
textCancel: "キャンセルする",
|
||||
@ -114,26 +141,39 @@ class DrawerPage extends StatelessWidget {
|
||||
width: 0,
|
||||
height: 0,
|
||||
),
|
||||
// アカウント削除のアイテムは、ユーザーがログインしている場合にのみ表示されます。
|
||||
// "delete_account".trというテキストを表示し、タップするとAuthService.deleteUser()を呼び出してアカウントを削除し、AppPages.TRAVELにナビゲートします。
|
||||
indexController.currentUser.isNotEmpty
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.delete_forever),
|
||||
title: Text("delete_account".tr),
|
||||
onTap: () {
|
||||
String token = indexController.currentUser[0]['token'];
|
||||
AuthService.deleteUser(token).then((value) {
|
||||
if (value.isNotEmpty) {
|
||||
indexController.logout();
|
||||
Get.toNamed(AppPages.TRAVEL);
|
||||
Get.snackbar("accounted_deleted".tr,
|
||||
"account_deleted_message".tr);
|
||||
}
|
||||
});
|
||||
Get.defaultDialog(
|
||||
title: "アカウントを削除しますがよろしいですか?",
|
||||
middleText: "これにより、アカウント情報とすべてのゲーム データが削除され、すべての状態が削除されます",
|
||||
textConfirm: "確認する",
|
||||
textCancel: "キャンセルする",
|
||||
onCancel: () => Get.back(),
|
||||
onConfirm: () {
|
||||
String token = indexController.currentUser[0]['token'];
|
||||
AuthService.deleteUser(token).then((value) {
|
||||
if (value.isNotEmpty) {
|
||||
indexController.logout();
|
||||
Get.toNamed(AppPages.TRAVEL);
|
||||
Get.snackbar("accounted_deleted".tr,
|
||||
"account_deleted_message".tr);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
: const SizedBox(
|
||||
width: 0,
|
||||
height: 0,
|
||||
),
|
||||
// ユーザーデータ削除のアイテムは、ユーザーがログインしている場合にのみ表示されます。
|
||||
// タップすると、AuthService.deleteUser()を呼び出してユーザーデータを削除します。
|
||||
indexController.currentUser.isNotEmpty
|
||||
? ListTile(
|
||||
// 要検討:アカウント削除のリクエストが失敗した場合のエラーハンドリングを追加することをお勧めします。
|
||||
@ -141,13 +181,21 @@ class DrawerPage extends StatelessWidget {
|
||||
leading: const Icon(Icons.delete_forever),
|
||||
title: Text("ユーザーデータを削除する".tr),
|
||||
onTap: () {
|
||||
String token = indexController.currentUser[0]['token'];
|
||||
AuthService.deleteUser(token).then((value) {
|
||||
Get.snackbar("ユーザーデータを削除する",
|
||||
Get.defaultDialog(
|
||||
title: "アカウントを削除しますがよろしいですか?",
|
||||
middleText: "これにより、アカウント情報とすべてのゲーム データが削除され、すべての状態が削除されます",
|
||||
textConfirm: "確認する",
|
||||
textCancel: "キャンセルする",
|
||||
onCancel: () => Get.back(),
|
||||
onConfirm: () {
|
||||
String token = indexController.currentUser[0]['token'];
|
||||
AuthService.deleteUser(token).then((value) {
|
||||
Get.snackbar("ユーザーデータを削除する",
|
||||
"データを削除するためにユーザーの同意が設定されています アプリとサーバーでユーザーデータが削除されました");
|
||||
});
|
||||
},
|
||||
)
|
||||
});
|
||||
});
|
||||
},
|
||||
)
|
||||
: const SizedBox(
|
||||
width: 0,
|
||||
height: 0,
|
||||
@ -167,18 +215,23 @@ class DrawerPage extends StatelessWidget {
|
||||
// title: Text("point_rank".tr),
|
||||
// onTap: (){},
|
||||
// ),
|
||||
|
||||
// "rog_web".trというテキストのアイテムは、ユーザーがログインしている場合にのみ表示されます。
|
||||
// タップすると、_launchURL()メソッドを呼び出して外部のウェブサイトを開きます。
|
||||
indexController.currentUser.isNotEmpty
|
||||
? ListTile(
|
||||
leading: const Icon(Icons.featured_video),
|
||||
title: Text("rog_web".tr),
|
||||
onTap: () {
|
||||
_launchURL("https://www.gifuai.net/?page_id=17397");
|
||||
_launchURL("https://www.gifuai.net/?page_id=60043");
|
||||
},
|
||||
)
|
||||
: const SizedBox(
|
||||
width: 0,
|
||||
height: 0,
|
||||
),
|
||||
// "privacy".trというテキストのアイテムは、常に表示されます。
|
||||
// タップすると、_launchURL()メソッドを呼び出してプライバシーポリシーのURLを開きます。
|
||||
ListTile(
|
||||
leading: const Icon(Icons.privacy_tip),
|
||||
title: Text("privacy".tr),
|
||||
|
||||
Reference in New Issue
Block a user