• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.HealthConnectException
25 import android.health.connect.HealthConnectManager
26 import android.health.connect.HealthPermissions
27 import android.health.connect.datatypes.ExerciseSessionRecord
28 import android.health.connect.datatypes.ExerciseSessionType
29 import android.os.Bundle
30 import android.view.LayoutInflater
31 import android.view.View
32 import android.view.ViewGroup
33 import android.widget.Button
34 import android.widget.Toast
35 import androidx.activity.result.ActivityResultLauncher
36 import androidx.activity.result.contract.ActivityResultContracts
37 import androidx.core.content.ContextCompat
38 import androidx.fragment.app.Fragment
39 import androidx.fragment.app.viewModels
40 import androidx.navigation.NavController
41 import androidx.navigation.fragment.findNavController
42 import com.android.healthconnect.testapps.toolbox.Constants.ALL_PERMISSIONS
43 import com.android.healthconnect.testapps.toolbox.PerformanceTesting
44 import com.android.healthconnect.testapps.toolbox.R
45 import com.android.healthconnect.testapps.toolbox.data.ExerciseRoutesTestData.Companion.WARSAW_ROUTE
46 import com.android.healthconnect.testapps.toolbox.data.ExerciseRoutesTestData.Companion.generateExerciseRouteFromLocations
47 import com.android.healthconnect.testapps.toolbox.seed.SeedData
48 import com.android.healthconnect.testapps.toolbox.utils.GeneralUtils
49 import com.android.healthconnect.testapps.toolbox.viewmodels.PerformanceTestingViewModel
50 import kotlinx.coroutines.runBlocking
51 import java.time.Instant
52 import java.time.temporal.ChronoUnit
53 
54 
55 /** Home fragment for Health Connect Toolbox. */
56 class HomeFragment : Fragment() {
57 
58     override fun onCreateView(
59             inflater: LayoutInflater,
60             container: ViewGroup?,
61             savedInstanceState: Bundle?,
62     ): View? {
63         return inflater.inflate(R.layout.fragment_home, container, false)
64     }
65 
66     private lateinit var mRequestPermissionLauncher: ActivityResultLauncher<Array<String>>
67     private lateinit var mRequestRoutePermissionLauncher: ActivityResultLauncher<String>
68     private lateinit var mNavigationController: NavController
69     private val manager by lazy {
70         requireContext().getSystemService(HealthConnectManager::class.java)
71     }
72     private val performanceTestingViewModel: PerformanceTestingViewModel by viewModels()
73 
74     override fun onCreate(savedInstanceState: Bundle?) {
75         super.onCreate(savedInstanceState)
76 
77         // Starting API Level 30 If permission is denied more than once, user doesn't see the dialog
78         // asking permissions again unless they grant the permission from settings.
79         mRequestPermissionLauncher =
80                 registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissionMap: Map<String, Boolean> ->
81                     requestPermissionResultHandler(permissionMap)
82                 }
83         mRequestRoutePermissionLauncher =
84                 registerForActivityResult(ActivityResultContracts.RequestPermission()) { granted ->
85                     if (granted) {
86                         readRoute()
87                     }
88                 }
89     }
90 
91     private fun requestPermissionResultHandler(permissionMap: Map<String, Boolean>) {
92         var numberOfPermissionsMissing = ALL_PERMISSIONS.size
93         for (value in permissionMap.values) {
94             if (value) {
95                 numberOfPermissionsMissing--
96             }
97         }
98 
99         if (numberOfPermissionsMissing == 0) {
100             Toast.makeText(
101                     this.requireContext(), R.string.all_permissions_success, Toast.LENGTH_SHORT)
102                     .show()
103         } else {
104             Toast.makeText(
105                     this.requireContext(),
106                     getString(
107                             R.string.number_of_permissions_not_granted, numberOfPermissionsMissing),
108                     Toast.LENGTH_SHORT)
109                     .show()
110         }
111     }
112 
113     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
114         super.onViewCreated(view, savedInstanceState)
115         val performanceTesting = PerformanceTesting(performanceTestingViewModel)
116         childFragmentManager
117                 .beginTransaction()
118                 .add(performanceTesting, "PERFORMANCE_TESTING_FRAGMENT")
119                 .commit()
120         view.findViewById<Button>(R.id.launch_health_connect_button).setOnClickListener {
121             launchHealthConnect()
122         }
123         view.findViewById<Button>(R.id.request_permissions_button).setOnClickListener {
124             requestPermissions()
125         }
126         view.findViewById<Button>(R.id.request_route_permissions_button).setOnClickListener {
127             requestRoutesPermissions()
128         }
129         view.findViewById<Button>(R.id.insert_update_data_button).setOnClickListener {
130             goToCategoryListPage()
131         }
132         view.findViewById<Button>(R.id.seed_random_data_button).setOnClickListener {
133             seedDataButtonPressed()
134         }
135         view.findViewById<Button>(R.id.seed_performance_read_data_button).setOnClickListener {
136             performanceTestingViewModel.beginReadingData()
137         }
138         view.findViewById<Button>(R.id.seed_performance_insert_data_button).setOnClickListener {
139             performanceTestingViewModel.beginInsertingData(false)
140         }
141 
142         view.findViewById<Button>(R.id.toggle_permission_intent_filter).setOnClickListener {
143             togglePermissionIntentFilter()
144         }
145 
146         // view
147         //     .findViewById<Button>(R.id.seed_performance_insert_data_button_in_parallel)
148         //     .setOnClickListener { performanceTestingViewModel.beginInsertingData(true) }
149         mNavigationController = findNavController()
150     }
151 
152   private fun launchHealthConnect() {
153     val intent = Intent("android.health.connect.action.HEALTH_HOME_SETTINGS")
154     intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
155     startActivity(intent)
156   }
157 
158   private fun seedDataButtonPressed() {
159         try {
160           SeedData(requireContext(), manager).seedData()
161           Toast.makeText(this.requireContext(), R.string.toast_seed_data_success, Toast.LENGTH_SHORT).show()
162         } catch (ex: Exception) {
163           Toast.makeText(this.requireContext(), ex.localizedMessage, Toast.LENGTH_SHORT).show()
164         }
165     }
166 
167     private fun isPermissionMissing(): Boolean {
168         for (permission in ALL_PERMISSIONS) {
169             if (ContextCompat.checkSelfPermission(this.requireContext(), permission) !=
170                     PackageManager.PERMISSION_GRANTED) {
171                 return true
172             }
173         }
174         return false
175     }
176 
177     private fun togglePermissionIntentFilter() {
178         val pm = requireActivity().applicationContext.packageManager
179         val packageName = requireActivity().packageName
180         val compName = ComponentName(packageName, "$packageName.AliasMainActivity")
181         val componentState = pm.getComponentEnabledSetting(compName)
182         var desiredState = COMPONENT_ENABLED_STATE_ENABLED
183         if (componentState == COMPONENT_ENABLED_STATE_DEFAULT || componentState == COMPONENT_ENABLED_STATE_ENABLED) {
184             desiredState = COMPONENT_ENABLED_STATE_DISABLED
185         }
186         pm.setComponentEnabledSetting(
187                 compName,
188                 desiredState,
189                 PackageManager.DONT_KILL_APP)
190 
191         val toastText = if (desiredState == COMPONENT_ENABLED_STATE_ENABLED) R.string.toast_permission_filter_enabled else R.string.toast_permission_filter_disabled
192 
193         Toast.makeText(this.requireContext(), toastText, Toast.LENGTH_SHORT).show()
194 
195     }
196 
197     private fun requestPermissions() {
198         if (isPermissionMissing()) {
199             mRequestPermissionLauncher.launch(ALL_PERMISSIONS)
200             return
201         }
202         Toast.makeText(
203                 this.requireContext(),
204                 R.string.all_permissions_already_granted_toast,
205                 Toast.LENGTH_LONG)
206                 .show()
207     }
208 
209     private fun requestRoutesPermissions() {
210         if (ContextCompat.checkSelfPermission(
211                         requireContext(), HealthPermissions.WRITE_EXERCISE_ROUTE) !=
212                 PackageManager.PERMISSION_GRANTED) {
213             mRequestRoutePermissionLauncher.launch(HealthPermissions.WRITE_EXERCISE_ROUTE)
214             return
215         }
216         readRoute()
217     }
218 
219     private fun readRoute() {
220         // insert a route data
221         val start = Instant.now().truncatedTo(ChronoUnit.DAYS)
222         val end = start.plusSeconds(100_000)
223         val route =
224                 ExerciseSessionRecord.Builder(
225                         GeneralUtils.getMetaData(requireContext()),
226                         start,
227                         end,
228                         ExerciseSessionType.EXERCISE_SESSION_TYPE_RUNNING)
229                         .setRoute(generateExerciseRouteFromLocations(WARSAW_ROUTE, start.toEpochMilli()))
230                         .build()
231         runBlocking {
232             val result = GeneralUtils.insertRecords(listOf(route), manager)
233             if (result.isNotEmpty()) {
234                 val record = result.first()
235                 val intent =
236                         Intent(HealthConnectManager.ACTION_REQUEST_EXERCISE_ROUTE).apply {
237                             putExtra(HealthConnectManager.EXTRA_SESSION_ID, record.metadata.id)
238                             putExtra(Intent.EXTRA_PACKAGE_NAME, requireContext().packageName)
239                         }
240                 startActivityForResult(intent, 1)
241             }
242         }
243     }
244 
245     private fun goToCategoryListPage() {
246         mNavigationController.navigate(R.id.action_homeFragment_to_categoryList)
247     }
248 }
249