1 /*
<lambda>null2  * Copyright (C) 2023 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.controller.data.appdata
17 
18 import android.content.Intent.EXTRA_PACKAGE_NAME
19 import android.os.Bundle
20 import android.view.View
21 import androidx.core.os.bundleOf
22 import androidx.fragment.app.viewModels
23 import androidx.navigation.fragment.findNavController
24 import androidx.preference.PreferenceCategory
25 import com.android.healthconnect.controller.R
26 import com.android.healthconnect.controller.permissions.data.FitnessPermissionStrings
27 import com.android.healthconnect.controller.shared.Constants
28 import com.android.healthconnect.controller.shared.HealthDataCategoryExtensions.icon
29 import com.android.healthconnect.controller.shared.HealthDataCategoryExtensions.uppercaseTitle
30 import com.android.healthconnect.controller.shared.preference.HealthPreference
31 import com.android.healthconnect.controller.shared.preference.HealthPreferenceFragment
32 import com.android.healthconnect.controller.shared.preference.NoDataPreference
33 import com.android.settingslib.widget.AppHeaderPreference
34 import com.android.settingslib.widget.FooterPreference
35 import dagger.hilt.android.AndroidEntryPoint
36 
37 /** Fragment to display data in Health Connect written by a given app. */
38 @AndroidEntryPoint(HealthPreferenceFragment::class)
39 open class AppDataFragment : Hilt_AppDataFragment() {
40 
41     companion object {
42         private const val TAG = "AppDataFragmentTag"
43         const val PERMISSION_TYPE_KEY = "permission_type_key"
44     }
45 
46     init {
47         // TODO(b/281811925):
48         // this.setPageName(PageName.APP_DATA_PAGE)
49     }
50 
51     private var packageName: String = ""
52     private var appName: String = ""
53 
54     private val viewModel: AppDataViewModel by viewModels()
55 
56     private lateinit var header: AppHeaderPreference
57 
58     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
59         super.onCreatePreferences(savedInstanceState, rootKey)
60         setPreferencesFromResource(R.xml.app_data_screen, rootKey)
61 
62         if (requireArguments().containsKey(EXTRA_PACKAGE_NAME) &&
63             requireArguments().getString(EXTRA_PACKAGE_NAME) != null) {
64             packageName = requireArguments().getString(EXTRA_PACKAGE_NAME)!!
65         }
66         if (requireArguments().containsKey(Constants.EXTRA_APP_NAME) &&
67             requireArguments().getString(Constants.EXTRA_APP_NAME) != null) {
68             appName = requireArguments().getString(Constants.EXTRA_APP_NAME)!!
69         }
70     }
71 
72     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
73         super.onViewCreated(view, savedInstanceState)
74 
75         viewModel.loadAppInfo(packageName)
76         viewModel.loadAppData(packageName)
77 
78         header = AppHeaderPreference(requireContext())
79         viewModel.appInfo.observe(viewLifecycleOwner) { appMetadata ->
80             header.apply {
81                 icon = appMetadata.icon
82                 title = appMetadata.appName
83             }
84         }
85 
86         viewModel.appData.observe(viewLifecycleOwner) { state ->
87             when (state) {
88                 is AppDataViewModel.AppDataState.Loading -> {
89                     setLoading(isLoading = true)
90                 }
91                 is AppDataViewModel.AppDataState.Error -> {
92                     setError(hasError = true)
93                 }
94                 is AppDataViewModel.AppDataState.WithData -> {
95                     setLoading(isLoading = false)
96                     setError(hasError = false)
97                     updatePreferenceScreen(state.dataMap)
98                 }
99             }
100         }
101     }
102 
103     private fun updatePreferenceScreen(
104         permissionTypesPerCategoryList: List<PermissionTypesPerCategory>
105     ) {
106         preferenceScreen?.removeAll()
107         preferenceScreen.addPreference(header)
108 
109         val populatedCategories =
110             permissionTypesPerCategoryList
111                 .filter { it.data.isNotEmpty() }
112                 .sortedBy { getString(it.category.uppercaseTitle()) }
113 
114         if (populatedCategories.isEmpty()) {
115             preferenceScreen.addPreference(NoDataPreference(requireContext()))
116             preferenceScreen.addPreference(
117                 FooterPreference(requireContext()).also { it.setTitle(R.string.no_data_footer) })
118             return
119         }
120 
121         populatedCategories.forEach { permissionTypesPerCategory ->
122             val category = permissionTypesPerCategory.category
123             val categoryIcon = category.icon(requireContext())
124 
125             val preferenceCategory =
126                 PreferenceCategory(requireContext()).also { it.setTitle(category.uppercaseTitle()) }
127             preferenceScreen.addPreference(preferenceCategory)
128 
129             permissionTypesPerCategory.data
130                 .sortedBy {
131                     getString(FitnessPermissionStrings.fromPermissionType(it).uppercaseLabel)
132                 }
133                 .forEach { permissionType ->
134                     preferenceCategory.addPreference(
135                         HealthPreference(requireContext()).also {
136                             it.icon = categoryIcon
137                             it.setTitle(
138                                 FitnessPermissionStrings.fromPermissionType(permissionType)
139                                     .uppercaseLabel)
140                             it.setOnPreferenceClickListener {
141                                 // TODO(b/281811925): Add in upcoming cl.
142                                 // it.logName = AppDataElement.PERMISSION_TYPE_BUTTON
143                                 findNavController()
144                                     .navigate(
145                                         R.id.action_appData_to_appEntries,
146                                         bundleOf(
147                                             EXTRA_PACKAGE_NAME to packageName,
148                                             Constants.EXTRA_APP_NAME to appName,
149                                             PERMISSION_TYPE_KEY to permissionType))
150                                 true
151                             }
152                         })
153                 }
154         }
155     }
156 }
157