|
...
|
...
|
@@ -404,41 +404,114 @@ class HealthRawDataCoreService { |
|
|
|
result.sleepResults.isEmpty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
final latestRealtimeStressPoint = result.realtimeStressPoints.isEmpty
|
|
|
|
? null
|
|
|
|
: result.realtimeStressPoints.reduce(
|
|
|
|
(a, b) => a.rawEndTime >= b.rawEndTime ? a : b,
|
|
|
|
);
|
|
|
|
var notificationResult = result;
|
|
|
|
var realtimeWindow = const <HealthRawRealtimeStressPoint>[];
|
|
|
|
if (latestRealtimeStressPoint != null) {
|
|
|
|
final shouldResetRealtimeStressPushTime =
|
|
|
|
latestRealtimeStressPoint.isWorkout ||
|
|
|
|
latestRealtimeStressPoint.isWorkoutRecovery;
|
|
|
|
if (shouldResetRealtimeStressPushTime) {
|
|
|
|
await _recordRealtimeStressTimeSafely(
|
|
|
|
userId: result.userId,
|
|
|
|
recordTime: latestRealtimeStressPoint.rawEndTime,
|
|
|
|
);
|
|
|
|
notificationResult = result.copyWith(
|
|
|
|
realtimeStressPoints: const <HealthRawRealtimeStressPoint>[],
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
realtimeWindow = await _queryRealtimeStressNotificationWindowSafely(
|
|
|
|
userId: result.userId,
|
|
|
|
latestRawEndTime: latestRealtimeStressPoint.rawEndTime,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
final record = await _readLocalNotificationRecordSafely(result.userId);
|
|
|
|
final notifications = HealthRawLocalNotificationBuilder(l10n).build(
|
|
|
|
result: notificationResult,
|
|
|
|
hasExistingHrv: hasExistingHrv,
|
|
|
|
realtimeWindow: realtimeWindow,
|
|
|
|
record: record,
|
|
|
|
);
|
|
|
|
if (notifications.isEmpty) return;
|
|
|
|
|
|
|
|
final sentNotifications = <HealthRawLocalNotification>[];
|
|
|
|
for (final notification in notifications) {
|
|
|
|
final sent = await _sendLocalNotificationSafely(
|
|
|
|
userId: result.userId,
|
|
|
|
notification: notification,
|
|
|
|
);
|
|
|
|
if (sent) {
|
|
|
|
sentNotifications.add(notification);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_scheduleRealtimeStressServerPush(sentNotifications);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<HealthRawLocalNotificationRecord> _readLocalNotificationRecordSafely(
|
|
|
|
int userId,
|
|
|
|
) async {
|
|
|
|
try {
|
|
|
|
return await _localNotificationDispatcher.readRecord(userId);
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
_logError('read local notification record failed', error, stackTrace);
|
|
|
|
return const HealthRawLocalNotificationRecord();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<void> _recordRealtimeStressTimeSafely({
|
|
|
|
required int userId,
|
|
|
|
required int recordTime,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
final latestRealtimeStressPoint = result.realtimeStressPoints.isEmpty
|
|
|
|
? null
|
|
|
|
: result.realtimeStressPoints.reduce(
|
|
|
|
(a, b) => a.rawEndTime >= b.rawEndTime ? a : b,
|
|
|
|
);
|
|
|
|
final shouldSkipRealtimeStressNotification =
|
|
|
|
latestRealtimeStressPoint?.isWorkout ?? false;
|
|
|
|
final realtimeWindow = latestRealtimeStressPoint == null ||
|
|
|
|
shouldSkipRealtimeStressNotification
|
|
|
|
? const <HealthRawRealtimeStressPoint>[]
|
|
|
|
: await _localStore.queryRealtimeStressPoints(
|
|
|
|
userId: result.userId,
|
|
|
|
startTime: latestRealtimeStressPoint.rawEndTime -
|
|
|
|
Duration.secondsPerHour +
|
|
|
|
1,
|
|
|
|
endTime: latestRealtimeStressPoint.rawEndTime,
|
|
|
|
);
|
|
|
|
final record = await _localNotificationDispatcher.readRecord(
|
|
|
|
result.userId,
|
|
|
|
await _localNotificationDispatcher.recordRealtimeStressTime(
|
|
|
|
userId: userId,
|
|
|
|
recordTime: recordTime,
|
|
|
|
);
|
|
|
|
final notifications = HealthRawLocalNotificationBuilder(l10n).build(
|
|
|
|
result: result,
|
|
|
|
hasExistingHrv: hasExistingHrv,
|
|
|
|
realtimeWindow: realtimeWindow,
|
|
|
|
record: record,
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
_logError('record realtime stress push time failed', error, stackTrace);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<List<HealthRawRealtimeStressPoint>>
|
|
|
|
_queryRealtimeStressNotificationWindowSafely({
|
|
|
|
required int userId,
|
|
|
|
required int latestRawEndTime,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
return await _localStore.queryRealtimeStressPoints(
|
|
|
|
userId: userId,
|
|
|
|
startTime: latestRawEndTime - Duration.secondsPerHour + 1,
|
|
|
|
endTime: latestRawEndTime,
|
|
|
|
);
|
|
|
|
if (notifications.isEmpty) return;
|
|
|
|
final sentNotifications = await _localNotificationDispatcher.sendAll(
|
|
|
|
userId: result.userId,
|
|
|
|
notifications: notifications,
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
_logError(
|
|
|
|
'query realtime stress notification window failed',
|
|
|
|
error,
|
|
|
|
stackTrace,
|
|
|
|
);
|
|
|
|
return const <HealthRawRealtimeStressPoint>[];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<bool> _sendLocalNotificationSafely({
|
|
|
|
required int userId,
|
|
|
|
required HealthRawLocalNotification notification,
|
|
|
|
}) async {
|
|
|
|
try {
|
|
|
|
return await _localNotificationDispatcher.sendOne(
|
|
|
|
userId: userId,
|
|
|
|
notification: notification,
|
|
|
|
);
|
|
|
|
_scheduleRealtimeStressServerPush(sentNotifications);
|
|
|
|
} catch (error, stackTrace) {
|
|
|
|
_logError('send local health notifications failed', error, stackTrace);
|
|
|
|
_logError('send local health notification failed', error, stackTrace);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
...
|
...
|
|