Android のバックグラウンドGPSを組み込み

This commit is contained in:
2024-05-06 00:06:38 +09:00
parent 7a97127a19
commit dd9343bef7
89 changed files with 521 additions and 273 deletions

View File

@ -52,7 +52,12 @@ void saveGameState() async {
Get.find<DestinationController>();
IndexController indexController = Get.find<IndexController>();
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
debugPrint("indexController.currentUser = ${indexController.currentUser}");
if(indexController.currentUser.isNotEmpty) {
pref.setInt("user_id", indexController.currentUser[0]["user"]["id"]);
}else{
debugPrint("User is empty....");
}
pref.setBool("is_in_rog", destinationController.isInRog.value);
pref.setBool(
"rogaining_counted", destinationController.rogainingCounted.value);
@ -79,6 +84,8 @@ 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}, currentUser=${currUserId}");
if (indexController.currentUser.isNotEmpty &&
indexController.currentUser[0]["user"]["id"] == savedUserId) {
DestinationController destinationController =
@ -127,11 +134,15 @@ void main() async {
// startMemoryMonitoring(); // 2024-4-8 Akira: メモリ使用量のチェックを開始 See #2810
Get.put(SettingsController()); // これを追加
/*
runZonedGuarded(() {
runApp(const ProviderScope(child: MyApp()));
}, (error, stackTrace) {
ErrorService.reportError(error, stackTrace, deviceInfo);
});
*/
runApp(const ProviderScope(child: MyApp()));
//runApp(const MyApp());
}
@ -307,6 +318,11 @@ Future<void> stopBackgroundTracking() async {
debugPrint("バックグラウンド処理:停止しました。");
await positionStream?.cancel();
positionStream = null;
}else if(Platform.isAndroid && background==true){
background=false;
debugPrint("バックグラウンド処理:停止しました。");
await positionStream?.cancel();
positionStream = null;
}
}
@ -354,38 +370,54 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// Get.find<DestinationController>();
switch (state) {
case AppLifecycleState.resumed:
// Foreground に戻った時の処理
debugPrint(" ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました");
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
// バックグラウンド処理を停止
// バックグラウンド処理を停止
if (Platform.isIOS && destinationController.isRunningBackgroundGPS) {
// Foreground に戻った時の処理
debugPrint(" ==(Status Changed)==> RESUMED. フォアグラウンドに戻りました");
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
stopBackgroundTracking();
destinationController.isRunningBackgroundGPS=false;
destinationController.restartGPS();
} else if (Platform.isAndroid) {
const platform = MethodChannel('location');
platform.invokeMethod('stopLocationService');
} else if (Platform.isAndroid ) {
if( destinationController.isRunningBackgroundGPS ){
const platform = MethodChannel('location');
platform.invokeMethod('stopLocationService');
destinationController.isRunningBackgroundGPS=false;
destinationController.restartGPS();
debugPrint("stopped android location service..");
}
debugPrint("==(Status Changed)==> RESUMED. android フォアグラウンドに戻りました");
locationController.resumePositionStream();
//print("RESUMED");
restoreGame();
}else{
debugPrint("==(Status Changed)==> RESUMED 不明状態");
}
break;
case AppLifecycleState.inactive:
// アプリが非アクティブになったときに発生します。
// これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。
debugPrint(" ==(Status Changed)==> PAUSED. 非アクティブ処理。");
//locationController.resumePositionStream();
// 追加: フロントエンドのGPS信号のlistenを停止
locationController.stopPositionStream();
if (Platform.isIOS && !destinationController.isRunningBackgroundGPS) { // iOSはバックグラウンドでもフロントの処理が生きている。
// これは、別のアプリやシステムのオーバーレイ(着信通話やアラームなど)によって一時的に中断された状態です。
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
//locationController.resumePositionStream();
// 追加: フロントエンドのGPS信号のlistenを停止
locationController.stopPositionStream();
if (Platform.isIOS ) { // iOSはバックグラウンドでもフロントの処理が生きている。
destinationController.isRunningBackgroundGPS=true;
startBackgroundTracking();
}else if(Platform.isAndroid){
const platform = MethodChannel('location');
platform.invokeMethod('startLocationService');
}else if(Platform.isAndroid && !destinationController.isRunningBackgroundGPS){
debugPrint(" ==(Status Changed)==> INACTIVE. 非アクティブ処理。");
}else{
debugPrint("==(Status Changed)==> INACTIVE 不明状態");
}
saveGameState();
break;
@ -393,6 +425,19 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
// バックグラウンドに移行したときの処理
//locationController.resumePositionStream();
debugPrint(" ==(Status Changed)==> PAUSED. バックグラウンド処理。");
if (Platform.isIOS && !destinationController.isRunningBackgroundGPS) {
debugPrint("iOS already running background GPS processing when it's inactive");
} else if(Platform.isAndroid && !destinationController.isRunningBackgroundGPS) {
debugPrint(
" ==(Status Changed)==> PAUSED. Android バックグラウンド処理。");
locationController.stopPositionStream();
const platform = MethodChannel('location');
platform.invokeMethod('startLocationService');
//platform.invokeMethod('stopLocationService');
destinationController.isRunningBackgroundGPS = true;
//startBackgroundTracking();
}
saveGameState();
break;
case AppLifecycleState.detached:

View File

@ -112,7 +112,7 @@ class DestinationController extends GetxController {
DateTime lastGPSDataReceivedTime = DateTime.now();
DateTime lastPopupShownTime = DateTime.now().subtract(Duration(minutes: 10));
bool isPopupShown = false;
bool hasReceivedGPSData = false;
bool hasReceivedGPSData = true;
var isCheckingIn = false.obs; // チェックイン操作中はtrueになり、重複してポップアップが出ないようにするもの。
@ -161,31 +161,36 @@ class DestinationController extends GetxController {
*/
void showGPSDataNotReceivedPopup() {
Get.dialog(
AlertDialog(
title: Text('GPS信号が受信できません'),
content: Text('GPS信号が受信できる場所に移動してください。'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('OK'),
),
],
),
);
if (Get.context != null) {
Get.dialog(
AlertDialog(
title: Text('GPS信号が受信できません'),
content: Text('GPS信号が受信できる場所に移動してください。'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text('OK'),
),
],
),
);
} else {
// Get.contextがnullの場合の処理を追加
print('GPS signal not received, but context is null');
}
}
// 最後に有効なGPSデータを受け取ってから10分以上経過している場合にのみメッセージを表示するようにします。
//
void checkGPSDataReceived() {
if (!hasReceivedGPSData) {
// GPS信号を全く受信していない。
if (!isPopupShown) {
// ポップアップしていない。
showGPSDataNotReceivedPopup();
lastPopupShownTime = DateTime.now();
isPopupShown = true;
}
//debugPrint("GPS信号を全く受信していない。");
if (!isPopupShown) {
// ポップアップしていない。
showGPSDataNotReceivedPopup();
lastPopupShownTime = DateTime.now();
isPopupShown = true;
}
} else {
if (DateTime.now().difference(lastGPSDataReceivedTime).inSeconds >= 600) {
// 前回GPS信号を受信してから10分経過。
@ -946,7 +951,7 @@ class DestinationController extends GetxController {
is_checkin: isCheckin,
created_at: DateTime.now().millisecondsSinceEpoch);
var res = await db.insertGps(gps_data);
//debugPrint("Saved GPS data into DB...");
debugPrint("Saved GPS data into DB...:${gps_data}");
}
} catch (err) {
print("errr ready gps ${err}");
@ -1188,7 +1193,7 @@ class DestinationController extends GetxController {
// ゲームを開始する関数です。
//
Future<void> startGame() async {
//print("------ starting game ------");
debugPrint("------ starting game ------");
if (game_started == false) {
await checkForCheckin();
}
@ -1206,6 +1211,7 @@ class DestinationController extends GetxController {
//
bool inError=false;
bool isRunningBackgroundGPS=false;
int activeEngineCount = 0;
@override
void onInit() async {
@ -1215,6 +1221,8 @@ class DestinationController extends GetxController {
// MapControllerの初期化完了を待機するフラグを設定
WidgetsBinding.instance.addPostFrameCallback((_) {
//checkGPSDataReceived(); removed 2024-5-4
isMapControllerReady = true;
});
@ -1280,7 +1288,7 @@ class DestinationController extends GetxController {
startGame();
checkGPSDataReceived();
//checkGPSDataReceived();
}
void restartGPS(){
@ -1308,8 +1316,11 @@ class DestinationController extends GetxController {
//
double prevLat = 0.0; // 直前の位置
double prevLon = 0.0;
bool gpsDebugMode=false;
void handleLocationUpdate(LocationMarkerPosition? position) async {
//debugPrint("DestinationController.handleLocationUpdate");
try {
//final DestinationController destinationController = Get.find<DestinationController>();
//final signalStrength = locationController.getGpsSignalStrength();
@ -1399,7 +1410,7 @@ class DestinationController extends GetxController {
lastGPSCollectedTime = DateTime.now();
prevLat = position.latitude;
prevLon = position.longitude;
debugPrint("フロントエンドでのGPS保存(時間差:${difference.inSeconds}, 距離差:${distanceToDest}) : Time=${lastGPSCollectedTime}");
gpsDebugMode ? debugPrint("フロントエンドでのGPS保存(時間差:${difference.inSeconds}, 距離差:${distanceToDest}) : Time=${lastGPSCollectedTime}"):null;
}
}
}

View File

@ -104,7 +104,7 @@ class IndexPage extends GetView<IndexController> {
// タップすることでGPS信号の強弱をシミュレーションできるようにする
// Akira 2024-4-5
//
/*
/*
Obx(() {
if (locationController.isSimulationMode) {
return DropdownButton<String>(
@ -122,34 +122,11 @@ class IndexPage extends GetView<IndexController> {
}).toList(),
);
} else {
return InkWell(
onTap: () {
locationController.setSimulatedSignalStrength('high');
},
child: const Icon(Icons.info),
);
return Container();
}
}),
*/
*/
/*
Obx(() => locationController.isSimulationMode
? DropdownButton<String>(
value: locationController.getSimulatedSignalStrength(),
onChanged: (value) {
locationController.setSimulatedSignalStrength(value!);
},
items: ['low', 'medium', 'high','real']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
)
: Container(),
),
*/
],
),
// bottomNavigationBar: BottomAppBar(

View File

@ -25,24 +25,10 @@ class LoginPage extends StatelessWidget {
backgroundColor: Colors.white,
automaticallyImplyLeading: false,
),
/* 2024-04-03 Updated by Akira . See https://wiki.sumasen.net/issues/2796?issue_count=1&issue_position=1
appBar: AppBar(
elevation: 0,
backgroundColor: Colors.white,
leading: IconButton(
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(
Icons.arrow_back_ios,
size: 20,
color: Colors.black,
)),
),
*/
body: indexController.currentUser.isEmpty
? SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
@ -129,30 +115,6 @@ class LoginPage extends StatelessWidget {
emailController.text,
passwordController.text,
context);
/*
try {
bool isLoggedIn = await indexController.login(emailController.text, passwordController.text,context);
if (isLoggedIn) {
Get.offAllNamed(AppPages.INDEX);
} else {
Get.snackbar(
"Login Failed",
"Invalid email or password",
icon: const Icon(Icons.error, color: Colors.red),
snackPosition: SnackPosition.BOTTOM,
);
}
} catch (e) {
Get.snackbar(
"Error",
"An error occurred during login",
icon: const Icon(Icons.error, color: Colors.red),
snackPosition: SnackPosition.BOTTOM,
);
} finally {
indexController.isLoading.value = false;
}
*/ // ここまで
},
color: Colors.indigoAccent[400],
shape: RoundedRectangleBorder(
@ -165,16 +127,6 @@ class LoginPage extends StatelessWidget {
fontSize: 16,
color: Colors.white70),
),
/*
child: Obx(
() => indexController.isLoading.value
? const CircularProgressIndicator(color: Colors.white)
: const Text(
"ログイン",
style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16, color: Colors.white70),
),
),
*/ // ここまで
),
const SizedBox(
height: 5.0,
@ -223,7 +175,7 @@ class LoginPage extends StatelessWidget {
),
)),
const SizedBox(
height: 5,
height: 3,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
@ -280,6 +232,7 @@ class LoginPage extends StatelessWidget {
),
],
),
)
: TextButton(
onPressed: () {

View File

@ -7,6 +7,8 @@ import 'package:rogapp/pages/index/index_controller.dart';
import 'package:rogapp/utils/const.dart';
class LocationService {
static Future<GeoJSONFeatureCollection?> loadLocationsFor(
String perfecture, String cat) async {
final IndexController indexController = Get.find<IndexController>();

View File

@ -39,6 +39,8 @@ class LocationController extends GetxController {
LatLng? lastValidLocation;
DateTime lastGPSDataReceivedTime = DateTime.now(); // 最後にGPSデータを受け取った時刻
bool gpsDebugMode = false;
/*
// GPSシミュレーション用のメソッドを追加
void setSimulationMode(bool value) {
isSimulationMode = value;
@ -52,7 +54,7 @@ class LocationController extends GetxController {
bool isSimulationMode = false;
// GPS信号強度をシミュレートするための変数
final Rx<String> _simulatedSignalStrength = Rx<String>('low');
final Rx<String> _simulatedSignalStrength = Rx<String>('high');
// GPS信号強度をシミュレートするための関数
void setSimulatedSignalStrength(String strength) {
@ -72,31 +74,38 @@ class LocationController extends GetxController {
return _simulatedSignalStrength.value;
}
*/
//
// ====== Akira , GPS信号強度をシミュレート ==== ここまで
// GPS信号が弱い場合のフラグ. 本番では、true,high にする。
bool isGpsSignalWeak = true;
RxString latestSignalStrength = 'low'.obs;
// GPS信号が弱い場合のフラグ. 本番では、false,high にする。
bool isGpsSignalWeak = false;
RxString latestSignalStrength = 'high'.obs;
//final _latestSignalStrength = 'low'.obs; // 初期値を設定
//String get latestSignalStrength => _latestSignalStrength.value;
Stream<String> get gpsSignalStrengthStream => latestSignalStrength.stream;
bool isRunningBackgroundGPS=false;
int activeEngineCount = 0;
// GPS信号の強弱を判断するメソッドを追加. 10m 以内強、30m以内中、それ以上
//
String getGpsSignalStrength(Position? position) {
if (position == null) {
gpsDebugMode ? debugPrint("getGpsSignalStrength position is null.") : null;
latestSignalStrength.value = "low";
isGpsSignalWeak = true;
return 'low';
}
final accuracy = position.accuracy;
//debugPrint("getGpsSignalStrength : ${accuracy}");
gpsDebugMode ? debugPrint("getGpsSignalStrength : ${accuracy}") : null;
/*
if(isSimulationMode){
return _simulatedSignalStrength.value; // GPS信号強度シミュレーション
}else {
*/
if (accuracy <= 10) {
latestSignalStrength.value = "high";
isGpsSignalWeak = false;
@ -110,7 +119,7 @@ class LocationController extends GetxController {
isGpsSignalWeak = true;
return 'low';
}
}
// }
}
// 現在位置を調整するメソッドを追加
@ -130,7 +139,7 @@ class LocationController extends GetxController {
//debugPrint("=== adjustCurrentLocation (Position:Get and return Valid location:${position} ... )===");
lastValidLocation = LatLng(position.latitude, position.longitude);
}
return lastValidLocation ?? LatLng(position.latitude, position.longitude);
return lastValidLocation ?? LatLng(lastValidLocation!.latitude, lastValidLocation!.longitude);
}
//===== Akira Added 2024-4-9 end
@ -256,7 +265,7 @@ class LocationController extends GetxController {
// 位置情報の設定を行います。z11
// Set up the location options
const locationOptions =
LocationSettings(accuracy: LocationAccuracy.high, distanceFilter: 0);
LocationSettings(accuracy: LocationAccuracy.medium, distanceFilter: 0);
// 既存の位置情報のストリームをキャンセルします。
await positionStream?.cancel();
@ -265,11 +274,14 @@ class LocationController extends GetxController {
//
positionStream = Geolocator.getPositionStream(locationSettings: locationOptions).listen(
(Position? position) {
gpsDebugMode ? debugPrint("Position = ${position}"):null;
final signalStrength = getGpsSignalStrength(position);
if (signalStrength == 'low') {
isGpsSignalWeak = true;
gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null;
} else {
isGpsSignalWeak = false;
gpsDebugMode ? debugPrint("LocationController.getPositionStream : isGpsSignalWeak = ${isGpsSignalWeak}"):null;
}
final DestinationController destinationController = Get.find<DestinationController>();
@ -349,9 +361,15 @@ class LocationController extends GetxController {
void handleLocationUpdate(LocationMarkerPosition? position) async {
if (position != null) {
//debugPrint("position = ${position}");
/*
//debugPrint("locationController.handleLocationUpdate");
try {
if (position != null) {
double currentLat = position.latitude;
double currentLon = position.longitude;
//debugPrint("Flutter: Received GPS signal. Latitude: $currentLat, Longitude: $currentLon");
//debugPrint("position = ${position}");
/*
currentPosition.value = position;
final locationMarkerPosition = LocationMarkerPosition(
latitude: position.latitude,
@ -359,8 +377,13 @@ class LocationController extends GetxController {
accuracy: position.accuracy,
);
*/
lastGPSDataReceivedTime = DateTime.now(); // 最後にGPS信号を受け取った時刻
locationMarkerPositionStreamController.add(position);
lastGPSDataReceivedTime = DateTime.now(); // 最後にGPS信号を受け取った時刻
locationMarkerPositionStreamController.add(position);
}else{
gpsDebugMode ? debugPrint("Flutter: No GPS signal received."):null;
}
} catch( e ) {
debugPrint("Flutter: Error in handleLocationUpdate: $e");
}
}

View File

@ -31,7 +31,7 @@ class StringValues extends Translations{
'no_values': 'No Values',
'email_and_password_required': 'Email and password required',
'rogaining_user_need_tosign_up': "Rogaining participants do need to sign up.",
'add_location': 'Add Location',
'add_location': 'Gifu',
'select_travel_mode':'Select your travel mode',
'walking':'Walking',
'driving': 'Driving',
@ -105,7 +105,7 @@ class StringValues extends Translations{
'no_values': '値なし',
'email_and_password_required': 'メールとパスワードが必要です',
'rogaining_user_need_tosign_up': "ロゲイニング参加者はサインアップの必要はありません。",
'add_location': '目的地選択',
'add_location': '岐阜',
'select_travel_mode':'移動モードを選択してください',
'walking':'歩行',
'driving': '自動車利用',

View File

@ -87,6 +87,7 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
}
void _resetIdleTimer() {
debugPrint("_resetIdleTimer...");
_timer?.cancel();
_startIdleTimer();
}
@ -101,15 +102,20 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
}
// added by Akira
/*
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
debugPrint("MapWidget:didChangeAppLifecycleState...state=${state}");
if (state == AppLifecycleState.resumed) {
_resetTimer();
}
}
*/
// _centerMapOnUser を10秒間でコール
void _startIdleTimer() {
//debugPrint("_startIdleTimer ....");
final settingsController = Get.find<SettingsController>();
if (!settingsController.autoReturnDisabled.value) {
_timer = Timer(settingsController.timerDuration.value, _centerMapOnUser);
@ -118,13 +124,16 @@ class _MapWidgetState extends State<MapWidget> with WidgetsBindingObserver {
// タイマーをリセットして_startIdleTimer をコール
void _resetTimer() {
//debugPrint("_resetTimer ....");
_timer?.cancel();
_startIdleTimer();
}
// マッぷを現在位置を中心にする。
void _centerMapOnUser() {
//debugPrint("_centerMapOnUser ....");
if (mounted) {
//debugPrint("_centerMapOnUser => centering ....");
destinationController.centerMapToCurrentLocation();
}
}