|
|
|
1
|
+class OhosHealthRawDataType {
|
|
|
|
2
|
+ const OhosHealthRawDataType._();
|
|
|
|
3
|
+
|
|
|
|
4
|
+ static const int sleepAnalysis = -1;
|
|
|
|
5
|
+ static const int activitySummary = -2;
|
|
|
|
6
|
+ static const int workout = -3;
|
|
|
|
7
|
+}
|
|
|
|
8
|
+
|
|
|
|
9
|
+class OhosHealthRawDataSyncService {
|
|
|
|
10
|
+ OhosHealthRawDataSyncService({
|
|
|
|
11
|
+ OhosHealthRawDataRemoteDataSource? remoteDataSource,
|
|
|
|
12
|
+ OhosHealthRawDataLocalStore? localStore,
|
|
|
|
13
|
+ DateTime Function()? nowProvider,
|
|
|
|
14
|
+ }) : _remoteDataSource =
|
|
|
|
15
|
+ remoteDataSource ?? const TodoOhosHealthRawDataRemoteDataSource(),
|
|
|
|
16
|
+ _localStore = localStore ?? const TodoOhosHealthRawDataLocalStore(),
|
|
|
|
17
|
+ _nowProvider = nowProvider ?? DateTime.now;
|
|
|
|
18
|
+
|
|
|
|
19
|
+ static const int defaultLookbackDays = 183;
|
|
|
|
20
|
+
|
|
|
|
21
|
+ final OhosHealthRawDataRemoteDataSource _remoteDataSource;
|
|
|
|
22
|
+ final OhosHealthRawDataLocalStore _localStore;
|
|
|
|
23
|
+ final DateTime Function() _nowProvider;
|
|
|
|
24
|
+
|
|
|
|
25
|
+ Future<OhosHealthRawDataSyncResult> syncRawData({
|
|
|
|
26
|
+ required int dataType,
|
|
|
|
27
|
+ int? startTime,
|
|
|
|
28
|
+ int? endTime,
|
|
|
|
29
|
+ }) async {
|
|
|
|
30
|
+ final resolvedEndTime = endTime ?? _unixSeconds(_nowProvider());
|
|
|
|
31
|
+ final resolvedStartTime = startTime ??
|
|
|
|
32
|
+ await _resolveStartTime(
|
|
|
|
33
|
+ dataType: dataType,
|
|
|
|
34
|
+ endTime: resolvedEndTime,
|
|
|
|
35
|
+ );
|
|
|
|
36
|
+
|
|
|
|
37
|
+ if (resolvedEndTime < resolvedStartTime) {
|
|
|
|
38
|
+ throw ArgumentError.value(endTime, 'endTime');
|
|
|
|
39
|
+ }
|
|
|
|
40
|
+
|
|
|
|
41
|
+ String? pageToken;
|
|
|
|
42
|
+ var pageCount = 0;
|
|
|
|
43
|
+ var storedCount = 0;
|
|
|
|
44
|
+
|
|
|
|
45
|
+ do {
|
|
|
|
46
|
+ final page = await _remoteDataSource.fetchRawDataPage(
|
|
|
|
47
|
+ dataType: dataType,
|
|
|
|
48
|
+ startTime: resolvedStartTime,
|
|
|
|
49
|
+ endTime: resolvedEndTime,
|
|
|
|
50
|
+ pageToken: pageToken,
|
|
|
|
51
|
+ );
|
|
|
|
52
|
+ pageCount += 1;
|
|
|
|
53
|
+
|
|
|
|
54
|
+ if (page.items.isNotEmpty) {
|
|
|
|
55
|
+ await _localStore.upsertRawDataBatch(
|
|
|
|
56
|
+ dataType: dataType,
|
|
|
|
57
|
+ items: page.items,
|
|
|
|
58
|
+ );
|
|
|
|
59
|
+ storedCount += page.items.length;
|
|
|
|
60
|
+ }
|
|
|
|
61
|
+
|
|
|
|
62
|
+ pageToken = page.nextPageToken;
|
|
|
|
63
|
+ } while (pageToken != null && pageToken.isNotEmpty);
|
|
|
|
64
|
+
|
|
|
|
65
|
+ return OhosHealthRawDataSyncResult(
|
|
|
|
66
|
+ dataType: dataType,
|
|
|
|
67
|
+ startTime: resolvedStartTime,
|
|
|
|
68
|
+ endTime: resolvedEndTime,
|
|
|
|
69
|
+ pageCount: pageCount,
|
|
|
|
70
|
+ storedCount: storedCount,
|
|
|
|
71
|
+ );
|
|
|
|
72
|
+ }
|
|
|
|
73
|
+
|
|
|
|
74
|
+ Future<int> _resolveStartTime({
|
|
|
|
75
|
+ required int dataType,
|
|
|
|
76
|
+ required int endTime,
|
|
|
|
77
|
+ }) async {
|
|
|
|
78
|
+ final latestDataTime = await _localStore.latestDataTime(dataType: dataType);
|
|
|
|
79
|
+ if (latestDataTime != null) {
|
|
|
|
80
|
+ return latestDataTime;
|
|
|
|
81
|
+ }
|
|
|
|
82
|
+ final fallbackStartTime = _unixSeconds(
|
|
|
|
83
|
+ _nowProvider().subtract(const Duration(days: defaultLookbackDays)),
|
|
|
|
84
|
+ );
|
|
|
|
85
|
+ return fallbackStartTime > endTime ? endTime : fallbackStartTime;
|
|
|
|
86
|
+ }
|
|
|
|
87
|
+
|
|
|
|
88
|
+ int _unixSeconds(DateTime time) {
|
|
|
|
89
|
+ return time.millisecondsSinceEpoch ~/ 1000;
|
|
|
|
90
|
+ }
|
|
|
|
91
|
+}
|
|
|
|
92
|
+
|
|
|
|
93
|
+abstract class OhosHealthRawDataRemoteDataSource {
|
|
|
|
94
|
+ Future<OhosHealthRawDataPage> fetchRawDataPage({
|
|
|
|
95
|
+ required int dataType,
|
|
|
|
96
|
+ required int startTime,
|
|
|
|
97
|
+ required int endTime,
|
|
|
|
98
|
+ String? pageToken,
|
|
|
|
99
|
+ });
|
|
|
|
100
|
+}
|
|
|
|
101
|
+
|
|
|
|
102
|
+abstract class OhosHealthRawDataLocalStore {
|
|
|
|
103
|
+ Future<int?> latestDataTime({required int dataType});
|
|
|
|
104
|
+
|
|
|
|
105
|
+ Future<void> upsertRawDataBatch({
|
|
|
|
106
|
+ required int dataType,
|
|
|
|
107
|
+ required List<OhosHealthRawDataItem> items,
|
|
|
|
108
|
+ });
|
|
|
|
109
|
+}
|
|
|
|
110
|
+
|
|
|
|
111
|
+class TodoOhosHealthRawDataRemoteDataSource
|
|
|
|
112
|
+ implements OhosHealthRawDataRemoteDataSource {
|
|
|
|
113
|
+ const TodoOhosHealthRawDataRemoteDataSource();
|
|
|
|
114
|
+
|
|
|
|
115
|
+ @override
|
|
|
|
116
|
+ Future<OhosHealthRawDataPage> fetchRawDataPage({
|
|
|
|
117
|
+ required int dataType,
|
|
|
|
118
|
+ required int startTime,
|
|
|
|
119
|
+ required int endTime,
|
|
|
|
120
|
+ String? pageToken,
|
|
|
|
121
|
+ }) async {
|
|
|
|
122
|
+ // TODO: Call the OHOS raw-data API after the endpoint and response schema
|
|
|
|
123
|
+ // are available.
|
|
|
|
124
|
+ return const OhosHealthRawDataPage(items: <OhosHealthRawDataItem>[]);
|
|
|
|
125
|
+ }
|
|
|
|
126
|
+}
|
|
|
|
127
|
+
|
|
|
|
128
|
+class TodoOhosHealthRawDataLocalStore implements OhosHealthRawDataLocalStore {
|
|
|
|
129
|
+ const TodoOhosHealthRawDataLocalStore();
|
|
|
|
130
|
+
|
|
|
|
131
|
+ @override
|
|
|
|
132
|
+ Future<int?> latestDataTime({required int dataType}) async {
|
|
|
|
133
|
+ // TODO: Query latest datatime/data_time for dataType from local database.
|
|
|
|
134
|
+ return null;
|
|
|
|
135
|
+ }
|
|
|
|
136
|
+
|
|
|
|
137
|
+ @override
|
|
|
|
138
|
+ Future<void> upsertRawDataBatch({
|
|
|
|
139
|
+ required int dataType,
|
|
|
|
140
|
+ required List<OhosHealthRawDataItem> items,
|
|
|
|
141
|
+ }) async {
|
|
|
|
142
|
+ // TODO: Store one fetched OHOS raw-data page into local database.
|
|
|
|
143
|
+ }
|
|
|
|
144
|
+}
|
|
|
|
145
|
+
|
|
|
|
146
|
+class OhosHealthRawDataPage {
|
|
|
|
147
|
+ const OhosHealthRawDataPage({
|
|
|
|
148
|
+ required this.items,
|
|
|
|
149
|
+ this.nextPageToken,
|
|
|
|
150
|
+ });
|
|
|
|
151
|
+
|
|
|
|
152
|
+ final List<OhosHealthRawDataItem> items;
|
|
|
|
153
|
+ final String? nextPageToken;
|
|
|
|
154
|
+}
|
|
|
|
155
|
+
|
|
|
|
156
|
+class OhosHealthRawDataItem {
|
|
|
|
157
|
+ const OhosHealthRawDataItem({
|
|
|
|
158
|
+ required this.dataType,
|
|
|
|
159
|
+ required this.dataTime,
|
|
|
|
160
|
+ required this.payload,
|
|
|
|
161
|
+ });
|
|
|
|
162
|
+
|
|
|
|
163
|
+ final int dataType;
|
|
|
|
164
|
+ final int dataTime;
|
|
|
|
165
|
+ final Map<String, Object?> payload;
|
|
|
|
166
|
+}
|
|
|
|
167
|
+
|
|
|
|
168
|
+class OhosHealthRawDataSyncResult {
|
|
|
|
169
|
+ const OhosHealthRawDataSyncResult({
|
|
|
|
170
|
+ required this.dataType,
|
|
|
|
171
|
+ required this.startTime,
|
|
|
|
172
|
+ required this.endTime,
|
|
|
|
173
|
+ required this.pageCount,
|
|
|
|
174
|
+ required this.storedCount,
|
|
|
|
175
|
+ });
|
|
|
|
176
|
+
|
|
|
|
177
|
+ final int dataType;
|
|
|
|
178
|
+ final int startTime;
|
|
|
|
179
|
+ final int endTime;
|
|
|
|
180
|
+ final int pageCount;
|
|
|
|
181
|
+ final int storedCount;
|
|
|
|
182
|
+} |