1 /* <lambda>null2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.healthconnect.controller.backuprestore 18 19 import android.os.Bundle 20 import android.view.View 21 import android.widget.Toast 22 import androidx.fragment.app.viewModels 23 import androidx.navigation.fragment.findNavController 24 import androidx.preference.PreferenceGroup 25 import com.android.healthconnect.controller.R 26 import com.android.healthconnect.controller.exportimport.ExportStatusPreference 27 import com.android.healthconnect.controller.exportimport.api.ExportFrequency 28 import com.android.healthconnect.controller.exportimport.api.ExportSettings 29 import com.android.healthconnect.controller.exportimport.api.ExportSettingsViewModel 30 import com.android.healthconnect.controller.exportimport.api.ExportStatusViewModel 31 import com.android.healthconnect.controller.exportimport.api.ImportStatusViewModel 32 import com.android.healthconnect.controller.exportimport.api.ImportUiState 33 import com.android.healthconnect.controller.exportimport.api.ImportUiStatus 34 import com.android.healthconnect.controller.exportimport.api.ScheduledExportUiState 35 import com.android.healthconnect.controller.exportimport.api.ScheduledExportUiStatus 36 import com.android.healthconnect.controller.shared.preference.BannerPreference 37 import com.android.healthconnect.controller.shared.preference.HealthPreference 38 import com.android.healthconnect.controller.shared.preference.HealthPreferenceFragment 39 import com.android.healthconnect.controller.utils.AttributeResolver 40 import com.android.healthconnect.controller.utils.DeviceInfoUtilsImpl 41 import com.android.healthconnect.controller.utils.LocalDateTimeFormatter 42 import com.android.healthconnect.controller.utils.logging.ErrorPageElement 43 import com.android.healthconnect.controller.utils.pref 44 import com.android.settingslib.widget.FooterPreference 45 import dagger.hilt.android.AndroidEntryPoint 46 47 /** Fragment displaying backup and restore settings. */ 48 @AndroidEntryPoint(HealthPreferenceFragment::class) 49 class BackupAndRestoreSettingsFragment : Hilt_BackupAndRestoreSettingsFragment() { 50 // TODO: b/330169060 - Add proper logging for the backup and restore settings fragment. 51 52 companion object { 53 const val SCHEDULED_EXPORT_PREFERENCE_KEY = "scheduled_export" 54 const val IMPORT_DATA_PREFERENCE_KEY = "import_data" 55 const val EXPORT_IMPORT_SETTINGS_CATEGORY_PREFERENCE_KEY = "settings_category" 56 const val IMPORT_ERROR_BANNER_KEY = "import_error_banner" 57 const val IMPORT_ERROR_BANNER_ORDER = 0 58 const val PREVIOUS_EXPORT_STATUS_ORDER = 2 59 } 60 61 private val exportSettingsViewModel: ExportSettingsViewModel by viewModels() 62 private val exportStatusViewModel: ExportStatusViewModel by viewModels() 63 private val importStatusViewModel: ImportStatusViewModel by viewModels() 64 65 private val scheduledExportPreference: HealthPreference? by lazy { 66 preferenceScreen.findPreference(SCHEDULED_EXPORT_PREFERENCE_KEY) 67 } 68 69 private val importDataPreference: HealthPreference? by lazy { 70 preferenceScreen.findPreference(IMPORT_DATA_PREFERENCE_KEY) 71 } 72 73 private val settingsCategory: PreferenceGroup? by lazy { 74 preferenceScreen.findPreference(EXPORT_IMPORT_SETTINGS_CATEGORY_PREFERENCE_KEY) 75 } 76 77 private val dateFormatter: LocalDateTimeFormatter by lazy { 78 LocalDateTimeFormatter(requireContext()) 79 } 80 81 private val footerPreference: FooterPreference by pref("backup_restore_footer") 82 83 override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { 84 super.onCreatePreferences(savedInstanceState, rootKey) 85 setPreferencesFromResource(R.xml.backup_and_restore_settings_screen, rootKey) 86 87 footerPreference.setLearnMoreText(getString(R.string.backup_and_restore_footer_link_text)) 88 footerPreference.setLearnMoreAction { 89 DeviceInfoUtilsImpl().openHCGetStartedLink(requireActivity()) 90 } 91 92 scheduledExportPreference?.setOnPreferenceClickListener { 93 findNavController() 94 .navigate(R.id.action_backupAndRestoreSettingsFragment_to_exportSetupActivity) 95 true 96 } 97 98 importDataPreference?.setOnPreferenceClickListener { 99 findNavController() 100 .navigate(R.id.action_backupAndRestoreSettingsFragment_to_importFlowActivity) 101 true 102 } 103 } 104 105 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 106 super.onViewCreated(view, savedInstanceState) 107 108 importStatusViewModel.storedImportStatus.observe(viewLifecycleOwner) { importUiStatus -> 109 when (importUiStatus) { 110 is ImportUiStatus.WithData -> { 111 maybeShowImportErrorBanner(importUiStatus.importUiState) 112 } 113 else -> { 114 // Do nothing. 115 } 116 } 117 } 118 119 exportStatusViewModel.storedScheduledExportStatus.observe(viewLifecycleOwner) { 120 scheduledExportUiStatus -> 121 when (scheduledExportUiStatus) { 122 is ScheduledExportUiStatus.WithData -> { 123 maybeShowPreviousExportStatus(scheduledExportUiStatus.scheduledExportUiState) 124 } 125 else -> { 126 // do nothing 127 } 128 } 129 } 130 131 exportSettingsViewModel.storedExportSettings.observe(viewLifecycleOwner) { exportSettings -> 132 when (exportSettings) { 133 is ExportSettings.WithData -> { 134 val frequency = exportSettings.frequency 135 if (frequency == ExportFrequency.EXPORT_FREQUENCY_NEVER) { 136 scheduledExportPreference?.setOnPreferenceClickListener { 137 findNavController() 138 .navigate( 139 R.id 140 .action_backupAndRestoreSettingsFragment_to_exportSetupActivity) 141 true 142 } 143 } else { 144 scheduledExportPreference?.setOnPreferenceClickListener { 145 findNavController() 146 .navigate( 147 R.id 148 .action_backupAndRestoreSettingsFragment_to_scheduledExportFragment) 149 true 150 } 151 } 152 scheduledExportPreference?.summary = buildSummary(frequency) 153 } 154 is ExportSettings.LoadingFailed -> 155 Toast.makeText(activity, R.string.default_error, Toast.LENGTH_LONG).show() 156 else -> {} 157 } 158 } 159 } 160 161 override fun onResume() { 162 super.onResume() 163 exportSettingsViewModel.loadExportSettings() 164 } 165 166 private fun buildSummary(frequency: ExportFrequency): String { 167 val on = getString(R.string.automatic_export_on) 168 val automaticExportStatusId = R.string.automatic_export_status 169 return when (frequency) { 170 ExportFrequency.EXPORT_FREQUENCY_NEVER -> getString(R.string.automatic_export_off) 171 ExportFrequency.EXPORT_FREQUENCY_DAILY -> 172 getString(automaticExportStatusId, on, getString(R.string.frequency_daily)) 173 ExportFrequency.EXPORT_FREQUENCY_WEEKLY -> 174 getString(automaticExportStatusId, on, getString(R.string.frequency_weekly)) 175 ExportFrequency.EXPORT_FREQUENCY_MONTHLY -> 176 getString(automaticExportStatusId, on, getString(R.string.frequency_monthly)) 177 } 178 } 179 180 private fun maybeShowPreviousExportStatus(scheduledExportUiState: ScheduledExportUiState) { 181 val lastSuccessfulExportTime = scheduledExportUiState.lastSuccessfulExportTime 182 if (lastSuccessfulExportTime != null) { 183 val lastExportTime = 184 getString( 185 R.string.last_export_time, 186 dateFormatter.formatLongDate(lastSuccessfulExportTime)) 187 settingsCategory?.addPreference( 188 ExportStatusPreference(requireContext(), lastExportTime).also { 189 it.order = PREVIOUS_EXPORT_STATUS_ORDER 190 }) 191 } 192 } 193 194 private fun maybeShowImportErrorBanner(importUiState: ImportUiState) { 195 if (importUiState.dataImportError == 196 ImportUiState.DataImportError.DATA_IMPORT_ERROR_WRONG_FILE) { 197 preferenceScreen.addPreference( 198 BannerPreference(requireContext(), ErrorPageElement.UNKNOWN_ELEMENT).also { 199 it.setPrimaryButton( 200 getString(R.string.import_wrong_file_error_banner_button), 201 ErrorPageElement.UNKNOWN_ELEMENT) 202 it.title = getString(R.string.import_wrong_file_error_banner_title) 203 it.key = IMPORT_ERROR_BANNER_KEY 204 it.summary = getString(R.string.import_wrong_file_error_banner_summary) 205 it.icon = 206 AttributeResolver.getNullableDrawable(requireContext(), R.attr.warningIcon) 207 it.setPrimaryButtonOnClickListener { 208 findNavController() 209 .navigate( 210 R.id.action_backupAndRestoreSettingsFragment_to_importFlowActivity) 211 } 212 it.order = IMPORT_ERROR_BANNER_ORDER 213 }) 214 } 215 } 216 } 217