HealthKitHostApiImpl.ets
5.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import { HealthAuthorization, HealthKitHostApi, Result } from '../pigeon/HealthKitApi';
import common from '@ohos.app.ability.common';
import { bundleManager, Want } from '@kit.AbilityKit';
import { productViewManager } from '@kit.AppGalleryKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { healthStore } from '@kit.HealthServiceKit';
/**
* Bridges Flutter's health authorization flow to Health Service Kit.
*/
export class HealthKitHostApiImpl extends HealthKitHostApi {
private static readonly HEALTH_APP_LINK = 'huaweischeme://healthapp/home/main';
private static readonly HEALTH_APP_BUNDLE_NAME = 'com.huawei.hmos.health';
private readonly context: common.UIAbilityContext;
// Keep this list aligned with the health metrics requested by the Flutter app.
private readonly authorizationRequest: healthStore.AuthorizationRequest = {
readDataTypes: [
healthStore.healthDataTypes.DAILY_ACTIVITIES,
healthStore.healthDataTypes.HEART_RATE,
healthStore.healthDataTypes.HEART_RATE_VARIABILITY,
healthStore.healthDataTypes.BLOOD_OXYGEN_SATURATION,
healthStore.healthDataTypes.STRESS,
healthStore.healthDataTypes.SLEEP_RECORD,
healthStore.healthDataTypes.BODY_TEMPERATURE,
healthStore.healthDataTypes.SKIN_TEMPERATURE,
healthStore.healthDataTypes.WORKOUT,
],
writeDataTypes: [],
};
constructor(context: common.UIAbilityContext) {
super();
this.context = context;
}
getHealthServerAuthUrl(result: Result<string>): void {
// Health Service Kit uses a system authorization sheet, not an OAuth URL.
result.success('');
}
checkHealthAppAuthorization(result: Result<HealthAuthorization>): void {
this.getAuthorization().then((response: healthStore.AuthorizationResponse) => {
// The API returns only the data types currently granted to this app.
// A non-empty intersection means the app can read at least one metric.
const hasData = response.readDataTypes.length > 0;
result.success(new HealthAuthorization(hasData ? 1 : 0, hasData));
}).catch((error: Error) => {
// The user still needs to accept the Huawei Health privacy agreement.
// Return "needs authorization" so the user action can launch Health.
console.error(`checkHealthAppAuthorization failed: ${error.name}: ${error.message}`);
result.success(new HealthAuthorization(
this.requiresHealthAppPrivacyAuthorization(error) ? 0 : -1,
false,
));
});
}
requestHealthClientAuthorization(result: Result<boolean>): void {
this.requestAuthorizations().then((response: healthStore.AuthorizationResponse) => {
console.error(`requestHealthClientAuthorization Huawei success`);
result.success(response.readDataTypes.length > 0);
}).catch((error: Error) => {
console.error(`requestHealthClientAuthorization failed: ${error.name}: ${error.message}`);
if (!this.requiresHealthAppPrivacyAuthorization(error)) {
result.success(false);
return;
}
// Privacy consent is managed by the Huawei Health app. Opening it does
// not grant this application's data types yet, so Flutter must re-check
// authorization after the user returns.
this.openHealthAppForPrivacyAuthorization().then(() => {
result.success(false);
}).catch(() => {
result.success(false);
});
});
}
/**
* Triggers the manual device-to-cloud synchronization exposed by Huawei
* Health. The application must have obtained the "manual data sync"
* permission in the Health Service Kit console before this call can work.
*/
syncHealthDataToCloud(result: Result<boolean>): void {
this.syncAllHealthData().then(() => {
result.success(true);
}).catch((error: Error) => {
console.error(`syncHealthDataToCloud failed: ${error.name}: ${error.message}`);
result.success(false);
});
}
private async getAuthorization(): Promise<healthStore.AuthorizationResponse> {
await this.ensureInitialized();
return healthStore.getAuthorizations(this.authorizationRequest);
}
private async requestAuthorizations(): Promise<healthStore.AuthorizationResponse> {
await this.ensureInitialized();
return healthStore.requestAuthorizations(this.context, this.authorizationRequest);
}
private async syncAllHealthData(): Promise<void> {
await this.ensureInitialized();
await healthStore.syncAll();
}
private async ensureInitialized(): Promise<void> {
await healthStore.init(this.context);
}
private requiresHealthAppPrivacyAuthorization(error: Error): boolean {
// Health Service Kit uses 1002703001 when the user has not accepted the
// Huawei Health privacy agreement.
return (error as BusinessError).code === 1002703001;
}
private async openHealthAppForPrivacyAuthorization(): Promise<void> {
if (this.canOpenHealthApp()) {
await this.openInstalledHealthApp();
return;
}
this.openHealthAppInAppGallery();
}
private canOpenHealthApp(): boolean {
try {
return bundleManager.canOpenLink(HealthKitHostApiImpl.HEALTH_APP_LINK);
} catch (error) {
console.error(`Health app availability check failed: ${error.name}: ${error.message}`);
return false;
}
}
private async openInstalledHealthApp(): Promise<void> {
try {
console.error(`Opening Huawei Health`);
await this.context.openLink(HealthKitHostApiImpl.HEALTH_APP_LINK);
} catch (error) {
console.error(`Failed to open Huawei Health: ${error.name}: ${error.message}`);
this.openHealthAppInAppGallery();
}
}
private openHealthAppInAppGallery(): void {
const want: Want = {
parameters: {
bundleName: HealthKitHostApiImpl.HEALTH_APP_BUNDLE_NAME,
},
};
try {
productViewManager.loadProduct(this.context, want);
} catch (error) {
console.error(`Failed to open AppGallery recommendation: ${error.name}: ${error.message}`);
}
}
}