@@ -22,13 +22,13 @@ import io.flutter.plugin.common.PluginRegistry.ActivityResultListener
2222import kotlinx.coroutines.*
2323
2424/* *
25- * Main Flutter plugin class for Health Connect integration.
26- * Manages plugin lifecycle, method channel communication, permission handling,
27- * and coordinates between Flutter and Android Health Connect APIs.
25+ * Main Flutter plugin class for Health Connect integration. Manages plugin lifecycle, method
26+ * channel communication, permission handling, and coordinates between Flutter and Android Health
27+ * Connect APIs.
2828 */
2929class HealthPlugin (private var channel : MethodChannel ? = null ) :
30- MethodCallHandler , ActivityResultListener , Result , ActivityAware , FlutterPlugin {
31-
30+ MethodCallHandler , ActivityResultListener , Result , ActivityAware , FlutterPlugin {
31+
3232 private var mResult: Result ? = null
3333 private var handler: Handler ? = null
3434 private var activity: Activity ? = null
@@ -53,33 +53,32 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
5353 }
5454
5555 /* *
56- * Initializes the plugin when attached to the Flutter engine.
57- * Sets up method channel, checks Health Connect availability, and initializes helper classes.
58- *
56+ * Initializes the plugin when attached to the Flutter engine. Sets up method channel, checks
57+ * Health Connect availability, and initializes helper classes.
58+ *
5959 * @param flutterPluginBinding Plugin binding providing access to Flutter engine resources
6060 */
6161 override fun onAttachedToEngine (
62- @NonNull flutterPluginBinding : FlutterPlugin .FlutterPluginBinding
62+ @NonNull flutterPluginBinding : FlutterPlugin .FlutterPluginBinding
6363 ) {
6464 scope = CoroutineScope (SupervisorJob () + Dispatchers .Main )
6565 channel = MethodChannel (flutterPluginBinding.binaryMessenger, CHANNEL_NAME )
6666 channel?.setMethodCallHandler(this )
6767 context = flutterPluginBinding.applicationContext
6868 handler = Handler (context!! .mainLooper)
69-
69+
7070 checkAvailability()
7171 if (healthConnectAvailable) {
72- healthConnectClient = HealthConnectClient .getOrCreate(
73- flutterPluginBinding.applicationContext
74- )
72+ healthConnectClient =
73+ HealthConnectClient .getOrCreate(flutterPluginBinding.applicationContext)
7574 initializeHelpers()
7675 }
7776 }
7877
7978 /* *
80- * Cleans up resources when plugin is detached from Flutter engine.
81- * Cancels coroutines and nullifies references to prevent memory leaks.
82- *
79+ * Cleans up resources when plugin is detached from Flutter engine. Cancels coroutines and
80+ * nullifies references to prevent memory leaks.
81+ *
8382 * @param binding Plugin binding (unused in cleanup)
8483 */
8584 override fun onDetachedFromEngine (binding : FlutterPlugin .FlutterPluginBinding ) {
@@ -97,74 +96,86 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
9796 }
9897
9998 override fun error (
100- errorCode : String ,
101- errorMessage : String? ,
102- errorDetails : Any? ,
99+ errorCode : String ,
100+ errorMessage : String? ,
101+ errorDetails : Any? ,
103102 ) {
104103 handler?.post { mResult?.error(errorCode, errorMessage, errorDetails) }
105104 }
106-
105+
107106 override fun onActivityResult (requestCode : Int , resultCode : Int , data : Intent ? ): Boolean {
108107 return false
109108 }
110109
111110 /* *
112- * Handles method calls from Flutter and routes them to appropriate handler classes.
113- * Central dispatcher for all Health Connect operations including permissions,
114- * data reading, writing, and deletion.
115- *
111+ * Handles method calls from Flutter and routes them to appropriate handler classes. Central
112+ * dispatcher for all Health Connect operations including permissions, data reading, writing ,
113+ * and deletion.
114+ *
116115 * @param call Method call from Flutter containing method name and arguments
117116 * @param result Result callback to return data or status to Flutter
118117 */
119118 override fun onMethodCall (call : MethodCall , result : Result ) {
120119 when (call.method) {
121120 // SDK and Installation
122121 " installHealthConnect" -> installHealthConnect(call, result)
123- " getHealthConnectSdkStatus" -> dataOperations.getHealthConnectSdkStatus(call, result)
124-
122+ " getHealthConnectSdkStatus" -> {
123+ checkAvailability()
124+ if (healthConnectAvailable && ! (this ::dataOperations.isInitialized)) {
125+ healthConnectClient = HealthConnectClient .getOrCreate(context!! )
126+ initializeHelpers()
127+ }
128+ result.success(healthConnectStatus)
129+ }
130+
125131 // Permissions
126132 " hasPermissions" -> dataOperations.hasPermissions(call, result)
127133 " requestAuthorization" -> requestAuthorization(call, result)
128134 " revokePermissions" -> dataOperations.revokePermissions(call, result)
129-
135+
130136 // History permissions
131- " isHealthDataHistoryAvailable" -> dataOperations.isHealthDataHistoryAvailable(call, result)
132- " isHealthDataHistoryAuthorized" -> dataOperations.isHealthDataHistoryAuthorized(call, result)
133- " requestHealthDataHistoryAuthorization" -> requestHealthDataHistoryAuthorization(call, result)
134-
137+ " isHealthDataHistoryAvailable" ->
138+ dataOperations.isHealthDataHistoryAvailable(call, result)
139+ " isHealthDataHistoryAuthorized" ->
140+ dataOperations.isHealthDataHistoryAuthorized(call, result)
141+ " requestHealthDataHistoryAuthorization" ->
142+ requestHealthDataHistoryAuthorization(call, result)
143+
135144 // Background permissions
136- " isHealthDataInBackgroundAvailable" -> dataOperations.isHealthDataInBackgroundAvailable(call, result)
137- " isHealthDataInBackgroundAuthorized" -> dataOperations.isHealthDataInBackgroundAuthorized(call, result)
138- " requestHealthDataInBackgroundAuthorization" -> requestHealthDataInBackgroundAuthorization(call, result)
139-
145+ " isHealthDataInBackgroundAvailable" ->
146+ dataOperations.isHealthDataInBackgroundAvailable(call, result)
147+ " isHealthDataInBackgroundAuthorized" ->
148+ dataOperations.isHealthDataInBackgroundAuthorized(call, result)
149+ " requestHealthDataInBackgroundAuthorization" ->
150+ requestHealthDataInBackgroundAuthorization(call, result)
151+
140152 // Reading data
141153 " getData" -> dataReader.getData(call, result)
142154 " getIntervalData" -> dataReader.getIntervalData(call, result)
143155 " getAggregateData" -> dataReader.getAggregateData(call, result)
144156 " getTotalStepsInInterval" -> dataReader.getTotalStepsInInterval(call, result)
145-
157+
146158 // Writing data
147159 " writeData" -> dataWriter.writeData(call, result)
148160 " writeWorkoutData" -> dataWriter.writeWorkoutData(call, result)
149161 " writeBloodPressure" -> dataWriter.writeBloodPressure(call, result)
150162 " writeBloodOxygen" -> dataWriter.writeBloodOxygen(call, result)
151163 " writeMenstruationFlow" -> dataWriter.writeMenstruationFlow(call, result)
152164 " writeMeal" -> dataWriter.writeMeal(call, result)
153- // TODO: Add support for multiple speed for iOS as well
165+ // TODO: Add support for multiple speed for iOS as well
154166 // "writeMultipleSpeed" -> dataWriter.writeMultipleSpeedData(call, result)
155-
167+
156168 // Deleting data
157169 " delete" -> dataOperations.deleteData(call, result)
158170 " deleteByUUID" -> dataOperations.deleteByUUID(call, result)
159-
160171 else -> result.notImplemented()
161172 }
162173 }
163174
164175 /* *
165- * Called when activity is attached to the plugin.
166- * Sets up permission request launcher and activity result handling.
167- *
176+ * Called when activity is attached to the plugin. Sets up permission request launcher and
177+ * activity result handling.
178+ *
168179 * @param binding Activity plugin binding providing activity context
169180 */
170181 override fun onAttachedToActivity (binding : ActivityPluginBinding ) {
@@ -175,14 +186,14 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
175186 activity = binding.activity
176187
177188 val requestPermissionActivityContract =
178- PermissionController .createRequestPermissionResultContract()
189+ PermissionController .createRequestPermissionResultContract()
179190
180191 healthConnectRequestPermissionsLauncher =
181- (activity as ComponentActivity ).registerForActivityResult(
182- requestPermissionActivityContract
183- ) { granted -> onHealthConnectPermissionCallback(granted) }
192+ (activity as ComponentActivity ).registerForActivityResult(
193+ requestPermissionActivityContract
194+ ) { granted -> onHealthConnectPermissionCallback(granted) }
184195 }
185-
196+
186197 override fun onDetachedFromActivityForConfigChanges () {
187198 onDetachedFromActivity()
188199 }
@@ -192,8 +203,8 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
192203 }
193204
194205 /* *
195- * Called when activity is detached from plugin.
196- * Cleans up activity-specific resources and permission launchers.
206+ * Called when activity is detached from plugin. Cleans up activity-specific resources and
207+ * permission launchers.
197208 */
198209 override fun onDetachedFromActivity () {
199210 if (channel == null ) {
@@ -204,71 +215,83 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
204215 }
205216
206217 /* *
207- * Checks Health Connect availability and SDK status on the current device.
208- * Determines if Health Connect is installed and accessible.
218+ * Checks Health Connect availability and SDK status on the current device. Determines if Health
219+ * Connect is installed and accessible.
209220 */
210221 private fun checkAvailability () {
211222 healthConnectStatus = HealthConnectClient .getSdkStatus(context!! )
212223 healthConnectAvailable = healthConnectStatus == HealthConnectClient .SDK_AVAILABLE
213224 }
214225
215226 /* *
216- * Initializes helper classes for data operations after Health Connect client is ready.
217- * Creates instances of reader, writer, operations, and converter classes.
227+ * Initializes helper classes for data operations after Health Connect client is ready. Creates
228+ * instances of reader, writer, operations, and converter classes.
218229 */
219230 private fun initializeHelpers () {
220231 dataConverter = HealthDataConverter ()
221232 dataReader = HealthDataReader (healthConnectClient, scope, context!! , dataConverter)
222233 dataWriter = HealthDataWriter (healthConnectClient, scope)
223- dataOperations = HealthDataOperations (healthConnectClient, scope, healthConnectStatus, healthConnectAvailable)
234+ dataOperations =
235+ HealthDataOperations (
236+ healthConnectClient,
237+ scope,
238+ healthConnectStatus,
239+ healthConnectAvailable
240+ )
224241 }
225242
226243 /* *
227- * Launches Health Connect installation flow via Google Play Store.
228- * Directs users to install Health Connect when it's not available.
229- *
244+ * Launches Health Connect installation flow via Google Play Store. Directs users to install
245+ * Health Connect when it's not available.
246+ *
230247 * @param call Method call from Flutter (unused)
231248 * @param result Flutter result callback
232249 */
233250 private fun installHealthConnect (call : MethodCall , result : Result ) {
234251 val uriString =
235- " market://details?id=com.google.android.apps.healthdata&url=healthconnect%3A%2F%2Fonboarding"
252+ " market://details?id=com.google.android.apps.healthdata&url=healthconnect%3A%2F%2Fonboarding"
236253 context!! .startActivity(
237- Intent (Intent .ACTION_VIEW ).apply {
238- setPackage(" com.android.vending" )
239- data = android.net.Uri .parse(uriString)
240- addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
241- putExtra(" overlay" , true )
242- putExtra(" callerId" , context!! .packageName)
243- }
254+ Intent (Intent .ACTION_VIEW ).apply {
255+ setPackage(" com.android.vending" )
256+ data = android.net.Uri .parse(uriString)
257+ addFlags(Intent .FLAG_ACTIVITY_NEW_TASK )
258+ putExtra(" overlay" , true )
259+ putExtra(" callerId" , context!! .packageName)
260+ }
244261 )
245262 result.success(null )
246263 }
247264
248265 /* *
249- * Handles permission request results from Health Connect permission dialog.
250- * Called when user responds to permission request, updates Flutter with result.
251- *
266+ * Handles permission request results from Health Connect permission dialog. Called when user
267+ * responds to permission request, updates Flutter with result.
268+ *
252269 * @param permissionGranted Set of permission strings that were granted
253270 */
254271 private fun onHealthConnectPermissionCallback (permissionGranted : Set <String >) {
255272 if (! isReplySubmitted) {
256273 if (permissionGranted.isEmpty()) {
257274 mResult?.success(false )
258- Log .i(" FLUTTER_HEALTH" , " Health Connect permissions were not granted! Make sure to declare the required permissions in the AndroidManifest.xml file." )
275+ Log .i(
276+ " FLUTTER_HEALTH" ,
277+ " Health Connect permissions were not granted! Make sure to declare the required permissions in the AndroidManifest.xml file."
278+ )
259279 } else {
260280 mResult?.success(true )
261- Log .i(" FLUTTER_HEALTH" , " ${permissionGranted.size} Health Connect permissions were granted!" )
262- Log .i(" FLUTTER_HEALTH" , " Permissions granted: $permissionGranted " )
281+ Log .i(
282+ " FLUTTER_HEALTH" ,
283+ " ${permissionGranted.size} Health Connect permissions were granted!"
284+ )
285+ Log .i(" FLUTTER_HEALTH" , " Permissions granted: $permissionGranted " )
263286 }
264287 isReplySubmitted = true
265288 }
266289 }
267290
268291 /* *
269- * Initiates Health Connect permission request flow.
270- * Prepares permission list and launches system permission dialog.
271- *
292+ * Initiates Health Connect permission request flow. Prepares permission list and launches
293+ * system permission dialog.
294+ *
272295 * @param call Method call containing permission types and access levels
273296 * @param result Flutter result callback for permission request outcome
274297 */
@@ -287,20 +310,20 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
287310 // Store the result to be called in onHealthConnectPermissionCallback
288311 mResult = result
289312 isReplySubmitted = false
290-
313+
291314 val permList = dataOperations.preparePermissionsList(call)
292315 if (permList == null ) {
293316 result.success(false )
294317 return
295318 }
296-
319+
297320 healthConnectRequestPermissionsLauncher!! .launch(permList.toSet())
298321 }
299322
300323 /* *
301- * Requests specific permission for accessing health data history.
302- * Launches permission dialog for historical data access capability.
303- *
324+ * Requests specific permission for accessing health data history. Launches permission dialog
325+ * for historical data access capability.
326+ *
304327 * @param call Method call from Flutter (unused)
305328 * @param result Flutter result callback for permission request outcome
306329 */
@@ -314,14 +337,14 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
314337 mResult = result
315338 isReplySubmitted = false
316339 healthConnectRequestPermissionsLauncher!! .launch(
317- setOf (HealthPermission .PERMISSION_READ_HEALTH_DATA_HISTORY )
340+ setOf (HealthPermission .PERMISSION_READ_HEALTH_DATA_HISTORY )
318341 )
319342 }
320343
321344 /* *
322- * Requests specific permission for background health data access.
323- * Launches permission dialog for background data reading capability.
324- *
345+ * Requests specific permission for background health data access. Launches permission dialog
346+ * for background data reading capability.
347+ *
325348 * @param call Method call from Flutter (unused)
326349 * @param result Flutter result callback for permission request outcome
327350 */
@@ -335,7 +358,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
335358 mResult = result
336359 isReplySubmitted = false
337360 healthConnectRequestPermissionsLauncher!! .launch(
338- setOf (HealthPermission .PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND )
361+ setOf (HealthPermission .PERMISSION_READ_HEALTH_DATA_IN_BACKGROUND )
339362 )
340363 }
341- }
364+ }
0 commit comments