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.DATA_TYPE_PERMISSIONS
40 import com.android.healthconnect.testapps.toolbox.Constants.HEALTH_PERMISSIONS
41 import com.android.healthconnect.testapps.toolbox.Constants.MEDICAL_PERMISSIONS
42 import com.android.healthconnect.testapps.toolbox.Constants.READ_HEALTH_DATA_HISTORY
43 import com.android.healthconnect.testapps.toolbox.Constants.READ_HEALTH_DATA_IN_BACKGROUND
44 import com.android.healthconnect.testapps.toolbox.PerformanceTestingFragment
45 import com.android.healthconnect.testapps.toolbox.R
46 import com.android.healthconnect.testapps.toolbox.seed.SeedData
47 import com.android.healthconnect.testapps.toolbox.viewmodels.PerformanceTestingViewModel
48 import kotlin.system.exitProcess
49 
50 /** Home fragment for Health Connect Toolbox. */
51 class HomeFragment : Fragment() {
52 
53     override fun onCreateView(
54         inflater: LayoutInflater,
55         container: ViewGroup?,
56         savedInstanceState: Bundle?,
57     ): View? {
58         return inflater.inflate(R.layout.fragment_home, container, false)
59     }
60 
61     private lateinit var mRequestPermissionLauncher: ActivityResultLauncher<Array<String>>
62     private lateinit var mNavigationController: NavController
63     private val performanceTestingViewModel: PerformanceTestingViewModel by viewModels()
64 
65     private val manager by lazy {
66         requireContext().getSystemService(HealthConnectManager::class.java)
67     }
68 
69     override fun onCreate(savedInstanceState: Bundle?) {
70         super.onCreate(savedInstanceState)
71 
72         // Starting API Level 30 If permission is denied more than once, user doesn't see the dialog
73         // asking permissions again unless they grant the permission from settings.
74         mRequestPermissionLauncher =
75             registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) {
76                 permissionMap: Map<String, Boolean> ->
77                 requestPermissionResultHandler(permissionMap)
78             }
79     }
80 
81     private fun requestPermissionResultHandler(permissionMap: Map<String, Boolean>) {
82         var numberOfPermissionsMissing = HEALTH_PERMISSIONS.size
83         for (value in permissionMap.values) {
84             if (value) {
85                 numberOfPermissionsMissing--
86             }
87         }
88 
89         if (numberOfPermissionsMissing == 0) {
90             Toast.makeText(
91                     this.requireContext(), R.string.all_permissions_success, Toast.LENGTH_SHORT)
92                 .show()
93         } else {
94             Toast.makeText(
95                     this.requireContext(),
96                     getString(
97                         R.string.number_of_permissions_not_granted, numberOfPermissionsMissing),
98                     Toast.LENGTH_SHORT)
99                 .show()
100         }
101     }
102 
103     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
104         super.onViewCreated(view, savedInstanceState)
105         val performanceTesting = PerformanceTestingFragment()
106         childFragmentManager
107             .beginTransaction()
108             .add(performanceTesting, "PERFORMANCE_TESTING_FRAGMENT")
109             .commit()
110         view.findViewById<Button>(R.id.launch_health_connect_button).setOnClickListener {
111             launchHealthConnect()
112         }
113         view.findViewById<Button>(R.id.request_data_type_permissions_button).setOnClickListener {
114             requestDataTypePermissions()
115         }
116         view.findViewById<Button>(R.id.request_route_button).setOnClickListener {
117             goToRequestRoute()
118         }
119         view.findViewById<Button>(R.id.insert_update_data_button).setOnClickListener {
120             goToCategoryListPage()
121         }
122         view.findViewById<Button>(R.id.seed_random_data_button).setOnClickListener {
123             seedDataButtonPressed()
124         }
125         view.findViewById<Button>(R.id.seed_performance_read_data_button).setOnClickListener {
126             performanceTestingViewModel.beginReadingData()
127         }
128         view.findViewById<Button>(R.id.seed_performance_insert_data_button).setOnClickListener {
129             performanceTestingViewModel.beginInsertingData(false)
130         }
131         view.findViewById<Button>(R.id.toggle_permission_intent_filter).setOnClickListener {
132             togglePermissionIntentFilter()
133         }
134         view.requireViewById<Button>(R.id.read_data_in_background_button).setOnClickListener {
135             goToReadDataInBackgroundPage()
136         }
137         view.requireViewById<Button>(R.id.read_data_in_foreground_button).setOnClickListener {
138             goToReadDataInForegroundPage()
139         }
140         view.findViewById<Button>(R.id.phr_options_button).setOnClickListener { goToPhrOptions() }
141         view.requireViewById<Button>(R.id.exit_process_button).setOnClickListener {
142             exitProcess(status = 0)
143         }
144 
145         view.findViewById<Button>(R.id.request_health_permissions).setOnClickListener {
146             requestHealthPermissions()
147         }
148 
149         view.findViewById<Button>(R.id.request_additional_permissions).setOnClickListener {
150             requestAdditionalPermissions()
151         }
152 
153         view.findViewById<Button>(R.id.request_bg_read_permission).setOnClickListener {
154             requestBgReadPermission()
155         }
156 
157         view.findViewById<Button>(R.id.request_history_read_permission).setOnClickListener {
158             requestHistoryReadPermission()
159         }
160 
161         // view
162         //     .findViewById<Button>(R.id.seed_performance_insert_data_button_in_parallel)
163         //     .setOnClickListener { performanceTestingViewModel.beginInsertingData(true) }
164         mNavigationController = findNavController()
165     }
166 
167     private fun launchHealthConnect() {
168         val intent = Intent("android.health.connect.action.HEALTH_HOME_SETTINGS")
169         intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
170         startActivity(intent)
171     }
172 
173     private fun seedDataButtonPressed() {
174         try {
175             SeedData(requireContext(), manager).seedData()
176             Toast.makeText(
177                     this.requireContext(), R.string.toast_seed_data_success, Toast.LENGTH_SHORT)
178                 .show()
179         } catch (ex: Exception) {
180             Toast.makeText(this.requireContext(), ex.localizedMessage, Toast.LENGTH_SHORT).show()
181         }
182     }
183 
184     private fun isPermissionGranted(permission: String): Boolean {
185         return ContextCompat.checkSelfPermission(this.requireContext(), permission) ==
186             PackageManager.PERMISSION_GRANTED
187     }
188 
189     private fun isMedicalPermissionMissing(): Boolean {
190         for (permission in MEDICAL_PERMISSIONS) {
191             if (!isPermissionGranted(permission)) {
192                 return true
193             }
194         }
195         return false
196     }
197 
198     private fun isDataTypePermissionMissing(): Boolean {
199         for (permission in DATA_TYPE_PERMISSIONS) {
200             if (!isPermissionGranted(permission)) {
201                 return true
202             }
203         }
204         return false
205     }
206 
207     private fun isAdditionalPermissionMissing(): Boolean {
208         for (permission in ADDITIONAL_PERMISSIONS) {
209             if (!isPermissionGranted(permission)) {
210                 return true
211             }
212         }
213         return false
214     }
215 
216     private fun togglePermissionIntentFilter() {
217         val pm = requireActivity().applicationContext.packageManager
218         val packageName = requireActivity().packageName
219         val compName = ComponentName(packageName, "$packageName.AliasMainActivity")
220         val componentState = pm.getComponentEnabledSetting(compName)
221         var desiredState = COMPONENT_ENABLED_STATE_ENABLED
222         if (componentState == COMPONENT_ENABLED_STATE_DEFAULT ||
223             componentState == COMPONENT_ENABLED_STATE_ENABLED) {
224             desiredState = COMPONENT_ENABLED_STATE_DISABLED
225         }
226         pm.setComponentEnabledSetting(compName, desiredState, PackageManager.DONT_KILL_APP)
227 
228         val toastText =
229             if (desiredState == COMPONENT_ENABLED_STATE_ENABLED)
230                 R.string.toast_permission_filter_enabled
231             else R.string.toast_permission_filter_disabled
232 
233         Toast.makeText(this.requireContext(), toastText, Toast.LENGTH_SHORT).show()
234     }
235 
236     private fun requestHealthPermissions() {
237         mRequestPermissionLauncher.launch(HEALTH_PERMISSIONS)
238         return
239     }
240 
241     private fun requestDataTypePermissions() {
242         if (isDataTypePermissionMissing()) {
243             mRequestPermissionLauncher.launch(DATA_TYPE_PERMISSIONS)
244             return
245         }
246         Toast.makeText(
247                 this.requireContext(),
248                 R.string.all_permissions_already_granted_toast,
249                 Toast.LENGTH_LONG)
250             .show()
251     }
252 
253     private fun requestAdditionalPermissions() {
254         if (isAdditionalPermissionMissing()) {
255             mRequestPermissionLauncher.launch(ADDITIONAL_PERMISSIONS)
256             return
257         }
258         Toast.makeText(
259                 this.requireContext(),
260                 R.string.all_permissions_already_granted_toast,
261                 Toast.LENGTH_LONG)
262             .show()
263     }
264 
265     private fun requestBgReadPermission() {
266         if (!isPermissionGranted(READ_HEALTH_DATA_IN_BACKGROUND)) {
267             mRequestPermissionLauncher.launch(arrayOf(READ_HEALTH_DATA_IN_BACKGROUND))
268         }
269 
270         Toast.makeText(
271                 this.requireContext(),
272                 R.string.all_permissions_already_granted_toast,
273                 Toast.LENGTH_LONG)
274             .show()
275     }
276 
277     private fun requestHistoryReadPermission() {
278         if (!isPermissionGranted(READ_HEALTH_DATA_HISTORY)) {
279             mRequestPermissionLauncher.launch(arrayOf(READ_HEALTH_DATA_HISTORY))
280         }
281 
282         Toast.makeText(
283                 this.requireContext(),
284                 R.string.all_permissions_already_granted_toast,
285                 Toast.LENGTH_LONG)
286             .show()
287     }
288 
289     private fun goToRequestRoute() {
290         mNavigationController.navigate(R.id.action_homeFragment_to_routeRequest)
291     }
292 
293     private fun goToCategoryListPage() {
294         mNavigationController.navigate(R.id.action_homeFragment_to_categoryList)
295     }
296 
297     private fun goToReadDataInBackgroundPage() {
298         mNavigationController.navigate(R.id.action_homeFragment_to_readDataInBackground)
299     }
300 
301     private fun goToReadDataInForegroundPage() {
302         mNavigationController.navigate(R.id.action_homeFragment_to_readDataInForeground)
303     }
304 
305     private fun goToPhrOptions() {
306         mNavigationController.navigate(R.id.action_homeFragment_to_phrOptions)
307     }
308 }
309