You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
HMG_Patient_App_New/lib/presentation/smartwatches/huawei_health_example.dart

1564 lines
45 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:huawei_health/huawei_health.dart';
const String packageName = 'com.ejada.hmg';
class HuaweiHealthExample extends StatefulWidget {
const HuaweiHealthExample({Key? key}) : super(key: key);
@override
State<HuaweiHealthExample> createState() => _HuaweiHealthExampleState();
}
class _HuaweiHealthExampleState extends State<HuaweiHealthExample> {
/// Styles
static const TextStyle cardTitleTextStyle = TextStyle(
fontWeight: FontWeight.w500,
fontSize: 18,
);
static const EdgeInsets componentPadding = EdgeInsets.all(8.0);
/// Text Controllers for showing the logs of different modules
final TextEditingController _activityTextController = TextEditingController();
final TextEditingController _dataTextController = TextEditingController();
final TextEditingController _settingTextController = TextEditingController();
final TextEditingController _autoRecorderTextController = TextEditingController();
final TextEditingController _consentTextController = TextEditingController();
final TextEditingController _healthTextController = TextEditingController();
/// Data controller reference to initialize at startup.
late DataController _dataController;
String? accessToken = '';
@override
void initState() {
super.initState();
if (!mounted) return;
// Initialize Event Callbacks
AutoRecorderController.autoRecorderStream.listen(_onAutoRecorderEvent);
// Initialize a DataController
initDataController();
}
/// Prints the specified text on both the console and the specified text controller.
void log(
String methodName,
TextEditingController controller,
LogOptions logOption, {
String? result = '',
String? error = '',
}) {
String log = '';
switch (logOption) {
case LogOptions.call:
log = '$methodName called';
break;
case LogOptions.success:
log = '$methodName [Success: $result] ';
break;
case LogOptions.error:
log = '$methodName [Error: $error] [Error Description: ${HiHealthStatusCodes.getStatusCodeMessage(error ?? '')}]';
break;
case LogOptions.custom:
log = methodName; // Custom text
break;
}
debugPrint(log);
setState(() {
controller.text = '$log\n${controller.text}';
});
}
/// Authorizes Huawei Health Kit for the user, with defined scopes.
void signIn() async {
// List of scopes to ask for authorization.
//
// Note: These scopes should also be authorized on the Huawei Developer Console.
final List<Scope> scopes = <Scope>[
Scope.HEALTHKIT_ACTIVITY_READ,
Scope.HEALTHKIT_ACTIVITY_WRITE,
Scope.HEALTHKIT_BLOODGLUCOSE_READ,
Scope.HEALTHKIT_BLOODGLUCOSE_WRITE,
Scope.HEALTHKIT_CALORIES_READ,
Scope.HEALTHKIT_CALORIES_WRITE,
Scope.HEALTHKIT_DISTANCE_READ,
Scope.HEALTHKIT_DISTANCE_WRITE,
Scope.HEALTHKIT_HEARTRATE_READ,
Scope.HEALTHKIT_HEARTRATE_WRITE,
Scope.HEALTHKIT_HEIGHTWEIGHT_READ,
Scope.HEALTHKIT_HEIGHTWEIGHT_WRITE,
Scope.HEALTHKIT_LOCATION_READ,
Scope.HEALTHKIT_LOCATION_WRITE,
Scope.HEALTHKIT_PULMONARY_READ,
Scope.HEALTHKIT_PULMONARY_WRITE,
Scope.HEALTHKIT_SLEEP_READ,
Scope.HEALTHKIT_SLEEP_WRITE,
Scope.HEALTHKIT_SPEED_READ,
Scope.HEALTHKIT_SPEED_WRITE,
Scope.HEALTHKIT_STEP_READ,
Scope.HEALTHKIT_STEP_WRITE,
Scope.HEALTHKIT_STRENGTH_READ,
Scope.HEALTHKIT_STRENGTH_WRITE,
Scope.HEALTHKIT_BODYFAT_READ,
Scope.HEALTHKIT_BODYFAT_WRITE,
Scope.HEALTHKIT_NUTRITION_READ,
Scope.HEALTHKIT_NUTRITION_WRITE,
Scope.HEALTHKIT_BLOODPRESSURE_READ,
Scope.HEALTHKIT_BLOODPRESSURE_WRITE,
Scope.HEALTHKIT_BODYTEMPERATURE_READ,
Scope.HEALTHKIT_BODYTEMPERATURE_WRITE,
Scope.HEALTHKIT_OXYGENSTATURATION_READ,
Scope.HEALTHKIT_OXYGENSTATURATION_WRITE,
Scope.HEALTHKIT_REPRODUCTIVE_READ,
Scope.HEALTHKIT_REPRODUCTIVE_WRITE,
Scope.HEALTHKIT_ACTIVITY_RECORD_READ,
Scope.HEALTHKIT_ACTIVITY_RECORD_WRITE,
Scope.HEALTHKIT_HEARTRATE_REALTIME,
Scope.HEALTHKIT_STEP_REALTIME,
Scope.HEALTHKIT_HEARTHEALTH_WRITE,
Scope.HEALTHKIT_HEARTHEALTH_READ,
Scope.HEALTHKIT_STRESS_WRITE,
Scope.HEALTHKIT_STRESS_READ,
Scope.HEALTHKIT_OXYGEN_SATURATION_WRITE,
Scope.HEALTHKIT_OXYGEN_SATURATION_READ,
Scope.HEALTHKIT_HISTORYDATA_OPEN_WEEK,
Scope.HEALTHKIT_HISTORYDATA_OPEN_MONTH,
Scope.HEALTHKIT_HISTORYDATA_OPEN_YEAR,
];
try {
AuthHuaweiId? result = await HealthAuth.signIn(scopes);
debugPrint(
'Granted Scopes for User(${result?.displayName}): ${result?.grantedScopes?.toString()}',
);
showSnackBar(
'Authorization Success.',
color: Colors.green,
);
setState(() => accessToken = result?.accessToken);
} on PlatformException catch (e) {
debugPrint('Error on authorization, Error:${e.toString()}');
showSnackBar(
'Error on authorization, Error:${e.toString()}, Error Description: '
'${HiHealthStatusCodes.getStatusCodeMessage(e.message ?? '')}',
);
}
}
// ActivityRecordsController
//
/// Adds an ActivityRecord with an ActivitySummary, time range is 2 hours from now.
Future<void> addActivityRecord() async {
log(
'addActivityRecord',
_activityTextController,
LogOptions.call,
);
DateTime startTime = DateTime.now().subtract(const Duration(hours: 2));
DateTime endTime = DateTime.now();
// Build an ActivityRecord object
ActivityRecord activityRecord = ActivityRecord(
startTime: startTime,
endTime: endTime,
id: 'ActivityRecordId0',
name: 'AddActivityRecord',
activityTypeId: HiHealthActivities.running,
description: 'This is a test for ActivityRecord',
activitySummary: ActivitySummary(
paceSummary: PaceSummary(
avgPace: 247.27626,
bestPace: 212.0,
britishPaceMap: <String, double>{
'102802480': 365.0,
},
britishPartTimeMap: <String, double>{
'1.0': 263.0,
},
partTimeMap: <String, double>{
'1.0': 456.0,
},
paceMap: <String, double>{
'1.0': 263.0,
},
),
dataSummary: <SamplePoint>[
SamplePoint(
dataType: DataType.DT_CONTINUOUS_DISTANCE_TOTAL,
startTime: startTime.add(Duration(seconds: 1)),
endTime: endTime.subtract(Duration(seconds: 1)),
fieldValueOptions: FieldFloat(Field.FIELD_DISTANCE, 400),
timeUnit: TimeUnit.MILLISECONDS,
),
SamplePoint(
dataType: DataType.POLYMERIZE_CONTINUOUS_SPEED_STATISTICS,
fieldValueOptions: FieldFloat(Field.FIELD_AVG, 60.0),
startTime: startTime.add(Duration(seconds: 1)),
endTime: endTime.subtract(Duration(seconds: 1)),
timeUnit: TimeUnit.MILLISECONDS,
)
..setFieldValue(Field.FIELD_MIN, 40.0)
..setFieldValue(Field.FIELD_MAX, 80.0),
]),
);
// Build the dataCollector object
DataCollector dataCollector = DataCollector(
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
dataType: DataType.DT_INSTANTANEOUS_STEPS_RATE,
name: 'AddActivityRecord1923',
);
// You can use sampleSets to add more sample points to the sampling dataset.
// Build a list of sampling point objects and add it to the sampling dataSet
List<SamplePoint> samplePoints = <SamplePoint>[
SamplePoint(
dataCollector: dataCollector,
startTime: startTime.add(Duration(seconds: 1)),
endTime: endTime.subtract(Duration(seconds: 1)),
fieldValueOptions: FieldFloat(Field.FIELD_STEP_RATE, 10.0),
timeUnit: TimeUnit.MILLISECONDS,
),
];
SampleSet sampleSet = SampleSet(
dataCollector,
samplePoints,
);
try {
await ActivityRecordsController.addActivityRecord(
ActivityRecordInsertOptions(
activityRecord: activityRecord,
sampleSets: <SampleSet>[
sampleSet,
],
),
);
log(
'addActivityRecord',
_activityTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'addActivityRecord',
_activityTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Obtains saved ActivityRecords between yesterday and now,
/// with the DT_CONTINUOUS_STEPS_DELTA data type
void getActivityRecord() async {
log(
'getActivityRecord',
_activityTextController,
LogOptions.call,
);
// Create start time that will be used to read activity record.
DateTime startTime = DateTime.now().subtract(const Duration(days: 1));
// Create end time that will be used to read activity record.
DateTime endTime = DateTime.now().add(const Duration(hours: 3));
ActivityRecordReadOptions activityRecordReadOptions = ActivityRecordReadOptions(
activityRecordId: "ActivityRecordId0",
activityRecordName: null,
startTime: startTime,
endTime: endTime,
timeUnit: TimeUnit.MILLISECONDS,
dataType: DataType.DT_INSTANTANEOUS_STEPS_RATE,
);
try {
List<ActivityRecord> result = await ActivityRecordsController.getActivityRecord(
activityRecordReadOptions,
);
log(
'getActivityRecord',
_activityTextController,
LogOptions.success,
result: '[IDs: ${result.map((ActivityRecord e) => e.id).toList()}]',
);
} on PlatformException catch (e) {
log(
'getActivityRecord',
_activityTextController,
LogOptions.error,
result: e.message,
);
}
}
/// Starts the ActivityRecord with the id:`ActivityRecordRun1`
void beginActivityRecord() async {
try {
log(
'beginActivityRecord',
_activityTextController,
LogOptions.call,
);
// Build an ActivityRecord object
ActivityRecord activityRecord = ActivityRecord(
id: 'ActivityRecordRun0',
name: 'BeginActivityRecord',
description: 'This is ActivityRecord begin test!',
activityTypeId: HiHealthActivities.running,
startTime: DateTime.now().subtract(const Duration(hours: 1)),
);
await ActivityRecordsController.beginActivityRecord(
activityRecord,
);
log(
'beginActivityRecord',
_activityTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'beginActivityRecord',
_activityTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Stops the ActivityRecord with the id:`ActivityRecordRun1`
void endActivityRecord() async {
try {
log(
'endActivityRecord',
_activityTextController,
LogOptions.call,
);
final List<ActivityRecord> result = await ActivityRecordsController.endActivityRecord(
'ActivityRecordRun0',
);
// Return the list of activity records that have stopped
log(
'endActivityRecord',
_activityTextController,
LogOptions.success,
result: result.toString(),
);
} on PlatformException catch (e) {
log(
'endActivityRecord',
_activityTextController,
LogOptions.error,
result: e.message,
);
}
}
/// Ends all the ongoing activity records.
///
/// Result list will be null if there is no ongoing activity record.
void endAllActivityRecords() async {
try {
log(
'endAllActivityRecords',
_activityTextController,
LogOptions.call,
);
// Return the list of activity records that have stopped
List<ActivityRecord> result = await ActivityRecordsController.endAllActivityRecords();
log(
'endAllActivityRecords',
_activityTextController,
LogOptions.success,
result: '[IDs: ${result.map((ActivityRecord e) => e.id).toList()}]',
);
} on PlatformException catch (e) {
log(
'endAllActivityRecords',
_activityTextController,
LogOptions.error,
result: e.message,
);
}
}
//
//
// End of ActivityRecordsController Methods
// DataController Methods
//
//
/// Initializes a DataController instance with a list of HiHealtOptions.
void initDataController() async {
if (!mounted) return;
log(
'init',
_dataTextController,
LogOptions.call,
);
try {
_dataController = await DataController.init();
log(
'init',
_dataTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'init',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Clears all the data inserted by the app.
void clearAll() async {
log('clearAll', _dataTextController, LogOptions.call);
try {
await _dataController.clearAll();
log('clearAll', _dataTextController, LogOptions.success);
} on PlatformException catch (e) {
log('clearAll', _dataTextController, LogOptions.error, error: e.message);
}
}
/// Deletes DT_CONTINUOUS_STEPS_DELTA type data by the specified time range.
void delete() async {
log(
'delete',
_dataTextController,
LogOptions.call,
);
// Build the dataCollector object
DataCollector dataCollector = DataCollector(
dataType: DataType.DT_CONTINUOUS_STEPS_DELTA,
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
dataStreamName: 'STEPS_DELTA',
);
// Build the time range for the deletion: start time and end time.
DeleteOptions deleteOptions = DeleteOptions(
dataCollectors: <DataCollector>[dataCollector],
startTime: DateTime.parse('2020-10-10 08:00:00'),
endTime: DateTime.parse('2020-10-10 12:30:00'),
);
// Call the api with the constructed DeleteOptions instance.
try {
_dataController.delete(deleteOptions);
log(
'delete',
_dataTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'delete',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Inserts a sampling set with the DT_CONTINUOUS_STEPS_DELTA data type at the
/// specified start and end dates.
void insert() async {
log(
'insert',
_dataTextController,
LogOptions.call,
);
// Build the dataCollector object
DataCollector dataCollector = DataCollector(
dataType: DataType.DT_CONTINUOUS_STEPS_DELTA,
dataStreamName: 'STEPS_DELTA',
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
);
// You can use sampleSets to add more sampling points to the sampling dataset.
SampleSet sampleSet = SampleSet(
dataCollector,
<SamplePoint>[
SamplePoint(
dataCollector: dataCollector,
startTime: DateTime.parse('2020-10-10 12:00:00'),
endTime: DateTime.parse('2020-10-10 12:12:00'),
fieldValueOptions: FieldInt(
Field.FIELD_STEPS_DELTA,
100,
),
),
],
);
// Call the api with the constructed sample set.
try {
_dataController.insert(sampleSet);
log(
'insert',
_dataTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'insert',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
// Reads the user data between the specified start and end dates.
void read() async {
log(
'read',
_dataTextController,
LogOptions.call,
);
// Build the dataCollector object
DataCollector dataCollector = DataCollector(
dataType: DataType.DT_CONTINUOUS_STEPS_DELTA,
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
dataStreamName: 'STEPS_DELTA',
);
// Build the time range for the query: start time and end time.
ReadOptions readOptions = ReadOptions(
dataCollectors: <DataCollector>[
dataCollector,
],
startTime: DateTime.parse('2020-10-10 12:00:00'),
endTime: DateTime.parse('2020-10-10 12:12:00'),
)..groupByTime(10000);
// Call the api with the constructed ReadOptions instance.
try {
ReadReply? readReply = await _dataController.read(readOptions);
log(
'read',
_dataTextController,
LogOptions.success,
result: readReply.toString(),
);
} on PlatformException catch (e) {
log(
'read',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Reads the daily summation between the dates: `2020.10.02` to `2020.12.15` for multiple data types.
/// Note that the time format is different for this method.
void readDailySummationList() async {
log(
'readDailySummationList',
_dataTextController,
LogOptions.call,
);
try {
List<SampleSet?>? sampleSets = await _dataController.readDailySummationList(
[DataType.DT_CONTINUOUS_STEPS_DELTA, DataType.DT_CONTINUOUS_CALORIES_BURNT],
20201002,
20201003,
);
log(
'readDailySummationList',
_dataTextController,
LogOptions.success,
result: sampleSets.toString(),
);
} on PlatformException catch (e) {
log(
'readDailySummationList',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Reads the steps summation for today.
void readTodaySummation() async {
log(
'readTodaySummation',
_dataTextController,
LogOptions.call,
);
try {
SampleSet? sampleSet = await _dataController.readTodaySummation(
DataType.DT_CONTINUOUS_STEPS_DELTA,
);
log(
'readTodaySummation',
_dataTextController,
LogOptions.success,
result: sampleSet.toString(),
);
} on PlatformException catch (e) {
log(
'readTodaySummation',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Updates DT_CONTINUOUS_STEPS_DELTA for the specified dates.
void update() async {
log(
'update',
_dataTextController,
LogOptions.call,
);
// Build the dataCollector object
DataCollector dataCollector = DataCollector(
dataType: DataType.DT_CONTINUOUS_STEPS_DELTA,
dataStreamName: 'STEPS_DELTA',
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
);
// You can use sampleSets to add more sampling points to the sampling dataset.
SampleSet sampleSet = SampleSet(
dataCollector,
<SamplePoint>[
SamplePoint(
dataCollector: dataCollector,
startTime: DateTime.parse('2020-12-12 09:00:00'),
endTime: DateTime.parse('2020-12-12 09:05:00'),
fieldValueOptions: FieldInt(
Field.FIELD_STEPS_DELTA,
120,
),
),
],
);
// Build a parameter object for the update.
// Note: (1) The start time of the modified object updateOptions can not be greater than the minimum
// value of the start time of all sample data points in the modified data sample set
// (2) The end time of the modified object updateOptions can not be less than the maximum value of the
// end time of all sample data points in the modified data sample set
UpdateOptions updateOptions = UpdateOptions(
startTime: DateTime.parse('2020-12-12 08:00:00'),
endTime: DateTime.parse('2020-12-12 09:25:00'),
sampleSet: sampleSet,
);
try {
await _dataController.update(updateOptions);
log(
'update',
_dataTextController,
LogOptions.success,
result: sampleSet.toString(),
);
} on PlatformException catch (e) {
log(
'update',
_dataTextController,
LogOptions.error,
error: e.message,
);
}
}
//
//
// End of DataController Methods
// SettingController Methods
//
/// Adds a custom DataType with the FIELD_ALTITUDE.
void addDataType() async {
log(
'addDataType',
_settingTextController,
LogOptions.call,
);
try {
// The name of the created data type must be prefixed with the package name
// of the app. Otherwise, the creation fails. If the same data type is tried to
// be added again an exception will be thrown.
DataTypeAddOptions options = DataTypeAddOptions(
'$packageName.myCustomDataType',
<Field>[
const Field.newIntField('myIntField'),
Field.FIELD_ALTITUDE,
],
);
final DataType dataTypeResult = await SettingController.addDataType(
options,
);
log(
'addDataType',
_settingTextController,
LogOptions.success,
result: dataTypeResult.toString(),
);
} on PlatformException catch (e) {
log(
'addDataType',
_settingTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Reads the inserted data type on the [addDataType] method.
void readDataType() async {
log(
'readDataType',
_settingTextController,
LogOptions.call,
);
try {
final DataType dataTypeResult = await SettingController.readDataType(
'$packageName.myCustomDataType',
);
log(
'readDataType',
_settingTextController,
LogOptions.success,
result: dataTypeResult.toString(),
);
} on PlatformException catch (e) {
log(
'readDataType',
_settingTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Disables the Health Kit function, cancels user authorization, and cancels
/// all data records. (The task takes effect in 24 hours.)
void disableHiHealth() async {
log(
'disableHiHealth',
_settingTextController,
LogOptions.call,
);
try {
await SettingController.disableHiHealth();
log(
'disableHiHealth',
_settingTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'disableHiHealth',
_settingTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Checks the user privacy authorization to Health Kit. Redirects the user to
/// the Authorization screen if the permissions are not given.
void checkHealthAppAuthorization() async {
log(
'checkHealthAppAuthorization',
_settingTextController,
LogOptions.call,
);
try {
await SettingController.checkHealthAppAuthorization();
log(
'checkHealthAppAuthorization',
_settingTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'checkHealthAppAuthorization',
_settingTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Checks the user privacy authorization to Health Kit. If authorized `true`
/// value would be returned.
void getHealthAppAuthorization() async {
log(
'getHealthAppAuthorization',
_settingTextController,
LogOptions.call,
);
try {
final bool result = await SettingController.getHealthAppAuthorization();
log(
'getHealthAppAuthorization',
_settingTextController,
LogOptions.success,
result: result.toString(),
);
} on PlatformException catch (e) {
log(
'getHealthAppAuthorization',
_settingTextController,
LogOptions.error,
error: e.message,
);
}
}
void requestAuth() async {
final HealthKitAuthResult res = await SettingController.requestAuthorizationIntent(
<Scope>[
Scope.HEALTHKIT_STEP_READ,
Scope.HEALTHKIT_STEP_WRITE,
Scope.HEALTHKIT_HEIGHTWEIGHT_READ,
Scope.HEALTHKIT_HEIGHTWEIGHT_WRITE,
Scope.HEALTHKIT_HEARTRATE_READ,
Scope.HEALTHKIT_HEARTRATE_WRITE,
Scope.HEALTHKIT_ACTIVITY_RECORD_READ,
Scope.HEALTHKIT_ACTIVITY_RECORD_WRITE,
Scope.HEALTHKIT_HEARTHEALTH_READ,
Scope.HEALTHKIT_HEARTHEALTH_WRITE,
],
true,
);
debugPrint(res.authAccount?.accessToken);
}
//
//
// End of SettingController Methods
// AutoRecorderController Methods
//
//
// Callback function for AutoRecorderStream event.
void _onAutoRecorderEvent(SamplePoint? res) {
log(
'[AutoRecorderEvent] obtained, SamplePoint Field Value is ${res?.fieldValues?.toString()}',
_autoRecorderTextController,
LogOptions.custom,
);
}
/// Starts an Android Foreground Service to count the steps of the user.
/// The steps will be emitted to the AutoRecorderStream.
void startRecord() async {
log(
'startRecord',
_autoRecorderTextController,
LogOptions.call,
);
try {
await AutoRecorderController.startRecord(
DataType.DT_CONTINUOUS_STEPS_TOTAL,
NotificationProperties(
title: 'HMS Flutter Health Demo',
text: 'Counting steps',
subText: 'this is a subtext',
ticker: 'this is a ticker',
showChronometer: true,
),
);
log(
'startRecord',
_autoRecorderTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'startRecord',
_autoRecorderTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Ends the Foreground service and stops the step count events.
void stopRecord() async {
log(
'endRecord',
_autoRecorderTextController,
LogOptions.call,
);
try {
await AutoRecorderController.stopRecord(
DataType.DT_CONTINUOUS_STEPS_TOTAL,
);
log(
'endRecord',
_autoRecorderTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'endRecord',
_autoRecorderTextController,
LogOptions.error,
error: e.message,
);
}
}
//
//
// End of AutoRecorderController Methods
// ConsentController Methods
//
/// Obtains the application id from the agconnect-services.json file.
void getAppId() async {
log(
'getAppId',
_consentTextController,
LogOptions.call,
);
try {
final String appId = await ConsentsController.getAppId();
log(
'getAppId',
_consentTextController,
LogOptions.success,
result: appId,
);
} on PlatformException catch (e) {
log(
'getAppId',
_consentTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Gets the granted permission scopes for the app.
void getScopes() async {
log(
'getScopes',
_consentTextController,
LogOptions.call,
);
try {
final String appId = await ConsentsController.getAppId();
final ScopeLangItem scopeLangItem = await ConsentsController.getScopes(
'en-gb',
appId,
);
log(
'getScopes',
_consentTextController,
LogOptions.success,
result: scopeLangItem.toString(),
);
} on PlatformException catch (e) {
log(
'getScopes',
_consentTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Revokes all the permissions that authorized for this app.
void revoke() async {
log(
'revoke',
_consentTextController,
LogOptions.call,
);
try {
final String appId = await ConsentsController.getAppId();
await ConsentsController.revoke(appId);
log(
'revoke',
_consentTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'revoke',
_consentTextController,
LogOptions.error,
error: e.message,
);
}
}
/// Revokes the distance read/write permissions for the app.
void revokeWithScopes() async {
log(
'revokeWithScopes',
_consentTextController,
LogOptions.call,
);
try {
// Obtain the application id.
final String appId = await ConsentsController.getAppId();
// Call the revokeWithScopes method with desired scopes.
await ConsentsController.revokeWithScopes(
appId,
<Scope>[
Scope.HEALTHKIT_DISTANCE_WRITE,
Scope.HEALTHKIT_DISTANCE_READ,
],
);
log(
'revokeWithScopes',
_consentTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'revokeWithScopes',
_consentTextController,
LogOptions.error,
error: e.message,
);
}
}
//
//
// End of ConsentController Methods
// HealthController Methods
//
void addHealthRecord() async {
log(
'addHealthRecord',
_healthTextController,
LogOptions.call,
);
try {
final DateTime startTime = DateTime(2023, 5, 11);
final DateTime endTime = DateTime(2023, 5, 13);
DataCollector contDataCollector = DataCollector(
dataStreamName: 'contDataCollector',
packageName: packageName,
dataType: DataType.POLYMERIZE_CONTINUOUS_HEART_RATE_STATISTICS,
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
);
DataCollector instDataCollector = DataCollector(
dataStreamName: 'instDataCollector',
packageName: packageName,
dataType: DataType.DT_INSTANTANEOUS_HEART_RATE,
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
);
List<SampleSet> subDataDetails = <SampleSet>[
SampleSet(instDataCollector, <SamplePoint>[
SamplePoint(
dataCollector: instDataCollector,
)
..setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
..setFieldValue(Field.FIELD_BPM, 88.0)
])
];
List<SamplePoint> subDataSummary = <SamplePoint>[
SamplePoint(
dataCollector: contDataCollector,
)
..setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
..setFieldValue(Field.FIELD_AVG, 90.0)
..setFieldValue(Field.FIELD_MAX, 100.0)
..setFieldValue(Field.FIELD_MIN, 80.0)
..setFieldValue(Field.LAST, 85.0)
];
final HealthRecord healthRecord = HealthRecord(
startTime: startTime,
endTime: endTime,
metadata: 'Data',
dataCollector: DataCollector(
dataStreamName: 'such as step count',
packageName: packageName,
dataType: HealthDataTypes.DT_HEALTH_RECORD_BRADYCARDIA,
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
),
)
..setSubDataSummary(subDataSummary)
..setSubDataDetails(subDataDetails)
..setFieldValue(HealthFields.FIELD_THRESHOLD, 42.0)
..setFieldValue(HealthFields.FIELD_MAX_HEART_RATE, 48.0)
..setFieldValue(HealthFields.FIELD_MIN_HEART_RATE, 42.0)
..setFieldValue(HealthFields.FIELD_AVG_HEART_RATE, 45.0);
final String? result = await HealthRecordController.addHealthRecord(
HealthRecordInsertOptions(
healthRecord: healthRecord,
),
);
log(
'addHealthRecord',
_healthTextController,
LogOptions.success,
result: result.toString(),
);
} on PlatformException catch (e) {
log(
'addHealthRecord',
_healthTextController,
LogOptions.error,
error: e.message,
);
}
}
void getHealthRecord() async {
log(
'getHealthRecord',
_healthTextController,
LogOptions.call,
);
try {
final DateTime startTime = DateTime(2023, 5, 11);
final DateTime endTime = DateTime(2023, 5, 13);
HealthRecordReply result = await HealthRecordController.getHealthRecord(
HealthRecordReadOptions(
packageName: packageName,
)
..setSubDataTypeList(
<DataType>[
DataType.DT_INSTANTANEOUS_HEART_RATE,
],
)
..setTimeInterval(
startTime,
endTime,
TimeUnit.MILLISECONDS,
)
..readByDataType(
HealthDataTypes.DT_HEALTH_RECORD_BRADYCARDIA,
)
..readHealthRecordsFromAllApps(),
);
log(
'getHealthRecord',
_healthTextController,
LogOptions.success,
result: result.healthRecords[0].toJson(),
);
} on PlatformException catch (e) {
log(
'getHealthRecord',
_healthTextController,
LogOptions.error,
error: e.message,
);
}
}
void updateHealthRecord() async {
log(
'updateHealthRecord',
_healthTextController,
LogOptions.call,
);
try {
final DateTime startTime = DateTime(2022, 10, 11);
final DateTime endTime = DateTime(2022, 10, 12);
final HealthRecord healthRecord = HealthRecord(
startTime: startTime,
endTime: endTime,
metadata: 'Data',
dataCollector: DataCollector(
dataStreamName: 'such as step count',
packageName: packageName,
dataType: HealthDataTypes.DT_HEALTH_RECORD_BRADYCARDIA,
dataGenerateType: DataGenerateType.DATA_TYPE_RAW,
),
)
..setFieldValue(HealthFields.FIELD_THRESHOLD, 41.9)
..setFieldValue(HealthFields.FIELD_MAX_HEART_RATE, 49.1)
..setFieldValue(HealthFields.FIELD_MIN_HEART_RATE, 41.1)
..setFieldValue(HealthFields.FIELD_AVG_HEART_RATE, 45.1);
await HealthRecordController.updateHealthRecord(
HealthRecordUpdateOptions(
healthRecord: healthRecord,
healthRecordId: '<your_health_record_id>',
),
);
log(
'updateHealthRecord',
_healthTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'updateHealthRecord',
_healthTextController,
LogOptions.error,
error: e.message,
);
}
}
void deleteHealthRecord() async {
log(
'deleteHealthRecord',
_healthTextController,
LogOptions.call,
);
try {
await HealthRecordController.deleteHealthRecord(
HealthRecordDeleteOptions(
startTime: DateTime.now().subtract(const Duration(days: 14)),
endTime: DateTime.now(),
)..setHealthRecordIds(
<String>[
'<your_health_record_id>',
],
),
);
log(
'deleteHealthRecord',
_healthTextController,
LogOptions.success,
);
} on PlatformException catch (e) {
log(
'deleteHealthRecord',
_healthTextController,
LogOptions.error,
error: e.message,
);
}
}
//
//
// End of HealthController Methods
// App's widgets.
//
//
Widget expansionCard({
required String titleText,
required List<Widget> children,
}) {
return Card(
margin: componentPadding,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: ExpansionTile(
title: Text(
titleText,
style: cardTitleTextStyle,
),
children: children,
),
);
}
Widget loggingArea(
TextEditingController moduleTextController,
) {
return Column(
children: <Widget>[
Container(
margin: componentPadding,
padding: const EdgeInsets.all(8.0),
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.0),
border: Border.all(color: Colors.black12),
),
child: TextField(
readOnly: true,
maxLines: 15,
controller: moduleTextController,
decoration: const InputDecoration(
enabledBorder: InputBorder.none,
),
),
),
TextButton(
child: const Text('Clear Log'),
onPressed: () => setState(() {
moduleTextController.text = '';
}),
)
],
);
}
void showSnackBar(
String text, {
Color color = Colors.blue,
}) {
final SnackBar snackBar = SnackBar(
content: Text(text),
backgroundColor: color,
action: SnackBarAction(
label: 'Close',
textColor: Colors.white,
onPressed: () {
ScaffoldMessenger.of(context).removeCurrentSnackBar();
},
),
);
ScaffoldMessenger.of(context).showSnackBar(snackBar);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
title: const Text(
'Huawei Health Kit',
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
centerTitle: true,
elevation: 0.0,
actions: <Widget>[
IconButton(
onPressed: requestAuth,
icon: const Icon(Icons.ac_unit),
),
],
),
body: Builder(
builder: (BuildContext context) {
return ListView(
physics: const BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
children: <Widget>[
// Sign In Widgets
Card(
margin: componentPadding,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: componentPadding,
child: Text(
'Tap to SignIn button to obtain the HMS Account to complete '
'login and authorization, and then use other buttons '
'to try the related API functions.',
textAlign: TextAlign.center,
),
),
const Padding(
padding: componentPadding,
child: Text(
'Note: If the login page is not displayed, change the package '
'name, AppID, and configure the signature file by referring '
'to the developer guide on the official website.',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.blue,
),
),
),
Container(
padding: componentPadding,
width: double.infinity,
child: OutlinedButton(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(
Colors.blue,
),
),
child: const Text(
'SignIn',
style: TextStyle(
color: Colors.white,
),
),
onPressed: () => signIn(),
),
),
],
),
),
// ActivityRecordsController
expansionCard(
titleText: 'ActivityRecords Controller',
children: <Widget>[
loggingArea(_activityTextController),
ListTile(
title: const Text('AddActivityRecord'),
onTap: () => addActivityRecord(),
),
ListTile(
title: const Text('GetActivityRecord'),
onTap: () => getActivityRecord(),
),
ListTile(
title: const Text('beginActivityRecord'),
onTap: () => beginActivityRecord(),
),
ListTile(
title: const Text('endActivityRecord'),
onTap: () => endActivityRecord(),
),
ListTile(
title: const Text('endAllActivityRecords'),
onTap: () => endAllActivityRecords(),
),
],
),
// DataController Widgets
expansionCard(
titleText: 'DataController',
children: <Widget>[
loggingArea(_dataTextController),
ListTile(
title: const Text('readTodaySummation'),
onTap: () => readTodaySummation(),
),
ListTile(
title: const Text('readDailySummationList'),
onTap: () => readDailySummationList(),
),
ListTile(
title: const Text('insert'),
onTap: () => insert(),
),
ListTile(
title: const Text('read'),
onTap: () => read(),
),
ListTile(
title: const Text('update'),
onTap: () => update(),
),
ListTile(
title: const Text('delete'),
onTap: () => delete(),
),
ListTile(
title: const Text('clearAll'),
onTap: () => clearAll(),
),
],
),
// SettingController Widgets.
expansionCard(
titleText: 'SettingController',
children: <Widget>[
loggingArea(_settingTextController),
ListTile(
title: const Text('addDataType'),
onTap: () => addDataType(),
),
ListTile(
title: const Text('readDataType'),
onTap: () => readDataType(),
),
ListTile(
title: const Text('disableHiHealth'),
onTap: () => disableHiHealth(),
),
ListTile(
title: const Text('checkHealthAppAuthorization'),
onTap: () => checkHealthAppAuthorization(),
),
ListTile(
title: const Text('getHealthAppAuthorization'),
onTap: () => getHealthAppAuthorization(),
),
],
),
// AutoRecorderController Widgets
expansionCard(
titleText: 'AutoRecorderController',
children: <Widget>[
loggingArea(_autoRecorderTextController),
ListTile(
title: const Text('startRecord'),
onTap: () => startRecord(),
),
ListTile(
title: const Text('stopRecord'),
onTap: () => stopRecord(),
),
],
),
// Consent Controller Widgets
expansionCard(
titleText: 'ConsentController',
children: <Widget>[
loggingArea(_consentTextController),
ListTile(
title: const Text('getAppId'),
onTap: () => getAppId(),
),
ListTile(
title: const Text('getScopes'),
onTap: () => getScopes(),
),
ListTile(
title: const Text('revoke'),
onTap: () => revoke(),
),
ListTile(
title: const Text('revokeWithScopes'),
onTap: () => revokeWithScopes(),
),
],
),
// Health Controller Widgets
expansionCard(
titleText: 'HealthController',
children: <Widget>[
loggingArea(_healthTextController),
ListTile(
title: const Text('addHealthRecord'),
onTap: () => addHealthRecord(),
),
ListTile(
title: const Text('getHealthRecord'),
onTap: () => getHealthRecord(),
),
ListTile(
title: const Text('updateHealthRecord'),
onTap: () => updateHealthRecord(),
),
ListTile(
title: const Text('deleteHealthRecord'),
onTap: () => deleteHealthRecord(),
),
],
),
],
);
},
),
);
}
}
/// Options for logging.
enum LogOptions {
call,
success,
error,
custom,
}