Skip to content

Commit 2e34b37

Browse files
Auto-request authorization in case query is called without permission #11
1 parent bf9de03 commit 2e34b37

File tree

6 files changed

+78
-52
lines changed

6 files changed

+78
-52
lines changed

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,12 @@ export class MyHealthyClass {
5454
```
5555

5656
### `isAvailable`
57-
This function does not return a Promise; it resolves immediately, and tells you whether or not the device supports Health Data. On iOS this is probably always `true`. On Android the user will be prompted to (automatically) update their Play Services version in case it's not sufficiently up to date.
57+
This tells you whether or not the device supports Health Data. On iOS this is probably always `true`.
58+
On Android the user will be prompted to (automatically) update their Play Services version in case it's not sufficiently up to date.
59+
If you don't want this behavior, pass false to this function, as shown below.
5860

5961
```typescript
60-
this.healthData.isAvailable()
62+
this.healthData.isAvailable(false)
6163
.then(available => console.log(available));
6264
```
6365

@@ -98,6 +100,9 @@ By default data is not aggregated, so all individual datapoints are returned.
98100
This plugin however offers a way to aggregate the data by either `hour`, `day`, or `sourceAndDay`,
99101
the latter will enable you to read daily data per source (Fitbit, Nike Run Club, manual entry, etc).
100102

103+
If you didn't run `requestAuthorization` before running `query`,
104+
the plugin will run `requestAuthorization` for you (for the requested `dataType`). You're welcome. 😉
105+
101106
```typescript
102107
this.healthData.query(
103108
{

demo-ng/app/app.component.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { AggregateBy, HealthData, HealthDataType } from "nativescript-health-dat
99

1010
export class AppComponent {
1111
private static TYPES: Array<HealthDataType> = [
12-
{name: "height", accessType: "write"},
13-
{name: "weight", accessType: "readAndWrite"},
12+
{name: "height", accessType: "read"},
13+
{name: "weight", accessType: "readAndWrite"}, // just for show
1414
{name: "steps", accessType: "read"},
1515
{name: "distance", accessType: "read"},
1616
{name: "heartRate", accessType: "read"},
@@ -25,13 +25,17 @@ export class AppComponent {
2525
}
2626

2727
isAvailable(): void {
28-
this.healthData.isAvailable()
28+
this.healthData.isAvailable(true)
2929
.then(available => this.resultToShow = available ? "Health Data available" : "Health Data not available :(");
3030
}
3131

3232
isAuthorized(): void {
3333
this.healthData.isAuthorized([<HealthDataType>{name: "weight", accessType: "read"}])
34-
.then(authorized => this.resultToShow = (authorized ? "" : "Not ") + "authorized for " + JSON.stringify(AppComponent.TYPES));
34+
.then(authorized => setTimeout(() => alert({
35+
title: "Authentication result",
36+
message: (authorized ? "" : "Not ") + "authorized for " + JSON.stringify(AppComponent.TYPES),
37+
okButtonText: "Ok!"
38+
}), 300))
3539
}
3640

3741
requestAuthForVariousTypes(): void {

src/health-data.android.ts

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ const FitnessOptions = com.google.android.gms.fitness.FitnessOptions;
1818
const GoogleSignIn = com.google.android.gms.auth.api.signin.GoogleSignIn;
1919

2020
export class HealthData extends Common implements HealthDataApi {
21-
isAvailable(): Promise<boolean> {
21+
isAvailable(updateGooglePlayServicesIfNeeded = true): Promise<boolean> {
2222
return new Promise<boolean>((resolve, reject) => {
2323
const gApi = GoogleApiAvailability.getInstance();
2424
const apiResult = gApi.isGooglePlayServicesAvailable(utils.ad.getApplicationContext());
2525
const available = apiResult === com.google.android.gms.common.ConnectionResult.SUCCESS;
26-
if (!available && gApi.isUserResolvableError(apiResult)) {
26+
if (!available && updateGooglePlayServicesIfNeeded && gApi.isUserResolvableError(apiResult)) {
2727
// show a dialog offering the user to update (no need to wait for it to finish)
28-
gApi.showErrorDialogFragment(application.android.foregroundActivity, apiResult, 1, new android.content.DialogInterface.OnCancelListener({
28+
gApi.showErrorDialogFragment(application.android.foregroundActivity || application.android.startActivity, apiResult, 1, new android.content.DialogInterface.OnCancelListener({
2929
onCancel: dialogInterface => console.log("Google Play Services update dialog was canceled")
3030
}));
3131
}
@@ -81,31 +81,40 @@ export class HealthData extends Common implements HealthDataApi {
8181
query(opts: QueryRequest): Promise<Array<ResponseItem>> {
8282
return new Promise((resolve, reject) => {
8383
try {
84-
const readRequest = new DataReadRequest.Builder()
85-
// using 'read' instead of 'aggregate' for now, for more finegrain control
86-
// .aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
87-
// .bucketByTime(1, TimeUnit.HOURS)
88-
.read(this.getDataType(opts.dataType))
89-
.setTimeRange(opts.startDate.getTime(), opts.endDate.getTime(), TimeUnit.MILLISECONDS)
90-
.build();
91-
92-
Fitness.getHistoryClient(application.android.currentContext, GoogleSignIn.getLastSignedInAccount(application.android.currentContext))
93-
.readData(readRequest)
94-
.addOnSuccessListener(new com.google.android.gms.tasks.OnSuccessListener({
95-
onSuccess: (dataReadResponse: any /* com.google.android.gms.fitness.result.DataReadResponse */) => {
96-
resolve(this.parseData(dataReadResponse.getResult(), opts));
97-
}
98-
}))
99-
.addOnFailureListener(new com.google.android.gms.tasks.OnFailureListener({
100-
onFailure: (exception: any) => {
101-
reject(exception.getMessage());
102-
}
103-
}))
104-
.addOnCompleteListener(new com.google.android.gms.tasks.OnCompleteListener({
105-
onComplete: (task: any) => {
106-
// noop
107-
}
108-
}));
84+
// make sure the user is authorized
85+
this.requestAuthorization([{ name: opts.dataType, accessType: "read"}]).then(authorized => {
86+
if (!authorized) {
87+
reject("Not authorized");
88+
return;
89+
}
90+
91+
const readRequest = new DataReadRequest.Builder()
92+
// using 'read' instead of 'aggregate' for now, for more finegrain control
93+
// .aggregate(DataType.TYPE_STEP_COUNT_DELTA, DataType.AGGREGATE_STEP_COUNT_DELTA)
94+
// .bucketByTime(1, TimeUnit.HOURS)
95+
.read(this.getDataType(opts.dataType))
96+
.setTimeRange(opts.startDate.getTime(), opts.endDate.getTime(), TimeUnit.MILLISECONDS)
97+
.build();
98+
99+
Fitness.getHistoryClient(application.android.currentContext, GoogleSignIn.getLastSignedInAccount(application.android.currentContext))
100+
.readData(readRequest)
101+
.addOnSuccessListener(new com.google.android.gms.tasks.OnSuccessListener({
102+
onSuccess: (dataReadResponse: any /* com.google.android.gms.fitness.result.DataReadResponse */) => {
103+
resolve(this.parseData(dataReadResponse.getResult(), opts));
104+
}
105+
}))
106+
.addOnFailureListener(new com.google.android.gms.tasks.OnFailureListener({
107+
onFailure: (exception: any) => {
108+
reject(exception.getMessage());
109+
}
110+
}))
111+
.addOnCompleteListener(new com.google.android.gms.tasks.OnCompleteListener({
112+
onComplete: (task: any) => {
113+
// noop
114+
}
115+
}));
116+
});
117+
109118
} catch (e) {
110119
reject(e);
111120
}

src/health-data.common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function createErrorResponse(action: string, description: string): ErrorR
7575
*/
7676

7777
export interface HealthDataApi {
78-
isAvailable(): Promise<boolean>;
78+
isAvailable(updateGooglePlayServicesIfNeeded? /* for Android, default true */: boolean): Promise<boolean>;
7979

8080
isAuthorized(types: Array<HealthDataType>): Promise<boolean>;
8181

src/health-data.ios.ts

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { AggregateBy, Common, HealthDataApi, HealthDataType, QueryRequest, ResponseItem } from './health-data.common';
1+
import { Common, HealthDataApi, HealthDataType, QueryRequest, ResponseItem } from './health-data.common';
22

33
export class HealthData extends Common implements HealthDataApi {
44
private healthStore: HKHealthStore;
@@ -10,7 +10,7 @@ export class HealthData extends Common implements HealthDataApi {
1010
}
1111
}
1212

13-
isAvailable(): Promise<boolean> {
13+
isAvailable(updateGooglePlayServicesIfNeeded? /* for Android */: boolean): Promise<boolean> {
1414
return new Promise<boolean>((resolve, reject) => {
1515
resolve(this.healthStore !== undefined);
1616
});
@@ -62,20 +62,28 @@ export class HealthData extends Common implements HealthDataApi {
6262

6363
query(opts: QueryRequest): Promise<Array<ResponseItem>> {
6464
return new Promise((resolve, reject) => {
65-
let typeOfData = acceptableDataTypes[opts.dataType];
66-
if (quantityTypes[typeOfData] || categoryTypes[typeOfData]) {
67-
this.queryForQuantityOrCategoryData(typeOfData, opts, (res, error) => {
68-
if (error) {
69-
reject(error);
70-
} else {
71-
resolve(res);
72-
}
73-
});
74-
// } else if (characteristicTypes[typeOfData]) {
75-
// resolve(this.queryForCharacteristicData(typeOfData));
76-
} else {
77-
reject('Type not supported (yet)');
78-
}
65+
// make sure the user is authorized
66+
this.requestAuthorization([{ name: opts.dataType, accessType: "read"}]).then(authorized => {
67+
if (!authorized) {
68+
reject("Not authorized");
69+
return;
70+
}
71+
72+
let typeOfData = acceptableDataTypes[opts.dataType];
73+
if (quantityTypes[typeOfData] || categoryTypes[typeOfData]) {
74+
this.queryForQuantityOrCategoryData(typeOfData, opts, (res, error) => {
75+
if (error) {
76+
reject(error);
77+
} else {
78+
resolve(res);
79+
}
80+
});
81+
// } else if (characteristicTypes[typeOfData]) {
82+
// resolve(this.queryForCharacteristicData(typeOfData));
83+
} else {
84+
reject('Type not supported (yet)');
85+
}
86+
});
7987
});
8088
}
8189

src/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nativescript-health-data",
3-
"version": "1.0.1",
3+
"version": "1.0.2",
44
"description": "Health Data plugin for Nativescript, using Google Fit and Apple HealthKit.",
55
"main": "health-data",
66
"typings": "index.d.ts",

0 commit comments

Comments
 (0)