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