@flomentumsolutions/capacitor-health-extended 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapacitorHealthExtended.podspec +17 -0
- package/LICENSE +21 -0
- package/Package.swift +28 -0
- package/README.md +343 -0
- package/android/build.gradle +70 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/flomentum/health/capacitor/HealthPlugin.kt +677 -0
- package/android/src/main/java/com/flomentum/health/capacitor/PermissionsRationaleActivity.kt +86 -0
- package/android/src/main/res/.gitkeep +0 -0
- package/dist/esm/definitions.d.ts +110 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/plugin.cjs.js +10 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +13 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/HealthPluginPlugin/HealthPlugin.swift +671 -0
- package/package.json +85 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ──────────────────────────────────────────────────────────────────────────────
|
|
3
|
+
* IMPORTANT SETUP NOTICE FOR HEALTH CONNECT PERMISSIONS RATIONALE ACTIVITY
|
|
4
|
+
* ──────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* This `PermissionsRationaleActivity` class must be copied into your **end-user app**
|
|
7
|
+
* (typically at: `android/app/src/main/java/<your-app-package>/PermissionsRationaleActivity.kt`)
|
|
8
|
+
* and registered in your app's `AndroidManifest.xml`.
|
|
9
|
+
*
|
|
10
|
+
* WHY THIS IS NECESSARY:
|
|
11
|
+
* Health Connect requires this Activity to be declared in the final **merged manifest**.
|
|
12
|
+
* Android will not recognize it if it's only inside a Capacitor plugin or library module.
|
|
13
|
+
* If this class is not present in the main app project, the following issues will occur:
|
|
14
|
+
*
|
|
15
|
+
* - `Health Connect rationale screen not found` warnings
|
|
16
|
+
* - The Health Connect permissions dialog may silently fail to appear
|
|
17
|
+
* - Permissions will never be granted, breaking health data access on Android
|
|
18
|
+
*
|
|
19
|
+
* ──────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
* TO FIX:
|
|
21
|
+
* 1. Copy this file to your app's native Android directory.
|
|
22
|
+
* 2. Add the `<activity>` and `<activity-alias>` declarations for this class
|
|
23
|
+
* into your AndroidManifest.xml file (in the app module). --> You can find an example manifest block in the plugin README.
|
|
24
|
+
* 3. Rebuild and re-deploy your app.
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
// package com.[YOUR_ORGANIZATION].[YOUR_APP...]
|
|
29
|
+
|
|
30
|
+
import android.os.Bundle
|
|
31
|
+
import android.util.Log
|
|
32
|
+
import android.webkit.WebChromeClient
|
|
33
|
+
import android.webkit.WebView
|
|
34
|
+
import android.webkit.WebViewClient
|
|
35
|
+
import androidx.activity.addCallback
|
|
36
|
+
import androidx.appcompat.app.AppCompatActivity
|
|
37
|
+
|
|
38
|
+
class PermissionsRationaleActivity : AppCompatActivity() {
|
|
39
|
+
private lateinit var webView: WebView
|
|
40
|
+
|
|
41
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
42
|
+
super.onCreate(savedInstanceState)
|
|
43
|
+
|
|
44
|
+
supportActionBar?.apply {
|
|
45
|
+
title = "Privacy Policy"
|
|
46
|
+
setDisplayHomeAsUpEnabled(true)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
webView = WebView(this).apply {
|
|
50
|
+
settings.apply {
|
|
51
|
+
javaScriptEnabled = true
|
|
52
|
+
domStorageEnabled = true
|
|
53
|
+
useWideViewPort = true
|
|
54
|
+
loadWithOverviewMode = true
|
|
55
|
+
}
|
|
56
|
+
webChromeClient = WebChromeClient()
|
|
57
|
+
webViewClient = object : WebViewClient() {
|
|
58
|
+
override fun shouldOverrideUrlLoading(view: WebView, request: android.webkit.WebResourceRequest) = false
|
|
59
|
+
override fun onReceivedError(
|
|
60
|
+
view: WebView,
|
|
61
|
+
request: android.webkit.WebResourceRequest,
|
|
62
|
+
error: android.webkit.WebResourceError
|
|
63
|
+
) {
|
|
64
|
+
Log.e("WebView", "Failed to load: ${error.description}")
|
|
65
|
+
}
|
|
66
|
+
override fun onPageFinished(view: WebView, url: String) {
|
|
67
|
+
Log.d("WebView", "Loaded: $url")
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
loadUrl("https://flomentumsolutions.com/privacy-policy")
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setContentView(webView)
|
|
74
|
+
|
|
75
|
+
// Device back button behavior
|
|
76
|
+
onBackPressedDispatcher.addCallback(this) {
|
|
77
|
+
finish()
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Toolbar Up button behavior
|
|
82
|
+
override fun onSupportNavigateUp(): Boolean {
|
|
83
|
+
finish()
|
|
84
|
+
return true
|
|
85
|
+
}
|
|
86
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
export interface HealthPlugin {
|
|
2
|
+
/**
|
|
3
|
+
* Checks if health API is available.
|
|
4
|
+
* Android: If false is returned, the Google Health Connect app is probably not installed.
|
|
5
|
+
* See showHealthConnectInPlayStore()
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
isHealthAvailable(): Promise<{
|
|
9
|
+
available: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Android only: Returns for each given permission, if it was granted by the underlying health API
|
|
13
|
+
* @param permissions permissions to query
|
|
14
|
+
*/
|
|
15
|
+
checkHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;
|
|
16
|
+
/**
|
|
17
|
+
* Requests the permissions from the user.
|
|
18
|
+
*
|
|
19
|
+
* Android: Apps can ask only a few times for permissions, after that the user has to grant them manually in
|
|
20
|
+
* the Health Connect app. See openHealthConnectSettings()
|
|
21
|
+
*
|
|
22
|
+
* iOS: If the permissions are already granted or denied, this method will just return without asking the user. In iOS
|
|
23
|
+
* we can't really detect if a user granted or denied a permission. The return value reflects the assumption that all
|
|
24
|
+
* permissions were granted.
|
|
25
|
+
*
|
|
26
|
+
* @param permissions permissions to request
|
|
27
|
+
*/
|
|
28
|
+
requestHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;
|
|
29
|
+
/**
|
|
30
|
+
* Opens the apps settings, which is kind of wrong, because health permissions are configured under:
|
|
31
|
+
* Settings > Apps > (Apple) Health > Access and Devices > [app-name]
|
|
32
|
+
* But we can't go there directly.
|
|
33
|
+
*/
|
|
34
|
+
openAppleHealthSettings(): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Opens the Google Health Connect app
|
|
37
|
+
*/
|
|
38
|
+
openHealthConnectSettings(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Opens the Google Health Connect app in PlayStore
|
|
41
|
+
*/
|
|
42
|
+
showHealthConnectInPlayStore(): Promise<void>;
|
|
43
|
+
/**
|
|
44
|
+
* Query aggregated data
|
|
45
|
+
* @param request
|
|
46
|
+
*/
|
|
47
|
+
queryAggregated(request: QueryAggregatedRequest): Promise<QueryAggregatedResponse>;
|
|
48
|
+
/**
|
|
49
|
+
* Query workouts
|
|
50
|
+
* @param request
|
|
51
|
+
*/
|
|
52
|
+
queryWorkouts(request: QueryWorkoutRequest): Promise<QueryWorkoutResponse>;
|
|
53
|
+
}
|
|
54
|
+
export declare type HealthPermission = 'READ_STEPS' | 'READ_WORKOUTS' | 'READ_ACTIVE_CALORIES' | 'READ_TOTAL_CALORIES' | 'READ_DISTANCE' | 'READ_HEART_RATE' | 'READ_ROUTE' | 'READ_MINDFULNESS';
|
|
55
|
+
export interface PermissionsRequest {
|
|
56
|
+
permissions: HealthPermission[];
|
|
57
|
+
}
|
|
58
|
+
export interface PermissionResponse {
|
|
59
|
+
permissions: {
|
|
60
|
+
[key: string]: boolean;
|
|
61
|
+
}[];
|
|
62
|
+
}
|
|
63
|
+
export interface QueryWorkoutRequest {
|
|
64
|
+
startDate: string;
|
|
65
|
+
endDate: string;
|
|
66
|
+
includeHeartRate: boolean;
|
|
67
|
+
includeRoute: boolean;
|
|
68
|
+
includeSteps: boolean;
|
|
69
|
+
}
|
|
70
|
+
export interface HeartRateSample {
|
|
71
|
+
timestamp: string;
|
|
72
|
+
bpm: number;
|
|
73
|
+
}
|
|
74
|
+
export interface RouteSample {
|
|
75
|
+
timestamp: string;
|
|
76
|
+
lat: number;
|
|
77
|
+
lng: number;
|
|
78
|
+
alt?: number;
|
|
79
|
+
}
|
|
80
|
+
export interface QueryWorkoutResponse {
|
|
81
|
+
workouts: Workout[];
|
|
82
|
+
}
|
|
83
|
+
export interface Workout {
|
|
84
|
+
startDate: string;
|
|
85
|
+
endDate: string;
|
|
86
|
+
workoutType: string;
|
|
87
|
+
sourceName: string;
|
|
88
|
+
id?: string;
|
|
89
|
+
duration: number;
|
|
90
|
+
distance?: number;
|
|
91
|
+
steps?: number;
|
|
92
|
+
calories: number;
|
|
93
|
+
sourceBundleId: string;
|
|
94
|
+
route?: RouteSample[];
|
|
95
|
+
heartRate?: HeartRateSample[];
|
|
96
|
+
}
|
|
97
|
+
export interface QueryAggregatedRequest {
|
|
98
|
+
startDate: string;
|
|
99
|
+
endDate: string;
|
|
100
|
+
dataType: 'steps' | 'active-calories' | 'mindfulness';
|
|
101
|
+
bucket: string;
|
|
102
|
+
}
|
|
103
|
+
export interface QueryAggregatedResponse {
|
|
104
|
+
aggregatedData: AggregatedSample[];
|
|
105
|
+
}
|
|
106
|
+
export interface AggregatedSample {
|
|
107
|
+
startDate: string;
|
|
108
|
+
endDate: string;
|
|
109
|
+
value: number;
|
|
110
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface HealthPlugin {\n /**\n * Checks if health API is available.\n * Android: If false is returned, the Google Health Connect app is probably not installed.\n * See showHealthConnectInPlayStore()\n *\n */\n isHealthAvailable(): Promise<{ available: boolean }>;\n\n /**\n * Android only: Returns for each given permission, if it was granted by the underlying health API\n * @param permissions permissions to query\n */\n checkHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;\n\n /**\n * Requests the permissions from the user.\n *\n * Android: Apps can ask only a few times for permissions, after that the user has to grant them manually in\n * the Health Connect app. See openHealthConnectSettings()\n *\n * iOS: If the permissions are already granted or denied, this method will just return without asking the user. In iOS\n * we can't really detect if a user granted or denied a permission. The return value reflects the assumption that all\n * permissions were granted.\n *\n * @param permissions permissions to request\n */\n requestHealthPermissions(permissions: PermissionsRequest): Promise<PermissionResponse>;\n\n /**\n * Opens the apps settings, which is kind of wrong, because health permissions are configured under:\n * Settings > Apps > (Apple) Health > Access and Devices > [app-name]\n * But we can't go there directly.\n */\n openAppleHealthSettings(): Promise<void>;\n\n /**\n * Opens the Google Health Connect app\n */\n openHealthConnectSettings(): Promise<void>;\n\n /**\n * Opens the Google Health Connect app in PlayStore\n */\n showHealthConnectInPlayStore(): Promise<void>;\n\n /**\n * Query aggregated data\n * @param request\n */\n queryAggregated(request: QueryAggregatedRequest): Promise<QueryAggregatedResponse>;\n\n /**\n * Query workouts\n * @param request\n */\n queryWorkouts(request: QueryWorkoutRequest): Promise<QueryWorkoutResponse>;\n}\n\nexport declare type HealthPermission =\n | 'READ_STEPS'\n | 'READ_WORKOUTS'\n | 'READ_ACTIVE_CALORIES'\n | 'READ_TOTAL_CALORIES'\n | 'READ_DISTANCE'\n | 'READ_HEART_RATE'\n | 'READ_ROUTE'\n | 'READ_MINDFULNESS';\n\nexport interface PermissionsRequest {\n permissions: HealthPermission[];\n}\n\nexport interface PermissionResponse {\n permissions: { [key: string]: boolean }[];\n}\n\nexport interface QueryWorkoutRequest {\n startDate: string;\n endDate: string;\n includeHeartRate: boolean;\n includeRoute: boolean;\n includeSteps: boolean;\n}\n\nexport interface HeartRateSample {\n timestamp: string;\n bpm: number;\n}\n\nexport interface RouteSample {\n timestamp: string;\n lat: number;\n lng: number;\n alt?: number;\n}\n\nexport interface QueryWorkoutResponse {\n workouts: Workout[];\n}\n\nexport interface Workout {\n startDate: string;\n endDate: string;\n workoutType: string;\n sourceName: string;\n id?: string;\n duration: number;\n distance?: number;\n steps?: number;\n calories: number;\n sourceBundleId: string;\n route?: RouteSample[];\n heartRate?: HeartRateSample[];\n}\n\nexport interface QueryAggregatedRequest {\n startDate: string;\n endDate: string;\n dataType: 'steps' | 'active-calories' | 'mindfulness';\n bucket: string;\n}\n\nexport interface QueryAggregatedResponse {\n aggregatedData: AggregatedSample[];\n}\n\nexport interface AggregatedSample {\n startDate: string;\n endDate: string;\n value: number;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,CAAC,MAAM,MAAM,GAAG,cAAc,CAAe,cAAc,EAAE,EAAE,CAAC,CAAC;AAEvE,cAAc,eAAe,CAAA","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { HealthPlugin } from './definitions';\n\nexport const Health = registerPlugin<HealthPlugin>('HealthPlugin', {});\n\nexport * from './definitions'\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.cjs.js","sources":["esm/index.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nexport const Health = registerPlugin('HealthPlugin', {});\nexport * from './definitions';\n//# sourceMappingURL=index.js.map"],"names":["registerPlugin"],"mappings":";;;;;;AACY,MAAC,MAAM,GAAGA,mBAAc,CAAC,cAAc,EAAE,EAAE;;;;"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
var capacitorHealthPlugin = (function (exports, core) {
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const Health = core.registerPlugin('HealthPlugin', {});
|
|
5
|
+
|
|
6
|
+
exports.Health = Health;
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
9
|
+
|
|
10
|
+
return exports;
|
|
11
|
+
|
|
12
|
+
})({}, capacitorExports);
|
|
13
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["esm/index.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nexport const Health = registerPlugin('HealthPlugin', {});\nexport * from './definitions';\n//# sourceMappingURL=index.js.map"],"names":["registerPlugin"],"mappings":";;;AACY,OAAC,MAAM,GAAGA,mBAAc,CAAC,cAAc,EAAE,EAAE;;;;;;;;;;;;"}
|