mirror of
https://github.com/flutter/samples.git
synced 2025-11-09 06:18:49 +00:00
Flutter 3.29 beta (#2571)
This commit is contained in:
173
pedometer/example/lib/steps_repo.dart
Normal file
173
pedometer/example/lib/steps_repo.dart
Normal file
@@ -0,0 +1,173 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:ffi' as ffi;
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:jni/jni.dart' as jni;
|
||||
import 'package:pedometer/health_connect.dart' as hc;
|
||||
import 'package:pedometer/pedometer_bindings_generated.dart' as pd;
|
||||
|
||||
/// Class to hold the information needed for the chart
|
||||
class Steps {
|
||||
final String startHour;
|
||||
final int steps;
|
||||
|
||||
Steps(this.startHour, this.steps);
|
||||
}
|
||||
|
||||
abstract class StepsRepo {
|
||||
static const _formatString = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
static StepsRepo? _instance;
|
||||
static StepsRepo get instance =>
|
||||
_instance ??= Platform.isAndroid ? _AndroidStepsRepo() : _IOSStepsRepo();
|
||||
|
||||
Future<List<Steps>> getSteps();
|
||||
}
|
||||
|
||||
class _IOSStepsRepo implements StepsRepo {
|
||||
static const _dylibPath =
|
||||
'/System/Library/Frameworks/CoreMotion.framework/CoreMotion';
|
||||
|
||||
// Bindings for the CMPedometer class.
|
||||
final lib = pd.PedometerBindings(ffi.DynamicLibrary.open(_dylibPath));
|
||||
// Bindings for the helper function.
|
||||
final helpLib = pd.PedometerBindings(ffi.DynamicLibrary.process());
|
||||
|
||||
late final pd.CMPedometer client;
|
||||
late final pd.NSDateFormatter formatter;
|
||||
late final pd.NSDateFormatter hourFormatter;
|
||||
|
||||
_IOSStepsRepo() {
|
||||
// Contains the Dart API helper functions
|
||||
final dylib = ffi.DynamicLibrary.open("pedometer.framework/pedometer");
|
||||
|
||||
// Initialize the Dart API
|
||||
final initializeApi = dylib.lookupFunction<
|
||||
ffi.IntPtr Function(ffi.Pointer<ffi.Void>),
|
||||
int Function(ffi.Pointer<ffi.Void>)
|
||||
>('Dart_InitializeApiDL');
|
||||
|
||||
final initializeResult = initializeApi(ffi.NativeApi.initializeApiDLData);
|
||||
if (initializeResult != 0) {
|
||||
throw StateError('failed to init API.');
|
||||
}
|
||||
|
||||
// Create a new CMPedometer instance.
|
||||
client = pd.CMPedometer.new1(lib);
|
||||
|
||||
// Setting the formatter for date strings.
|
||||
formatter = pd.NSDateFormatter.castFrom(
|
||||
pd.NSDateFormatter.alloc(lib).init(),
|
||||
);
|
||||
formatter.dateFormat = pd.NSString(lib, "${StepsRepo._formatString} zzz");
|
||||
hourFormatter = pd.NSDateFormatter.castFrom(
|
||||
pd.NSDateFormatter.alloc(lib).init(),
|
||||
);
|
||||
hourFormatter.dateFormat = pd.NSString(lib, "HH");
|
||||
}
|
||||
|
||||
pd.NSDate dateConverter(DateTime dartDate) {
|
||||
// Format dart date to string.
|
||||
final formattedDate = DateFormat(StepsRepo._formatString).format(dartDate);
|
||||
// Get current timezone.
|
||||
// If eastern african change to AST to follow with NSDate.
|
||||
final tz = dartDate.timeZoneName == "EAT" ? "AST" : dartDate.timeZoneName;
|
||||
|
||||
// Create a new NSString with the formatted date and timezone.
|
||||
final nString = pd.NSString(lib, "$formattedDate $tz");
|
||||
// Convert the NSString to NSDate.
|
||||
return formatter.dateFromString_(nString)!;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Steps>> getSteps() async {
|
||||
if (!pd.CMPedometer.isStepCountingAvailable(lib)) {
|
||||
debugPrint("Step counting is not available.");
|
||||
return [];
|
||||
}
|
||||
|
||||
final handlers = [];
|
||||
final futures = <Future<Steps?>>[];
|
||||
final now = DateTime.now();
|
||||
|
||||
for (var h = 0; h <= now.hour; h++) {
|
||||
final start = dateConverter(DateTime(now.year, now.month, now.day, h));
|
||||
final end = dateConverter(DateTime(now.year, now.month, now.day, h + 1));
|
||||
final completer = Completer<Steps?>();
|
||||
futures.add(completer.future);
|
||||
|
||||
final handler = helpLib.wrapCallback(
|
||||
pd.ObjCBlock_ffiVoid_CMPedometerData_NSError.listener(lib, (
|
||||
pd.CMPedometerData? result,
|
||||
pd.NSError? error,
|
||||
) {
|
||||
if (result != null) {
|
||||
final stepCount = result.numberOfSteps.intValue;
|
||||
final startHour =
|
||||
hourFormatter.stringFromDate_(result.startDate).toString();
|
||||
completer.complete(Steps(startHour, stepCount));
|
||||
} else {
|
||||
debugPrint("Query error: ${error?.localizedDescription}");
|
||||
completer.complete(null);
|
||||
}
|
||||
}),
|
||||
);
|
||||
handlers.add(handler);
|
||||
client.queryPedometerDataFromDate_toDate_withHandler_(
|
||||
start,
|
||||
end,
|
||||
handler,
|
||||
);
|
||||
}
|
||||
|
||||
return (await futures.wait).nonNulls.toList();
|
||||
}
|
||||
}
|
||||
|
||||
class _AndroidStepsRepo implements StepsRepo {
|
||||
late final hc.Activity activity;
|
||||
late final hc.Context applicationContext;
|
||||
late final hc.HealthConnectClient client;
|
||||
|
||||
_AndroidStepsRepo() {
|
||||
// ignore: invalid_use_of_internal_member
|
||||
activity = hc.Activity.fromReference(jni.Jni.getCurrentActivity());
|
||||
applicationContext =
|
||||
// ignore: invalid_use_of_internal_member
|
||||
hc.Context.fromReference(jni.Jni.getCachedApplicationContext());
|
||||
client = hc.HealthConnectClient.getOrCreate$1(applicationContext);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Steps>> getSteps() async {
|
||||
final futures = <Future<hc.AggregationResult>>[];
|
||||
final now = DateTime.now();
|
||||
|
||||
for (var h = 0; h <= now.hour; h++) {
|
||||
final start =
|
||||
DateTime(now.year, now.month, now.day, h).millisecondsSinceEpoch;
|
||||
final end =
|
||||
DateTime(now.year, now.month, now.day, h + 1).millisecondsSinceEpoch;
|
||||
final request = hc.AggregateRequest(
|
||||
{
|
||||
hc.StepsRecord.COUNT_TOTAL,
|
||||
}.toJSet(hc.AggregateMetric.type(jni.JLong.type)),
|
||||
hc.TimeRangeFilter.between(
|
||||
hc.Instant.ofEpochMilli(start)!,
|
||||
hc.Instant.ofEpochMilli(end)!,
|
||||
),
|
||||
jni.JSet.hash(jni.JObject.type),
|
||||
);
|
||||
futures.add(client.aggregate(request));
|
||||
}
|
||||
final data = await Future.wait(futures);
|
||||
return data.asMap().entries.map((entry) {
|
||||
final stepsLong = entry.value.get(hc.StepsRecord.COUNT_TOTAL);
|
||||
final steps = stepsLong?.intValue() ?? 0;
|
||||
return Steps(entry.key.toString().padLeft(2, '0'), steps);
|
||||
}).toList();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user