|
|
|
class OhosHealthRawDataType {
|
|
|
|
const OhosHealthRawDataType._();
|
|
|
|
|
|
|
|
static const int sleepAnalysis = -1;
|
|
|
|
static const int activitySummary = -2;
|
|
|
|
static const int workout = -3;
|
|
|
|
}
|
|
|
|
|
|
|
|
class OhosHealthRawDataSyncService {
|
|
|
|
OhosHealthRawDataSyncService({
|
|
|
|
OhosHealthRawDataRemoteDataSource? remoteDataSource,
|
|
|
|
OhosHealthRawDataLocalStore? localStore,
|
|
|
|
DateTime Function()? nowProvider,
|
|
|
|
}) : _remoteDataSource =
|
|
|
|
remoteDataSource ?? const TodoOhosHealthRawDataRemoteDataSource(),
|
|
|
|
_localStore = localStore ?? const TodoOhosHealthRawDataLocalStore(),
|
|
|
|
_nowProvider = nowProvider ?? DateTime.now;
|
|
|
|
|
|
|
|
static const int defaultLookbackDays = 183;
|
|
|
|
|
|
|
|
final OhosHealthRawDataRemoteDataSource _remoteDataSource;
|
|
|
|
final OhosHealthRawDataLocalStore _localStore;
|
|
|
|
final DateTime Function() _nowProvider;
|
|
|
|
|
|
|
|
Future<OhosHealthRawDataSyncResult> syncRawData({
|
|
|
|
required int dataType,
|
|
|
|
int? startTime,
|
|
|
|
int? endTime,
|
|
|
|
}) async {
|
|
|
|
final resolvedEndTime = endTime ?? _unixSeconds(_nowProvider());
|
|
|
|
final resolvedStartTime = startTime ??
|
|
|
|
await _resolveStartTime(
|
|
|
|
dataType: dataType,
|
|
|
|
endTime: resolvedEndTime,
|
|
|
|
);
|
|
|
|
|
|
|
|
if (resolvedEndTime < resolvedStartTime) {
|
|
|
|
throw ArgumentError.value(endTime, 'endTime');
|
|
|
|
}
|
|
|
|
|
|
|
|
String? pageToken;
|
|
|
|
var pageCount = 0;
|
|
|
|
var storedCount = 0;
|
|
|
|
|
|
|
|
do {
|
|
|
|
final page = await _remoteDataSource.fetchRawDataPage(
|
|
|
|
dataType: dataType,
|
|
|
|
startTime: resolvedStartTime,
|
|
|
|
endTime: resolvedEndTime,
|
|
|
|
pageToken: pageToken,
|
|
|
|
);
|
|
|
|
pageCount += 1;
|
|
|
|
|
|
|
|
if (page.items.isNotEmpty) {
|
|
|
|
await _localStore.upsertRawDataBatch(
|
|
|
|
dataType: dataType,
|
|
|
|
items: page.items,
|
|
|
|
);
|
|
|
|
storedCount += page.items.length;
|
|
|
|
}
|
|
|
|
|
|
|
|
pageToken = page.nextPageToken;
|
|
|
|
} while (pageToken != null && pageToken.isNotEmpty);
|
|
|
|
|
|
|
|
return OhosHealthRawDataSyncResult(
|
|
|
|
dataType: dataType,
|
|
|
|
startTime: resolvedStartTime,
|
|
|
|
endTime: resolvedEndTime,
|
|
|
|
pageCount: pageCount,
|
|
|
|
storedCount: storedCount,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Future<int> _resolveStartTime({
|
|
|
|
required int dataType,
|
|
|
|
required int endTime,
|
|
|
|
}) async {
|
|
|
|
final latestDataTime = await _localStore.latestDataTime(dataType: dataType);
|
|
|
|
if (latestDataTime != null) {
|
|
|
|
return latestDataTime;
|
|
|
|
}
|
|
|
|
final fallbackStartTime = _unixSeconds(
|
|
|
|
_nowProvider().subtract(const Duration(days: defaultLookbackDays)),
|
|
|
|
);
|
|
|
|
return fallbackStartTime > endTime ? endTime : fallbackStartTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
int _unixSeconds(DateTime time) {
|
|
|
|
return time.millisecondsSinceEpoch ~/ 1000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class OhosHealthRawDataRemoteDataSource {
|
|
|
|
Future<OhosHealthRawDataPage> fetchRawDataPage({
|
|
|
|
required int dataType,
|
|
|
|
required int startTime,
|
|
|
|
required int endTime,
|
|
|
|
String? pageToken,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract class OhosHealthRawDataLocalStore {
|
|
|
|
Future<int?> latestDataTime({required int dataType});
|
|
|
|
|
|
|
|
Future<void> upsertRawDataBatch({
|
|
|
|
required int dataType,
|
|
|
|
required List<OhosHealthRawDataItem> items,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
class TodoOhosHealthRawDataRemoteDataSource
|
|
|
|
implements OhosHealthRawDataRemoteDataSource {
|
|
|
|
const TodoOhosHealthRawDataRemoteDataSource();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<OhosHealthRawDataPage> fetchRawDataPage({
|
|
|
|
required int dataType,
|
|
|
|
required int startTime,
|
|
|
|
required int endTime,
|
|
|
|
String? pageToken,
|
|
|
|
}) async {
|
|
|
|
// TODO: Call the OHOS raw-data API after the endpoint and response schema
|
|
|
|
// are available.
|
|
|
|
return const OhosHealthRawDataPage(items: <OhosHealthRawDataItem>[]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class TodoOhosHealthRawDataLocalStore implements OhosHealthRawDataLocalStore {
|
|
|
|
const TodoOhosHealthRawDataLocalStore();
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<int?> latestDataTime({required int dataType}) async {
|
|
|
|
// TODO: Query latest datatime/data_time for dataType from local database.
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Future<void> upsertRawDataBatch({
|
|
|
|
required int dataType,
|
|
|
|
required List<OhosHealthRawDataItem> items,
|
|
|
|
}) async {
|
|
|
|
// TODO: Store one fetched OHOS raw-data page into local database.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class OhosHealthRawDataPage {
|
|
|
|
const OhosHealthRawDataPage({
|
|
|
|
required this.items,
|
|
|
|
this.nextPageToken,
|
|
|
|
});
|
|
|
|
|
|
|
|
final List<OhosHealthRawDataItem> items;
|
|
|
|
final String? nextPageToken;
|
|
|
|
}
|
|
|
|
|
|
|
|
class OhosHealthRawDataItem {
|
|
|
|
const OhosHealthRawDataItem({
|
|
|
|
required this.dataType,
|
|
|
|
required this.dataTime,
|
|
|
|
required this.payload,
|
|
|
|
});
|
|
|
|
|
|
|
|
final int dataType;
|
|
|
|
final int dataTime;
|
|
|
|
final Map<String, Object?> payload;
|
|
|
|
}
|
|
|
|
|
|
|
|
class OhosHealthRawDataSyncResult {
|
|
|
|
const OhosHealthRawDataSyncResult({
|
|
|
|
required this.dataType,
|
|
|
|
required this.startTime,
|
|
|
|
required this.endTime,
|
|
|
|
required this.pageCount,
|
|
|
|
required this.storedCount,
|
|
|
|
});
|
|
|
|
|
|
|
|
final int dataType;
|
|
|
|
final int startTime;
|
|
|
|
final int endTime;
|
|
|
|
final int pageCount;
|
|
|
|
final int storedCount;
|
|
|
|
} |
...
|
...
|
|