diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 504b13e4..3b344548 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -8,7 +8,7 @@ PODS: - Flutter - google_maps_flutter_ios (0.0.1): - Flutter - - GoogleMaps (< 9.0, >= 8.4) + - GoogleMaps (< 10.0, >= 8.4) - GoogleMaps (8.4.0): - GoogleMaps/Maps (= 8.4.0) - GoogleMaps/Base (8.4.0) @@ -75,7 +75,7 @@ SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_config: f48f0d47a284f1791aacce2687eabb3309ba7a41 flutter_foreground_task: 21ef182ab0a29a3005cc72cd70e5f45cb7f7f817 - google_maps_flutter_ios: c454f18e0e22df6ac0e9f2a4df340858f5a3680c + google_maps_flutter_ios: 5bc2be60ad012e79b182ce0fb0ef5030a50fb03e GoogleMaps: 8939898920281c649150e0af74aa291c60f2e77d health: 5a380c0f6c4f619535845992993964293962e99e location: d5cf8598915965547c3f36761ae9cc4f4e87d22e diff --git a/lib/controllers/walking_controller.dart b/lib/controllers/walking_controller.dart index 7f974001..1e040e79 100644 --- a/lib/controllers/walking_controller.dart +++ b/lib/controllers/walking_controller.dart @@ -26,8 +26,7 @@ class WalkingController extends GetxController { _initializeWeeklySteps(); _initializeCurrentStep(); - Timer.periodic(Duration(seconds: 10), (timer) { - print('--------------walk update---------------'); + Timer.periodic(Duration(seconds: 1), (timer) { updateCurrentStep(); }); } diff --git a/lib/main.dart b/lib/main.dart index 7696dd4d..46339e5c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,20 +2,14 @@ import 'package:flutter/material.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; -import 'package:ground_flip/utils/android_notification.dart'; import 'screens/main_screen.dart'; -import 'utils/walking_service_factory.dart'; Future main() async { WidgetsFlutterBinding.ensureInitialized(); - await dotenv.load(fileName: ".env"); await GetStorage.init(); runApp(const MyApp()); - // var test = WalkingServiceFactory.getWalkingService(); - // print(test.hashCode); - // initForegroundTask(); } class MyApp extends StatelessWidget { @@ -23,10 +17,6 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - var test = WalkingServiceFactory.getWalkingService(); - print('메인 생성${test.hashCode}'); - initForegroundTask(); - // initForegroundTask(); return GetMaterialApp( title: 'Ground Flip', theme: ThemeData( diff --git a/lib/service/android_walking_service.dart b/lib/service/android_walking_service.dart index ada629ad..22c2e77f 100644 --- a/lib/service/android_walking_service.dart +++ b/lib/service/android_walking_service.dart @@ -1,86 +1,27 @@ import 'dart:async'; +import 'dart:isolate'; -import 'package:get_storage/get_storage.dart'; -import 'package:pedometer/pedometer.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_foreground_task/flutter_foreground_task.dart'; +import '../utils/android_notification.dart'; import '../utils/walking_service.dart'; class AndroidWalkingService implements WalkingService { - final GetStorage localStorage = GetStorage(); - - static String LOCAL_STORE_STEP_KEY = 'pastSteps'; - static int checkDay = 0; - - late Stream _stepCountStream; - - bool isInit = false; + ReceivePort? _receivePort; int currentSteps = 0; - int totalSteps = 0; - int pastSteps = 0; static final AndroidWalkingService _instance = AndroidWalkingService._internal(); AndroidWalkingService._internal() { - // print('생성'); - print('AndroidWalkingService instance created: ${identityHashCode(this)}'); - // initPlatformState(); - // // init(); - Timer.periodic(Duration(minutes: 5), (t) { - print('---------------day init------------'); - if (checkDay != DateTime.now().day) { - resetStepTimer(); - checkDay = DateTime.now().day; - } - }); + _initForegroundWalkingTask(); } factory AndroidWalkingService() { - // initPlatformState(); - // // init(); - // Timer.periodic(Duration(minutes: 5), (t) { - // if (checkDay != DateTime.now().day) { - // resetStepTimer(); - // checkDay = DateTime.now().day; - // } - // }); return _instance; } - Future init() async { - await GetStorage.init(); - } - - void initPlatformState() { - _stepCountStream = Pedometer.stepCountStream.asBroadcastStream(); - _stepCountStream.listen(updateStep).onError(onStepCountError); - isInit = true; - } - - void onStepCountError(error) { - currentSteps = 0; - } - - void updateStep(StepCount event) async { - totalSteps = event.steps; - - int? value = localStorage.read(LOCAL_STORE_STEP_KEY); - if (value == null || value == 0) { - pastSteps = totalSteps; - localStorage.write(LOCAL_STORE_STEP_KEY, totalSteps); - } else { - pastSteps = value; - } - currentSteps = totalSteps - pastSteps; - print(currentSteps); - } - - void resetStepTimer() async { - pastSteps = totalSteps; - currentSteps = 0; - localStorage.write(LOCAL_STORE_STEP_KEY, totalSteps); - } - @override Future getCurrentStep() { return Future.value(currentSteps); @@ -93,4 +34,31 @@ class AndroidWalkingService implements WalkingService { ) { return Future.value([1500, 2500, 3500, 4500, 5500, 6500, 7500]); } + + Future _initForegroundWalkingTask() async { + initForegroundTask(); + final newReceivePort = FlutterForegroundTask.receivePort; + _registerReceivePort(newReceivePort); + } + + bool _registerReceivePort(ReceivePort? newReceivePort) { + if (newReceivePort == null) { + return false; + } + + _closeReceivePort(); + + _receivePort = newReceivePort; + _receivePort?.listen((data) { + currentSteps = data; + debugPrint('current walk: $data'); + }); + + return _receivePort != null; + } + + void _closeReceivePort() { + _receivePort?.close(); + _receivePort = null; + } } diff --git a/lib/utils/android_notification.dart b/lib/utils/android_notification.dart index 816c8e8a..430defe0 100644 --- a/lib/utils/android_notification.dart +++ b/lib/utils/android_notification.dart @@ -1,46 +1,44 @@ +import 'dart:async'; import 'dart:isolate'; import 'package:flutter_foreground_task/flutter_foreground_task.dart'; +import 'package:get_storage/get_storage.dart'; +import 'package:pedometer/pedometer.dart'; -import '../service/android_walking_service.dart'; - -// AndroidWalkingController androidWalkingController = -// Get.put(AndroidWalkingController()); - -// print(androidWalkingService); - -void initForegroundTask() { - AndroidWalkingService androidWalkingService = AndroidWalkingService(); - print('initForeGround 생성 ${androidWalkingService.hashCode}'); - FlutterForegroundTask.init( - androidNotificationOptions: AndroidNotificationOptions( - channelId: 'ground-flip', - channelName: 'ground-flip Notification', - channelDescription: - 'This notification appears when the ground-flip is running.', - channelImportance: NotificationChannelImportance.LOW, - priority: NotificationPriority.LOW, - iconData: const NotificationIconData( - resType: ResourceType.mipmap, - resPrefix: ResourcePrefix.ic, - name: 'launcher', +Future initForegroundTask() async { + if (!await FlutterForegroundTask.isRunningService) { + FlutterForegroundTask.init( + androidNotificationOptions: AndroidNotificationOptions( + channelId: 'ground-flip', + channelName: 'ground-flip Notification', + channelDescription: + 'This notification appears when the ground-flip is running.', + channelImportance: NotificationChannelImportance.LOW, + priority: NotificationPriority.LOW, + iconData: const NotificationIconData( + resType: ResourceType.mipmap, + resPrefix: ResourcePrefix.ic, + name: 'launcher', + ), + isSticky: true, ), - ), - iosNotificationOptions: const IOSNotificationOptions( - showNotification: true, - playSound: false, - ), - foregroundTaskOptions: const ForegroundTaskOptions( - interval: 1000, - isOnceEvent: false, - autoRunOnBoot: true, - allowWakeLock: true, - allowWifiLock: true, - ), - ); + iosNotificationOptions: const IOSNotificationOptions( + showNotification: true, + playSound: false, + ), + foregroundTaskOptions: const ForegroundTaskOptions( + interval: 1000, + isOnceEvent: false, + autoRunOnBoot: true, + allowWakeLock: true, + allowWifiLock: true, + ), + ); + } + FlutterForegroundTask.startService( notificationTitle: "걸음수", - notificationText: androidWalkingService.currentSteps.toString(), + notificationText: "0", callback: startCallback, ); } @@ -51,19 +49,86 @@ void startCallback() { } class FirstTaskHandler extends TaskHandler { - AndroidWalkingService androidWalkingService = AndroidWalkingService(); + SendPort? _sendPort; + late Stream _stepCountStream; + final GetStorage _localStorage = GetStorage(); + static String todayStepKey = 'currentSteps'; + static String lastSavedStepKey = 'lastSteps'; + Timer? _midnightTimer; + int currentSteps = 0; - // print('initForeGround 생성 ${androidWalkingService.hashCode}'); @override - void onStart(DateTime timestamp, SendPort? sendPort) async {} + void onStart(DateTime timestamp, SendPort? sendPort) async { + _sendPort = sendPort; + currentSteps = (await _localStorage.read(todayStepKey)) ?? 0; + _stepCountStream = Pedometer.stepCountStream.asBroadcastStream(); + _stepCountStream.listen(updateStep).onError(onStepCountError); + _initializeMidnightReset(); + } + + void onStepCountError(error) { + currentSteps = 0; + } + + void _initializeMidnightReset() { + DateTime now = DateTime.now(); + DateTime midnight = DateTime(now.year, now.month, now.day + 1); + Duration timeUntilMidnight = midnight.difference(now); + + _midnightTimer = Timer(timeUntilMidnight, () { + _resetStepsAtMidnight(); + _midnightTimer = Timer.periodic(Duration(days: 1), (timer) { + _resetStepsAtMidnight(); + }); + }); + } + + void _resetStepsAtMidnight() { + currentSteps = 0; + _localStorage.write(todayStepKey, 0); - @override - void onRepeatEvent(DateTime timestamp, SendPort? sendPort) async { - int updateStep = androidWalkingService.currentSteps; FlutterForegroundTask.updateService( notificationTitle: "걸음수", - notificationText: updateStep.toString(), + notificationText: currentSteps.toString(), ); + _sendPort?.send(currentSteps); + } + + updateStep(StepCount event) async { + int currentTotalStep = event.steps; + + int lastSavedStepCount = await _localStorage.read(lastSavedStepKey); + int todaySteps = await _localStorage.read(todayStepKey) ?? 0; + + if (lastSavedStepCount == null) { + _localStorage.write(lastSavedStepKey, currentTotalStep); + lastSavedStepCount = currentTotalStep; + } + + if (currentTotalStep < lastSavedStepCount) { + todaySteps += currentTotalStep; + lastSavedStepCount = currentTotalStep; + _localStorage.write(lastSavedStepKey, currentTotalStep); + } else { + int deltaSteps = currentTotalStep - lastSavedStepCount; + todaySteps += deltaSteps; + lastSavedStepCount = currentTotalStep; + } + + _localStorage.write(todayStepKey, todaySteps); + _localStorage.write(lastSavedStepKey, currentTotalStep); + currentSteps = todaySteps; + + FlutterForegroundTask.updateService( + notificationTitle: "걸음수", + notificationText: currentSteps.toString(), + ); + _sendPort?.send(currentSteps); + } + + @override + void onRepeatEvent(DateTime timestamp, SendPort? sendPort) async { + sendPort?.send(currentSteps); } @override diff --git a/lib/utils/walking_service_factory.dart b/lib/utils/walking_service_factory.dart index 0fc8de17..2e1ef0f5 100644 --- a/lib/utils/walking_service_factory.dart +++ b/lib/utils/walking_service_factory.dart @@ -9,12 +9,6 @@ class WalkingServiceFactory { if (Platform.isIOS) { return IosWalkingService(); } else if (Platform.isAndroid) { - var androidWalkingService = AndroidWalkingService(); - if (androidWalkingService.isInit == false) { - print('초기화'); - androidWalkingService.initPlatformState(); - } - print('팩토리 생성 ${androidWalkingService.hashCode}'); return AndroidWalkingService(); } else { throw UnsupportedError('지원하지 않는 플랫폼입니다.'); diff --git a/pubspec.lock b/pubspec.lock index 9a50d577..8a528311 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -85,10 +85,18 @@ packages: dependency: "direct main" description: name: dio - sha256: "11e40df547d418cc0c4900a9318b26304e665da6fa4755399a9ff9efd09034b5" + sha256: e17f6b3097b8c51b72c74c9f071a605c47bcc8893839bd66732457a5ebe73714 url: "https://pub.dev" source: hosted - version: "5.4.3+1" + version: "5.5.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "36c5b2d79eb17cdae41e974b7a8284fec631651d2a6f39a8a2ff22327e90aeac" + url: "https://pub.dev" + source: hosted + version: "1.0.1" dropdown_button2: dependency: "direct main" description: @@ -228,18 +236,18 @@ packages: dependency: transitive description: name: google_maps_flutter_android - sha256: ca7619fef5009d1ef35b7a67c3c5841499b755156a1986f442629338e7b51a5f + sha256: "6b7f7730960f9adb1b77a530572182451d66e30808ebb052665e64c4276a8f0e" url: "https://pub.dev" source: hosted - version: "2.9.1" + version: "2.11.1" google_maps_flutter_ios: dependency: transitive description: name: google_maps_flutter_ios - sha256: d2d63ae17297a5b045ec115572c5a86fa4e53bb6eceaa0c6d200ac5ca69bfca4 + sha256: "1043d0a4ad52612444b24edbd2d61f6de40e01e842fe2a9248be53fa70bc7047" url: "https://pub.dev" source: hosted - version: "2.7.0" + version: "2.8.1" google_maps_flutter_platform_interface: dependency: transitive description: @@ -364,10 +372,10 @@ packages: dependency: transitive description: name: location_web - sha256: "15ad7b4c8a9f55abee513373755e093a40c04d7e24fc1b4f89676fe99523d034" + sha256: "49dda13d415c4603c5775a33fb22f575e0aa3f0ec2916644ddcd722db31ee884" url: "https://pub.dev" source: hosted - version: "5.0.1" + version: "5.0.2" matcher: dependency: transitive description: