Skip to content

Commit

Permalink
M3-107 Feat : 안드로이드 만보기 구현 완료
Browse files Browse the repository at this point in the history
  • Loading branch information
koomin1227 committed Jul 6, 2024
1 parent 2e9caa6 commit 317cb44
Show file tree
Hide file tree
Showing 7 changed files with 160 additions and 136 deletions.
4 changes: 2 additions & 2 deletions ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions lib/controllers/walking_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
}
Expand Down
10 changes: 0 additions & 10 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,21 @@ 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<void> 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 {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
var test = WalkingServiceFactory.getWalkingService();
print('메인 생성${test.hashCode}');
initForegroundTask();
// initForegroundTask();
return GetMaterialApp(
title: 'Ground Flip',
theme: ThemeData(
Expand Down
98 changes: 33 additions & 65 deletions lib/service/android_walking_service.dart
Original file line number Diff line number Diff line change
@@ -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<StepCount> _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<void> 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<int> getCurrentStep() {
return Future.value(currentSteps);
Expand All @@ -93,4 +34,31 @@ class AndroidWalkingService implements WalkingService {
) {
return Future.value([1500, 2500, 3500, 4500, 5500, 6500, 7500]);
}

Future<void> _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;
}
}
151 changes: 108 additions & 43 deletions lib/utils/android_notification.dart
Original file line number Diff line number Diff line change
@@ -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<void> 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,
);
}
Expand All @@ -51,19 +49,86 @@ void startCallback() {
}

class FirstTaskHandler extends TaskHandler {
AndroidWalkingService androidWalkingService = AndroidWalkingService();
SendPort? _sendPort;
late Stream<StepCount> _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
Expand Down
6 changes: 0 additions & 6 deletions lib/utils/walking_service_factory.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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('지원하지 않는 플랫폼입니다.');
Expand Down
Loading

0 comments on commit 317cb44

Please sign in to comment.