Files
rog_app/lib/main.dart
2024-09-08 18:16:51 +09:00

1055 lines
37 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 'dart:io';
//import 'dart:convert';
//import 'dart:developer';
import 'package:flutter_map/flutter_map.dart';
import 'package:gifunavi/model/gps_data.dart';
//import 'package:gifunavi/pages/home/home_page.dart';
import 'package:gifunavi/utils/database_gps.dart';
import 'package:flutter/material.dart';
//import 'package:flutter_map_tile_caching/flutter_map_tile_caching.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
//import 'package:vm_service/vm_service.dart';
//import 'package:dart_vm_info/dart_vm_info.dart';
import 'package:timezone/data/latest.dart' as tz;
import 'package:gifunavi/pages/settings/settings_controller.dart';
import 'package:gifunavi/pages/destination/destination_controller.dart';
import 'package:gifunavi/pages/index/index_binding.dart';
import 'package:gifunavi/pages/index/index_controller.dart';
import 'package:gifunavi/routes/app_pages.dart';
import 'package:gifunavi/utils/location_controller.dart';
import 'package:gifunavi/utils/string_values.dart';
import 'package:gifunavi/widgets/debug_widget.dart';
import 'package:shared_preferences/shared_preferences.dart';
// import 'package:is_lock_screen/is_lock_screen.dart';
//import 'package:gifunavi/services/device_info_service.dart';
import 'package:gifunavi/services/error_service.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
//import 'dart:async';
//import 'package:get/get.dart';
import 'package:flutter/services.dart';
import 'package:permission_handler/permission_handler.dart';
import 'pages/permission/permission.dart';
import 'package:gifunavi/services/api_service.dart';
import 'package:gifunavi/provider/cached_tile_provider.dart';
//import 'package:gifunavi/pages/entry/entry_controller.dart';
//import 'package:gifunavi/pages/team/team_controller.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:gifunavi/services/motion_service.dart';
Map<String, dynamic> deviceInfo = {};
/*
void saveGameState() async {
DestinationController destinationController =
Get.find<DestinationController>();
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setBool("is_in_rog", destinationController.isInRog.value);
pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value);
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
}
*/
// 現在のユーザーのIDも一緒に保存するようにします。
Future<void> saveGameState() async {
DestinationController destinationController =
Get.find<DestinationController>();
IndexController indexController = Get.find<IndexController>();
SharedPreferences pref = await SharedPreferences.getInstance();
debugPrint("ゲームステータス保存  = ${indexController.currentUser}");
if(indexController.currentUser.isNotEmpty) {
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
if(indexController.currentUser[0]["user"]["event_date"]!=null) {
final date = indexController.currentUser[0]["user"]["event_date"];
pref.setString('eventDate', date.toIso8601String());
debugPrint("Saved date is ${date} => ${date.toIso8601String()}");
pref.setString('eventCode', indexController.currentUser[0]["user"]["event_code"]);
pref.setString('teamName', indexController.currentUser[0]["user"]["team_name"]);
pref.setString('group', indexController.currentUser[0]["user"]["group"]);
//final zekken = indexController.currentUser[0]["user"]["zekken_number"];
pref.setInt('zekkenNumber', indexController.currentUser[0]["user"]["zekken_number"]);
}
}else{
debugPrint("User is empty....");
}
pref.setBool("is_in_rog", destinationController.isInRog.value);
pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value);
pref.setBool("ready_for_goal", DestinationController.ready_for_goal);
}
// _indexController.currentUser[0]["user"]["event_date"] = entryDate; // 追加2024-8-9
// _indexController.currentUser[0]["user"]["event_code"] = entry.event.eventName;
// _indexController.currentUser[0]["user"]["team_name"] = entry.team.teamName;
// _indexController.currentUser[0]["user"]["group"] = entry.team.category.categoryName;
// _indexController.currentUser[0]["user"]["zekken_number"] = entry.zekkenNumber;
Future<void> restoreGame() async {
SharedPreferences pref = await SharedPreferences.getInstance();
IndexController indexController = Get.find<IndexController>();
int? savedUserId = pref.getInt("user_id");
//int? currUserId = indexController.currentUser[0]['user']['id'];
debugPrint("ゲームステータス再現  savedUserId=${savedUserId}");
if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]["id"] == savedUserId) {
final dateString = pref.getString('eventDate');
if (dateString != null) {
final parsedDate = DateTime.parse(dateString);
final jstDate = tz.TZDateTime.from(parsedDate, tz.getLocation('Asia/Tokyo'));
debugPrint("restore date is ${dateString} => ${jstDate}");
indexController.currentUser[0]["user"]["event_date"] = jstDate;
//indexController.currentUser[0]["user"]["event_date"] = DateTime.parse(dateString);
}
//debugPrint("restore date is ${dateString?} => ${DateTime.parse(dateString)}");
indexController.currentUser[0]["user"]["event_code"] = pref.getString('eventCode');
indexController.currentUser[0]["user"]["team_name"] = pref.getString('teamName');
indexController.currentUser[0]["user"]["group"] = pref.getString('group');
indexController.currentUser[0]["user"]["zekken_number"] = pref.getInt('zekkenNumber');
debugPrint("user = ${indexController.currentUser[0]["user"]}");
DestinationController destinationController = Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value = pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal = pref.getBool("ready_for_goal") ?? false;
//await Get.putAsync(() => ApiService().init());
if (indexController.currentUser[0]["user"]["event_code"] != null) {
indexController.setSelectedEventName(indexController.currentUser[0]["user"]["event_code"]);
} else {
indexController.setSelectedEventName('未参加');
}
}else{
indexController.setSelectedEventName('未参加');
}
}
/*
void restoreGame_new() async {
SharedPreferences pref = await SharedPreferences.getInstance();
IndexController indexController = Get.find<IndexController>();
DestinationController destinationController = Get.find<DestinationController>();
destinationController.skipGps = false;
destinationController.isInRog.value = pref.getBool("is_in_rog") ?? false;
destinationController.rogainingCounted.value = pref.getBool("rogaining_counted") ?? false;
DestinationController.ready_for_goal = pref.getBool("ready_for_goal") ?? false;
int? savedUserId = pref.getInt("user_id");
if (indexController.currentUser.isNotEmpty && indexController.currentUser[0]["user"]["id"] == savedUserId) {
final dateString = pref.getString('eventDate');
if (dateString != null) {
indexController.currentUser[0]["user"]["event_date"] = DateTime.parse(dateString);
}
indexController.currentUser[0]["user"]["event_code"] = pref.getString('eventCode');
indexController.currentUser[0]["user"]["team_name"] = pref.getString('teamName');
indexController.currentUser[0]["user"]["group"] = pref.getString('group');
indexController.currentUser[0]["user"]["zekken_number"] = pref.getInt('zekkenNumber');
if (indexController.currentUser[0]["user"]["event_code"] != null) {
indexController.setSelectedEventName(indexController.currentUser[0]["user"]["event_code"]);
} else {
indexController.setSelectedEventName('未参加');
_showEventSelectionWarning();
Get.toNamed(AppPages.EVENT_ENTRY);
}
} else {
indexController.setSelectedEventName('未参加');
_showEventSelectionWarning();
Get.toNamed(AppPages.EVENT_ENTRY);
}
await Get.putAsync(() => ApiService().init());
}
*/
void _showEventSelectionWarning() {
Get.dialog(
AlertDialog(
title: Text('警告'),
content: Text('イベントを選択してください。'),
actions: [
TextButton(
child: Text('OK'),
onPressed: () => Get.back(),
),
],
),
);
}
// main.dart の上部に追加
const bool isDebugMode = true; // リリース時にfalseに変更
// 各ファイルで使用
void debugLog(String message) {
if (isDebugMode) {
debugPrint('DEBUG: $message');
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isIOS) {
// アプリの起動時にモーション更新を開始
await MotionService.startMotionUpdates();
}
final IndexController _indexController;
FlutterError.onError = (FlutterErrorDetails details) {
FlutterError.presentError(details);
Get.log('Flutter error: ${details.exception}');
Get.log('Stack trace: ${details.stack}');
ErrorService.reportError(details.exception, details.stack ?? StackTrace.current, deviceInfo, LogManager().operationLogs);
};
try {
await initServices();
runApp(const ProviderScope(child: MyApp()));
}catch(e, stackTrace){
print('Error during initialization: $e');
print('Stack trace: $stackTrace');
// エラーが発生した場合、エラー画面を表示
runApp(ErrorApp(error: e.toString()));
}
}
Future <void> _initializeControllers() async {
final stopwatch = Stopwatch()..start();
const timeout = Duration(seconds: 30); // タイムアウト時間を10秒から30秒に延長
try {
while (!_areAllControllersRegistered()) {
if (stopwatch.elapsed > timeout) {
throw TimeoutException('Controller initialization timed out');
}
await Future.delayed(const Duration(milliseconds: 100));
}
final LocationController _locationController = Get.find<LocationController>();
final IndexController _indexController = Get.find<IndexController>();
final DestinationController _destinationController = Get.find<DestinationController>();
final PermissionController _permissionController = Get.find<PermissionController>();
print('All controllers initialized successfully');
} catch (e) {
print('Error initializing controllers: $e');
_handleInitializationError();
}
}
bool _areAllControllersRegistered() {
return Get.isRegistered<LocationController>() &&
Get.isRegistered<IndexController>() &&
Get.isRegistered<DestinationController>() &&
Get.isRegistered<PermissionController>();
}
void _handleInitializationError() {
// エラーハンドリングのロジックをここに実装
// 例: エラーダイアログの表示、アプリの再起動など
print("_handleInitializationError");
}
Future<void> initServices() async {
print('Starting services ...');
try {
await Get.putAsync(() => ApiService().init());
//await _initApiService();
debugPrint("1: start ApiService");
if (Platform.isIOS ) {
// コントローラーを初期化
/*
Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
Get.put(SettingsController(), permanent: true);
Get.put(DestinationController(), permanent: true);
Get.put(LocationController(), permanent: true);
*/
// すべてのコントローラーとサービスを非同期で初期化
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
debugPrint("2: start IndexController");
Get.lazyPut(() => MapController());
debugPrint("2: start MapController");
// その他のコントローラーを遅延初期化
Get.lazyPut(() => SettingsController());
debugPrint("2: start SettingsController");
Get.lazyPut(() => DestinationController());
debugPrint("3: start DestinationController");
Get.lazyPut(() => LocationController());
debugPrint("4: start LocationController");
//await _initializeControllers();
debugPrint("2: Controllers initialized");
}else {
// すべてのコントローラーとサービスを非同期で初期化
Get.lazyPut(() => IndexController(apiService: Get.find<ApiService>()));
debugPrint("2: start IndexController");
// その他のコントローラーを遅延初期化
Get.lazyPut(() => SettingsController());
debugPrint("2: start SettingsController");
Get.lazyPut(() => DestinationController());
debugPrint("3: start DestinationController");
Get.lazyPut(() => LocationController());
debugPrint("4: start LocationController");
Get.lazyPut(() => PermissionController());
}
// 非同期処理を並列実行
await Future.wait([
_initTimeZone(),
_initCacheProvider(),
]);
print('=== 5. Initialized TimeZone...');
print('=== 6. CacheProvider started...');
await _checkPermissions();
debugPrint("7: start PermissionController");
//await PermissionController.checkAndRequestPermissions();
}catch(e){
print('Error initializing : $e');
}
print('All services started...');
}
Future<void> _initLocationController() async {
if (!Get.isRegistered<LocationController>()) {
Get.put(LocationController());
}
print('=== 1. LocationController started...');
}
Future<void> _initTimeZone() async {
tz.initializeTimeZones();
}
Future<void> _initCacheProvider() async {
await CacheProvider.initialize();
}
Future<void> _checkPermissions() async {
int attempts = 0;
while (Get.context == null && attempts < 10) {
await Future.delayed(const Duration(milliseconds: 100));
attempts++;
}
if (Get.context != null) {
await PermissionController.checkAndRequestPermissions();
} else {
print('Context is still null, cannot check permissions');
}
//await PermissionController.checkAndRequestPermissions(); // main._checkPermissions
}
Future<void> _initApiService() async {
await Get.putAsync(() => ApiService().init());
//Get.lazyPut(() => ApiService());
}
class ErrorApp extends StatelessWidget {
final String error;
const ErrorApp({super.key, required this.error});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Text('アプリの起動中にエラーが発生しました: $error'),
),
),
);
}
}
Future<void> requestLocationPermission() async {
try {
final status = await Permission.locationAlways.request();
if (status == PermissionStatus.granted) {
print('Location permission granted');
} else {
print('Location permission denied');
//await showLocationPermissionDeniedDialog(); // 追加
}
} catch (e) {
print('Error requesting location permission: $e');
}
}
// メモリ使用量の解説https://qiita.com/hukusuke1007/items/e4e987836412e9bc73b9
/*
// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810
// startMemoryMonitoring関数が5分ごとに呼び出され、メモリ使用量をチェックします。
// メモリ使用量が閾値ここでは500MBを超えた場合、エラーメッセージとスタックトレースをレポートします。
//
void startMemoryMonitoring() {
const threshold = 500 * 1024 * 1024; // 500MB
// メモリ使用量情報を取得
final memoryUsage = MemoryUsage.fromJson(DartVMInfo.getAllocationProfile());
if (memoryUsage.heapUsage > threshold) {
final now = DateTime.now().toIso8601String();
final message = 'High memory usage detected at $now: ${memoryUsage.heapUsage} bytes';
print(message);
reportError(message, StackTrace.current);
showMemoryWarningDialog();
}
Timer(const Duration(minutes: 5), startMemoryMonitoring);
}
class MemoryUsage {
final int heapUsage;
MemoryUsage({required this.heapUsage});
factory MemoryUsage.fromJson(Map<String, dynamic> json) {
return MemoryUsage(
heapUsage: json['heapUsage'] as int,
);
}
}
*/
// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810
// reportError関数でエラーレポートを送信します。具体的な実装は、利用するエラー報告サービスによって異なります。
void reportError(String message, StackTrace stackTrace) async {
// エラーレポートの送信処理を実装
// 例: SentryやFirebase Crashlyticsなどのエラー報告サービスを利用
print("ReportError : $message . stacktrace : $stackTrace");
}
// 2024-4-8 Akira: メモリ使用量のチェックのため追加 See #2810
// showMemoryWarningDialog関数で、メモリ使用量が高い場合にユーザーに警告ダイアログを表示します。
//
void showMemoryWarningDialog() {
if (Get.context != null) {
showDialog(
context: Get.context!,
builder: (context) => AlertDialog(
title: const Text('メモリ使用量の警告'),
content: const Text('アプリのメモリ使用量が高くなっています。アプリを再起動することをお勧めします。'),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
}
}
StreamSubscription<Position>? positionStream;
bool background=false;
DateTime lastGPSCollectedTime=DateTime.now();
String team_name="";
String event_code="";
Future<void> startBackgroundTracking() async {
try {
if (Platform.isIOS && background == false) {
final IndexController indexController = Get.find<IndexController>();
if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]['team_name'] != null) {
team_name = indexController.currentUser[0]["user"]['team_name'];
event_code = indexController.currentUser[0]["user"]["event_code"];
}
background = true;
debugPrint("バックグラウンド処理を開始しました。");
const LocationSettings locationSettings = LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 100,
);
try {
positionStream =
Geolocator.getPositionStream(locationSettings: locationSettings)
.listen((Position? position) async {
if (position != null) {
final lat = position.latitude;
final lng = position.longitude;
//final timestamp = DateTime.now();
final accuracy = position.accuracy;
// GPS信号強度がlowの場合はスキップ
if (accuracy > 100) {
debugPrint(
"GPS signal strength is low. Skipping data saving.");
return;
}
Duration difference = lastGPSCollectedTime.difference(
DateTime.now())
.abs();
// 最後にGPS信号を取得した時刻から10秒以上経過、かつ10m以上経過普通に歩くスピード
//debugPrint("時間差:${difference}");
if (difference.inSeconds >= 10) {
debugPrint(
"バックグラウンドでのGPS取得時の処理(10secおき) count=${difference
.inSeconds}, time=${DateTime.now()}");
// DBにGPSデータを保存 pages/destination/destination_controller.dart
await addGPStoDB(lat, lng);
lastGPSCollectedTime = DateTime.now();
}
}
}, onError: (error) {
if (error is LocationServiceDisabledException) {
print('Location services are disabled');
} else if (error is PermissionDeniedException) {
print('Location permissions are denied');
} else {
print('Location Error: $error');
}
});
} catch (e) {
print('Error starting background tracking: $e');
}
} else if (Platform.isAndroid && background == false) {
background = true;
debugPrint("バックグラウンド処理を開始しました。");
try {
// 位置情報の権限が許可されているかを確認
WidgetsBinding.instance.addPostFrameCallback((_) {
PermissionController.checkAndRequestPermissions();
});
} catch (e) {
print('Error starting background tracking: $e');
}
}
} catch (e) {
print('Error starting background tracking: $e');
// 再試行するか、エラーを適切に処理
}
}
Future<void> addGPStoDB(double la, double ln) async {
//debugPrint("in addGPStoDB ${indexController.currentUser}");
GpsDatabaseHelper db = GpsDatabaseHelper.instance;
try {
GpsData gpsData = GpsData(
id: 0,
team_name: team_name,
event_code: event_code,
lat: la,
lon: ln,
is_checkin: 0,
created_at: DateTime.now().millisecondsSinceEpoch);
var res = await db.insertGps(gpsData);
debugPrint("バックグラウンドでのGPS保存");
} catch (err) {
print("errr ready gps $err");
return;
}
}
Future<void> stopBackgroundTracking() async {
try {
if (Platform.isIOS && background == true) {
background = false;
debugPrint("バックグラウンド処理:停止しました。");
await positionStream?.cancel();
positionStream = null;
} else if (Platform.isAndroid && background == true) {
background = false;
debugPrint("バックグラウンド処理:停止しました。");
const platform = MethodChannel('location');
try {
await platform.invokeMethod('stopLocationService');
} on PlatformException catch (e) {
print("Failed to stop location service: '${e.message}'.");
}
}
} catch(e){
print('Error stopping background tracking: $e');
}
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// This widget is the root of your application.
late final LocationController _locationController;
late final IndexController _indexController;
late final DestinationController _destinationController;
late final PermissionController _permissionController;
Timer? _memoryCheckTimer;
bool _isControllerInitialized = false;
@override
void initState() {
super.initState();
_initializeControllers();
WidgetsBinding.instance.addObserver(this);
//_startMemoryMonitoring();
if (context.mounted) {
// _restoreGameAndInitialize();
}
// ウィジェットが構築された後に権限をチェック
WidgetsBinding.instance.addPostFrameCallback((_) {
PermissionController.checkAndRequestPermissions();
});
debugPrint("Start MyAppState...");
}
Future<void> _restoreGameAndInitialize() async {
await restoreGame();
// ここに他の初期化処理を追加できます
}
Future <void> _initializeControllers() async {
while (!Get.isRegistered<LocationController>() ) {
print("LocationController is not up... ");
await Future.delayed(const Duration(milliseconds: 100));
}
while (!Get.isRegistered<IndexController>() ) {
print("IndexController is not up...");
await Future.delayed(const Duration(milliseconds: 100));
}
while (!Get.isRegistered<DestinationController>() ) {
print("DestinationController is not up...");
await Future.delayed(const Duration(milliseconds: 100));
}
/*
while (!Get.isRegistered<PermissionController>() ) {
print("PermissionController is not up... ");
await Future.delayed(const Duration(milliseconds: 100));
}
*/
while (!Get.isRegistered<LocationController>() ||
!Get.isRegistered<IndexController>() ||
!Get.isRegistered<DestinationController>() ) {
print("LocationController status = Get.isRegistered<LocationController>() ");
await Future.delayed(const Duration(milliseconds: 100));
}
if (!_isControllerInitialized) {
_locationController = Get.find<LocationController>();
_indexController = Get.find<IndexController>();
_destinationController = Get.find<DestinationController>();
//_permissionController = Get.find<PermissionController>();
_isControllerInitialized = true;
}
/*
while (true) {
try {
_locationController = Get.find<LocationController>();
break; // DestinationControllerが見つかったらループを抜ける
} catch (e) {
// DestinationControllerがまだ利用可能でない場合は少し待ってから再試行
await Future.delayed(const Duration(milliseconds: 100));
}
}
*/
/*
if (!Get.isRegistered<LocationController>()) {
_locationController = Get.put(LocationController(), permanent: true);
}
*/
/*
while (true) {
try {
_indexController = Get.find<IndexController>();
break; // DestinationControllerが見つかったらループを抜ける
} catch (e) {
// DestinationControllerがまだ利用可能でない場合は少し待ってから再試行
await Future.delayed(const Duration(milliseconds: 100));
}
}
*/
/*
if (!Get.isRegistered<IndexController>()) {
_indexController = Get.put(IndexController(apiService: Get.find<ApiService>()), permanent: true);
}
*/
/*
while (true) {
try {
_destinationController = Get.find<DestinationController>();
break; // DestinationControllerが見つかったらループを抜ける
} catch (e) {
// DestinationControllerがまだ利用可能でない場合は少し待ってから再試行
await Future.delayed(const Duration(milliseconds: 100));
}
}
*/
/*
if (!Get.isRegistered<DestinationController>()) {
_destinationController =
Get.put(DestinationController(), permanent: true);
}
*/
/*
while (true) {
try {
_permissionController = Get.find<PermissionController>();
break; // DestinationControllerが見つかったらループを抜ける
} catch (e) {
// DestinationControllerがまだ利用可能でない場合は少し待ってから再試行
await Future.delayed(const Duration(milliseconds: 100));
}
}
*/
/*
if (!Get.isRegistered<PermissionController>()) {
_permissionController = Get.put(PermissionController());
}
*/
// 他の必要なコントローラーの初期化
}
void _startMemoryMonitoring() {
/*
_memoryCheckTimer = Timer.periodic(const Duration(seconds: 10), (timer) {
_checkMemoryUsage();
});
*/
}
void _checkMemoryUsage() async {
final memoryInfo = await _getMemoryInfo();
//debugPrint('Current memory usage: ${memoryInfo['used']} MB');
if (memoryInfo['used']! > 100) { // 100MB以上使用している場合
_performMemoryCleanup();
}
}
Future<Map<String, int>> _getMemoryInfo() async {
// プラットフォーム固有のメモリ情報取得ロジックを実装
// この例では仮の値を返しています
return {'total': 1024, 'used': 512};
}
void _performMemoryCleanup() {
/*
debugPrint('Performing memory cleanup');
// キャッシュのクリア
Get.deleteAll(force: false); // 永続的なコントローラーを除外してキャッシュをクリア
imageCache.clear();
imageCache.clearLiveImages();
// 大きなオブジェクトの解放
_clearLargeObjects();
// 未使用のリソースの解放
_releaseUnusedResources();
// ガベージコレクションの促進
_forceGarbageCollection();
debugPrint('Performing memory cleanup');
*/
}
void _clearLargeObjects() {
// 大きなリストやマップをクリア
// 例: myLargeList.clear();
}
void _releaseUnusedResources() {
// 使用していないストリームのクローズ
// 例: myStream?.close();
}
void _forceGarbageCollection() {
/*
Timer(const Duration(seconds: 1), () {
debugPrint('Forcing garbage collection');
// ignore: dead_code
bool didRun = false;
assert(() {
didRun = true;
return true;
}());
if (didRun) {
debugPrint('Garbage collection forced in debug mode');
}
});
*/
}
/*
void showPermissionRequiredDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: Text('権限が必要です'),
content: Text('このアプリは機能するために位置情報の権限が必要です。設定で権限を許可してください。'),
actions: <Widget>[
TextButton(
child: Text('設定を開く'),
onPressed: () {
openAppSettings();
Navigator.of(context).pop();
},
),
TextButton(
child: Text('アプリを終了'),
onPressed: () {
// アプリを終了
Navigator.of(context).pop();
// よりクリーンな終了のために 'flutter_exit_app' のようなプラグインを使用することをお勧めします
// 今回は単にすべてのルートをポップします
Navigator.of(context).popUntil((route) => false);
},
),
],
);
},
);
}
*/
@override
void dispose() {
_isControllerInitialized = false;
WidgetsBinding.instance.removeObserver(this);
_memoryCheckTimer?.cancel();
super.dispose();
}
// void saveGameState() async {
// DestinationController destinationController = Get.find<DestinationController>();
// SharedPreferences pref = await SharedPreferences.getInstance();
// pref.setBool("is_in_rog", destinationController.is_in_rog.value);
// pref.setBool("rogaining_counted", destinationController.rogaining_counted.value);
// }
@override
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
try {
await _initializeControllers();
switch (state) {
case AppLifecycleState.resumed:
if (Platform.isIOS) {
MotionService.startMotionUpdates();
}
//await _onResumed();
await _onResumed();
break;
case AppLifecycleState.inactive:
// アプリが非アクティブになったときに発生します。
await _onInactive();
break;
case AppLifecycleState.paused:
MotionService.stopMotionUpdates();
// バックグラウンドに移行したときの処理
//locationController.resumePositionStream();
await _onPaused();
break;
case AppLifecycleState.detached:
// アプリが終了する直前に発生します。この状態では、アプリはメモリから解放される予定です。
//locationController.resumePositionStream();
await _onDetached();
break;
case AppLifecycleState.hidden:
// Web用の特殊な状態で、モバイルアプリでは発生しません。
//locationController.resumePositionStream();
await _onHidden();
break;
}
}catch(e){
print('Error finding didChangeAppLifecycleState: $e');
_initializeControllers();
}
}
Future<void> _onResumed() async {
debugPrint("==(Status Changed)==> RESUMED");
try {
if (!_isControllerInitialized) {
await _initializeControllers();
}
await stopBackgroundTracking();
_destinationController.restartGPS();
if (Platform.isIOS && _destinationController.isRunningBackgroundGPS) {
_locationController.resumePositionStream();
await restoreGame();
_destinationController.isRunningBackgroundGPS = false;
} else if (Platform.isAndroid) {
if (_destinationController.isRunningBackgroundGPS) {
const platform = MethodChannel('location');
await platform.invokeMethod('stopLocationService');
_destinationController.isRunningBackgroundGPS = false;
_destinationController.restartGPS();
}
_locationController.resumePositionStream();
await restoreGame();
}
} catch(e) {
print('Error in _onResumed: $e');
// 必要に応じて再試行またはエラー処理
}
}
Future<void> _onInactive() async {
try {
debugPrint("==(Status Changed)==> INACTIVE");
if (!_isControllerInitialized) {
await _initializeControllers();
}
if (Platform.isIOS && !_destinationController.isRunningBackgroundGPS) {
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
_locationController.stopPositionStream();
_destinationController.isRunningBackgroundGPS = true;
await startBackgroundTracking();
} else if (Platform.isAndroid &&
!_destinationController.isRunningBackgroundGPS) {
// Android特有の処理があれば追加
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
} else {
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
}
await saveGameState();
} catch (e) {
print('Error in _onInactive: $e');
}
}
Future<void> _onPaused() async {
try {
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
if (!_isControllerInitialized) {
await _initializeControllers();
}
if (Platform.isAndroid &&
!_destinationController.isRunningBackgroundGPS) {
debugPrint(
" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
_locationController.stopPositionStream();
const platform = MethodChannel('location');
await platform.invokeMethod('startLocationService');
_destinationController.isRunningBackgroundGPS = true;
}
await saveGameState();
} catch (e) {
print('Error in _onPaused: $e');
}
}
Future<void> _onDetached() async {
try {
if (!_isControllerInitialized) {
await _initializeControllers();
}
debugPrint(" ==(Status Changed)==> DETACHED アプリは終了します。");
await saveGameState();
// アプリ終了時の追加処理
} catch (e) {
print('Error in _onDetached: $e');
}
}
Future<void> _onHidden() async {
try {
if (!_isControllerInitialized) {
await _initializeControllers();
}
debugPrint(" ==(Status Changed)==> Hidden アプリが隠れた");
await saveGameState();
} catch (e) {
print('Error in _onHidden: $e');
}
}
@override
Widget build(BuildContext context) {
return GetMaterialApp(
translations: StringValues(),
locale: const Locale('ja', 'JP'),
//locale: const Locale('en', 'US'),
fallbackLocale: const Locale('en', 'US'),
title: 'ROGAINING',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: const Color.fromARGB(255, 36, 135, 221)),
useMaterial3: true,
),
debugShowCheckedModeBanner: false,
defaultTransition: Transition.cupertino,
opaqueRoute: Get.isOpaqueRouteDefault,
popGesture: Get.isPopGestureEnable,
transitionDuration: const Duration(milliseconds: 230),
initialBinding: IndexBinding(), //HomeBinding(),
initialRoute: AppPages.PERMISSION,
getPages: AppPages.routes,
enableLog: true,
);
}
}