1 /**
<lambda>null2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * ```
8  *    http://www.apache.org/licenses/LICENSE-2.0
9  * ```
10  *
11  * Unless required by applicable law or agreed to in writing, software distributed under the License
12  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13  * or implied. See the License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 package com.android.healthconnect.testapps.toolbox.ui
17 
18 import android.content.ComponentName
19 import android.content.Intent
20 import android.content.pm.PackageManager
21 import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
22 import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
23 import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
24 import android.health.connect.HealthConnectManager
25 import android.os.Bundle
26 import android.view.LayoutInflater
27 import android.view.View
28 import android.view.ViewGroup
29 import android.widget.Button
30 import android.widget.Toast
31 import androidx.activity.result.ActivityResultLauncher
32 import androidx.activity.result.contract.ActivityResultContracts
33 import androidx.core.content.ContextCompat
34 import androidx.fragment.app.Fragment
35 import androidx.fragment.app.viewModels
36 import androidx.navigation.NavController
37 import androidx.navigation.fragment.findNavController
38 import com.android.healthconnect.testapps.toolbox.Constants.ADDITIONAL_PERMISSIONS
39 import com.android.healthconnect.testapps.toolbox.Constants.FITNESS_PERMISSIONS
40 import com.android.healthconnect.testapps.toolbox.Constants.FITNESS_READ_PERMISSIONS
41 import com.android.healthconnect.testapps.toolbox.Constants.FITNESS_WRITE_PERMISSIONS
42 import com.android.healthconnect.testapps.toolbox.Constants.HEALTH_PERMISSIONS
43 import com.android.healthconnect.testapps.toolbox.Constants.MEDICAL_PERMISSIONS
44 import com.android.healthconnect.testapps.toolbox.Constants.MEDICAL_READ_PERMISSIONS
45 import com.android.healthconnect.testapps.toolbox.Constants.MEDICAL_WRITE_AND_OTHERS
46 import com.android.healthconnect.testapps.toolbox.Constants.READ_HEALTH_DATA_HISTORY
47 import com.android.healthconnect.testapps.toolbox.Constants.READ_HEALTH_DATA_IN_BACKGROUND
48 import com.android.healthconnect.testapps.toolbox.Constants.WRITE_ALL_MEDICAL_DATA
49 import com.android.healthconnect.testapps.toolbox.PerformanceTestingFragment
50 import com.android.healthconnect.testapps.toolbox.R
51 import com.android.healthconnect.testapps.toolbox.seed.SeedData
52 import com.android.healthconnect.testapps.toolbox.viewmodels.HomeFragmentViewModel
53 import com.android.healthconnect.testapps.toolbox.viewmodels.PerformanceTestingViewModel
54 import kotlin.system.exitProcess
55 
56 /** Home fragment for Health Connect Toolbox. */
57 class HomeFragment : Fragment() {
58 
59     override fun onCreateView(
60         inflater: LayoutInflater,
61         container: ViewGroup?,
62         savedInstanceState: Bundle?,
63     ): View? {
64         return inflater.inflate(R.layout.fragment_home, container, false)
65     }
66 
67     private lateinit var mRequestPermissionLauncher: ActivityResultLauncher<Array<String>>
68     private lateinit var mNavigationController: NavController
69     private val performanceTestingViewModel: PerformanceTestingViewModel by viewModels()
70     private val homeFragmentViewModel: HomeFragmentViewModel by viewModels()
71 
72     private val manager by lazy {
73         requireContext().getSystemService(HealthConnectManager::class.java)
74     }
75 
76     override fun onCreate(savedInstanceState: Bundle?) {
77         super.onCreate(savedInstanceState)
78 
79         // Starting API Level 30 If permission is denied more than once, user doesn't see the dialog
80         // asking permissions again unless they grant the permission from settings.
81         mRequestPermissionLauncher =
82             registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
83                 permissionMap: Map<String, Boolean> ->
84                 requestPermissionResultHandler(permissionMap)
85             }
86     }
87 
88     private fun requestPermissionResultHandler(permissionMap: Map<String, Boolean>) {
89         var numberOfPermissionsMissing = HEALTH_PERMISSIONS.size
90         for (value in permissionMap.values) {
91             if (value) {
92                 numberOfPermissionsMissing--
93             }
94         }
95 
96         if (numberOfPermissionsMissing == 0) {
97             Toast.makeText(
98                     this.requireContext(),
99                     R.string.all_permissions_success,
100                     Toast.LENGTH_SHORT,
101                 )
102                 .show()
103         } else {
104             Toast.makeText(
105                     this.requireContext(),
106                     getString(
107                         R.string.number_of_permissions_not_granted,
108                         numberOfPermissionsMissing,
109                     ),
110                     Toast.LENGTH_SHORT,
111                 )
112                 .show()
113         }
114     }
115 
116     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
117         super.onViewCreated(view, savedInstanceState)
118         val performanceTesting = PerformanceTestingFragment()
119         childFragmentManager
120             .beginTransaction()
121             .add(performanceTesting, "PERFORMANCE_TESTING_FRAGMENT")
122             .commit()
123         view.requireViewById<Button>(R.id.launch_health_connect_button).setOnClickListener {
124             launchHealthConnect()
125         }
126         view.requireViewById<Button>(R.id.request_data_type_permissions_button).setOnClickListener {
127             requestDataTypePermissions()
128         }
129         view.requireViewById<Button>(R.id.request_route_button).setOnClickListener {
130             goToRequestRoute()
131         }
132         view.requireViewById<Button>(R.id.insert_update_data_button).setOnClickListener {
133             goToCategoryListPage()
134         }
135         view.requireViewById<Button>(R.id.seed_random_data_button).setOnClickListener {
136             seedDataButtonPressed()
137         }
138         view.requireViewById<Button>(R.id.seed_all_data_button).setOnClickListener {
139             seedAllDataButtonPressed()
140         }
141         view.requireViewById<Button>(R.id.seed_performance_read_data_button).setOnClickListener {
142             performanceTestingViewModel.beginReadingData()
143         }
144         view.requireViewById<Button>(R.id.seed_performance_insert_data_button).setOnClickListener {
145             performanceTestingViewModel.beginInsertingData(false)
146         }
147         view.requireViewById<Button>(R.id.toggle_permission_intent_filter).setOnClickListener {
148             togglePermissionIntentFilter()
149         }
150         view.requireViewById<Button>(R.id.read_data_in_background_button).setOnClickListener {
151             goToReadDataInBackgroundPage()
152         }
153         view.requireViewById<Button>(R.id.read_data_in_foreground_button).setOnClickListener {
154             goToReadDataInForegroundPage()
155         }
156         view.findViewById<Button>(R.id.phr_options_button).setOnClickListener { goToPhrOptions() }
157         view.requireViewById<Button>(R.id.exit_process_button).setOnClickListener {
158             exitProcess(status = 0)
159         }
160 
161         view.findViewById<Button>(R.id.request_health_permissions).setOnClickListener {
162             requestHealthPermissions()
163         }
164 
165         view.findViewById<Button>(R.id.request_additional_permissions).setOnClickListener {
166             requestAdditionalPermissions()
167         }
168 
169         view.findViewById<Button>(R.id.request_bg_read_permission).setOnClickListener {
170             requestBgReadPermission()
171         }
172 
173         view.findViewById<Button>(R.id.request_history_read_permission).setOnClickListener {
174             requestHistoryReadPermission()
175         }
176 
177         view.findViewById<Button>(R.id.request_medical_write_permission).setOnClickListener {
178             requestMedicalWritePermission()
179         }
180 
181         view
182             .findViewById<Button>(R.id.request_medical_write_and_other_health_permissions)
183             .setOnClickListener { requestMedicalWriteAndOtherPermissions() }
184 
185         view.findViewById<Button>(R.id.request_medical_read).setOnClickListener {
186             requestMedicalReadPermissions()
187         }
188 
189         view.findViewById<Button>(R.id.request_fitness_read).setOnClickListener {
190             requestFitnessRead()
191         }
192 
193         view.findViewById<Button>(R.id.request_fitness_write).setOnClickListener {
194             requestFitnessWrite()
195         }
196 
197         // view
198         //     .findViewById<Button>(R.id.seed_performance_insert_data_button_in_parallel)
199         //     .setOnClickListener { performanceTestingViewModel.beginInsertingData(true) }
200         mNavigationController = findNavController()
201 
202         homeFragmentViewModel.seedAllDataState.observe(viewLifecycleOwner) { state ->
203             if (state is HomeFragmentViewModel.SeedAllDataState.Error) {
204                 Toast.makeText(context, state.errorMessage, Toast.LENGTH_SHORT).show()
205             } else {
206                 Toast.makeText(context, R.string.toast_seed_data_success, Toast.LENGTH_SHORT).show()
207             }
208         }
209     }
210 
211     private fun launchHealthConnect() {
212         val intent = Intent("android.health.connect.action.HEALTH_HOME_SETTINGS")
213         intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
214         startActivity(intent)
215     }
216 
217     private fun seedDataButtonPressed() {
218         try {
219             SeedData(requireContext(), manager).seedData()
220             Toast.makeText(
221                     this.requireContext(),
222                     R.string.toast_seed_data_success,
223                     Toast.LENGTH_SHORT,
224                 )
225                 .show()
226         } catch (ex: Exception) {
227             Toast.makeText(this.requireContext(), ex.localizedMessage, Toast.LENGTH_SHORT).show()
228         }
229     }
230 
231     private fun seedAllDataButtonPressed() {
232         homeFragmentViewModel.seedAllDataViewModel(requireContext(), manager)
233     }
234 
235     private fun isPermissionGranted(permission: String): Boolean {
236         return ContextCompat.checkSelfPermission(this.requireContext(), permission) ==
237             PackageManager.PERMISSION_GRANTED
238     }
239 
240     private fun isMedicalPermissionMissing(): Boolean {
241         for (permission in MEDICAL_PERMISSIONS) {
242             if (!isPermissionGranted(permission)) {
243                 return true
244             }
245         }
246         return false
247     }
248 
249     private fun isDataTypePermissionMissing(): Boolean {
250         for (permission in FITNESS_PERMISSIONS) {
251             if (!isPermissionGranted(permission)) {
252                 return true
253             }
254         }
255         return false
256     }
257 
258     private fun isAdditionalPermissionMissing(): Boolean {
259         for (permission in ADDITIONAL_PERMISSIONS) {
260             if (!isPermissionGranted(permission)) {
261                 return true
262             }
263         }
264         return false
265     }
266 
267     private fun togglePermissionIntentFilter() {
268         val pm = requireActivity().applicationContext.packageManager
269         val packageName = requireActivity().packageName
270         val compName = ComponentName(packageName, "$packageName.AliasMainActivity")
271         val componentState = pm.getComponentEnabledSetting(compName)
272         var desiredState = COMPONENT_ENABLED_STATE_ENABLED
273         if (
274             componentState == COMPONENT_ENABLED_STATE_DEFAULT ||
275                 componentState == COMPONENT_ENABLED_STATE_ENABLED
276         ) {
277             desiredState = COMPONENT_ENABLED_STATE_DISABLED
278         }
279         pm.setComponentEnabledSetting(compName, desiredState, PackageManager.DONT_KILL_APP)
280 
281         val toastText =
282             if (desiredState == COMPONENT_ENABLED_STATE_ENABLED)
283                 R.string.toast_permission_filter_enabled
284             else R.string.toast_permission_filter_disabled
285 
286         Toast.makeText(this.requireContext(), toastText, Toast.LENGTH_SHORT).show()
287     }
288 
289     private fun requestHealthPermissions() {
290         mRequestPermissionLauncher.launch(HEALTH_PERMISSIONS)
291         return
292     }
293 
294     private fun requestDataTypePermissions() {
295         if (isDataTypePermissionMissing()) {
296             mRequestPermissionLauncher.launch(FITNESS_PERMISSIONS)
297             return
298         }
299         Toast.makeText(
300                 this.requireContext(),
301                 R.string.all_permissions_already_granted_toast,
302                 Toast.LENGTH_LONG,
303             )
304             .show()
305     }
306 
307     private fun requestAdditionalPermissions() {
308         if (isAdditionalPermissionMissing()) {
309             mRequestPermissionLauncher.launch(ADDITIONAL_PERMISSIONS)
310             return
311         }
312         Toast.makeText(
313                 this.requireContext(),
314                 R.string.all_permissions_already_granted_toast,
315                 Toast.LENGTH_LONG,
316             )
317             .show()
318     }
319 
320     private fun requestBgReadPermission() {
321         if (!isPermissionGranted(READ_HEALTH_DATA_IN_BACKGROUND)) {
322             mRequestPermissionLauncher.launch(arrayOf(READ_HEALTH_DATA_IN_BACKGROUND))
323         }
324 
325         Toast.makeText(
326                 this.requireContext(),
327                 R.string.all_permissions_already_granted_toast,
328                 Toast.LENGTH_LONG,
329             )
330             .show()
331     }
332 
333     private fun requestHistoryReadPermission() {
334         if (!isPermissionGranted(READ_HEALTH_DATA_HISTORY)) {
335             mRequestPermissionLauncher.launch(arrayOf(READ_HEALTH_DATA_HISTORY))
336         }
337 
338         Toast.makeText(
339                 this.requireContext(),
340                 R.string.all_permissions_already_granted_toast,
341                 Toast.LENGTH_LONG,
342             )
343             .show()
344     }
345 
346     private fun requestMedicalWritePermission() {
347         if (!isPermissionGranted(WRITE_ALL_MEDICAL_DATA)) {
348             mRequestPermissionLauncher.launch(arrayOf(WRITE_ALL_MEDICAL_DATA))
349         }
350 
351         Toast.makeText(
352                 this.requireContext(),
353                 R.string.all_permissions_already_granted_toast,
354                 Toast.LENGTH_LONG,
355             )
356             .show()
357     }
358 
359     private fun requestMedicalWriteAndOtherPermissions() {
360         if (
361             !isPermissionGranted(WRITE_ALL_MEDICAL_DATA) &&
362                 isDataTypePermissionMissing() &&
363                 isAdditionalPermissionMissing()
364         ) {
365             mRequestPermissionLauncher.launch(MEDICAL_WRITE_AND_OTHERS)
366         }
367         Toast.makeText(
368                 this.requireContext(),
369                 R.string.all_permissions_already_granted_toast,
370                 Toast.LENGTH_LONG,
371             )
372             .show()
373     }
374 
375     private fun requestMedicalReadPermissions() {
376         mRequestPermissionLauncher.launch(MEDICAL_READ_PERMISSIONS)
377     }
378 
379     private fun requestFitnessRead() {
380         mRequestPermissionLauncher.launch(FITNESS_READ_PERMISSIONS)
381     }
382 
383     private fun requestFitnessWrite() {
384         mRequestPermissionLauncher.launch(FITNESS_WRITE_PERMISSIONS)
385     }
386 
387     private fun goToRequestRoute() {
388         mNavigationController.navigate(R.id.action_homeFragment_to_routeRequest)
389     }
390 
391     private fun goToCategoryListPage() {
392         mNavigationController.navigate(R.id.action_homeFragment_to_categoryList)
393     }
394 
395     private fun goToReadDataInBackgroundPage() {
396         mNavigationController.navigate(R.id.action_homeFragment_to_readDataInBackground)
397     }
398 
399     private fun goToReadDataInForegroundPage() {
400         mNavigationController.navigate(R.id.action_homeFragment_to_readDataInForeground)
401     }
402 
403     private fun goToPhrOptions() {
404         mNavigationController.navigate(R.id.action_homeFragment_to_phrOptions)
405     }
406 }
407