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.controller.deletion 17 18 import android.os.Bundle 19 import android.view.LayoutInflater 20 import android.view.View 21 import android.view.ViewGroup 22 import androidx.fragment.app.Fragment 23 import androidx.fragment.app.activityViewModels 24 import com.android.healthconnect.controller.R 25 import com.android.healthconnect.controller.deletion.DeletionConstants.CONFIRMATION_EVENT 26 import com.android.healthconnect.controller.deletion.DeletionConstants.DELETION_TYPE 27 import com.android.healthconnect.controller.deletion.DeletionConstants.END_TIME 28 import com.android.healthconnect.controller.deletion.DeletionConstants.GO_BACK_EVENT 29 import com.android.healthconnect.controller.deletion.DeletionConstants.START_DELETION_EVENT 30 import com.android.healthconnect.controller.deletion.DeletionConstants.START_INACTIVE_APP_DELETION_EVENT 31 import com.android.healthconnect.controller.deletion.DeletionConstants.START_TIME 32 import com.android.healthconnect.controller.deletion.DeletionConstants.TIME_RANGE_SELECTION_EVENT 33 import com.android.healthconnect.controller.deletion.DeletionConstants.TRY_AGAIN_EVENT 34 import com.android.healthconnect.controller.shared.dialog.ProgressDialogFragment 35 import dagger.hilt.android.AndroidEntryPoint 36 import java.time.Instant 37 38 /** 39 * Invisible fragment that handles every deletion flow with the deletion dialogs. 40 * 41 * <p>This fragment needs to be added to every page that performs deletion. Then the deletion flow 42 * can be started via {@link StartDeletionEvent}. 43 * 44 * <p>It can be added to the parent fragment without attaching to a view via the following snippet: 45 * <pre> if (childFragmentManager.findFragmentByTag(FRAGMENT_TAG_DELETION) == null) { 46 * ``` 47 * childFragmentManager 48 * .commitNow { 49 * add({@link DeletionFragment}(), FRAGMENT_TAG_DELETION) 50 * } 51 * ``` 52 * } </pre> 53 */ 54 @Deprecated("This won't be used once the NEW_INFORMATION_ARCHITECTURE feature is enabled.") 55 @AndroidEntryPoint(Fragment::class) 56 class DeletionFragment : Hilt_DeletionFragment() { 57 58 private val viewModel: DeletionViewModel by activityViewModels() 59 private var progressDialogFragment: ProgressDialogFragment? = null 60 61 override fun onCreate(savedInstanceState: Bundle?) { 62 super.onCreate(savedInstanceState) 63 64 // set event listeners 65 // start deletion 66 parentFragmentManager.setFragmentResultListener(START_DELETION_EVENT, this) { _, bundle -> 67 val deletionType = bundle.getParcelable(DELETION_TYPE, DeletionType::class.java) 68 val startTime = bundle.getParcelable(START_TIME, Instant::class.java) 69 val endTime = bundle.getParcelable(END_TIME, Instant::class.java) 70 viewModel.setDeletionType(deletionType!!) 71 if (startTime != null && endTime != null) { 72 viewModel.setStartTime(startTime) 73 viewModel.setEndTime(endTime) 74 } 75 showFirstDialog(deletionType, false) 76 } 77 78 parentFragmentManager.setFragmentResultListener(START_INACTIVE_APP_DELETION_EVENT, this) { 79 _, 80 bundle -> 81 val deletionType = bundle.getParcelable(DELETION_TYPE, DeletionType::class.java) 82 viewModel.setDeletionType(deletionType!!) 83 showFirstDialog(deletionType, true) 84 } 85 86 // time range selection 87 childFragmentManager.setFragmentResultListener(TIME_RANGE_SELECTION_EVENT, this) { _, _ -> 88 showConfirmationDialog() 89 } 90 91 // confirmation dialog 92 childFragmentManager.setFragmentResultListener(GO_BACK_EVENT, this) { _, _ -> 93 showTimeRagePickerDialog() 94 } 95 96 // deletion in progress 97 childFragmentManager.setFragmentResultListener(CONFIRMATION_EVENT, this) { _, _ -> 98 // start deletion from here which will trigger the progressDialog from observable 99 viewModel.delete() 100 } 101 102 // try again 103 childFragmentManager.setFragmentResultListener(TRY_AGAIN_EVENT, this) { _, _ -> 104 showConfirmationDialog() 105 } 106 } 107 108 override fun onCreateView( 109 inflater: LayoutInflater, 110 container: ViewGroup?, 111 savedInstanceState: Bundle? 112 ): View? { 113 return inflater.inflate(R.layout.fragment_deletion, container, false) 114 } 115 116 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 117 super.onViewCreated(view, savedInstanceState) 118 119 viewModel.deletionParameters.observe(viewLifecycleOwner) { deletion -> 120 deletion?.let { render(deletion) } 121 } 122 } 123 124 private fun render(deletionParameters: DeletionParameters) { 125 when (deletionParameters.deletionState) { 126 DeletionState.STATE_NO_DELETION_IN_PROGRESS -> { 127 hideProgressDialog() 128 } 129 DeletionState.STATE_PROGRESS_INDICATOR_STARTED -> { 130 showProgressDialogFragment() 131 } 132 DeletionState.STATE_PROGRESS_INDICATOR_CAN_END -> { 133 hideProgressDialog() 134 } 135 DeletionState.STATE_DELETION_SUCCESSFUL -> { 136 showSuccessDialogFragment() 137 } 138 DeletionState.STATE_DELETION_FAILED -> { 139 showFailedDialogFragment() 140 } 141 else -> { 142 // do nothing 143 } 144 } 145 } 146 147 private fun showConfirmationDialog() { 148 if (viewModel.deletionParameters.value?.deletionType is DeletionType.DeletionTypeAppData && 149 viewModel.deletionParameters.value?.chosenRange == ChosenRange.DELETE_RANGE_ALL_DATA) { 150 showAppDeleteConfirmationDialog() 151 } else { 152 DeletionConfirmationDialogFragment() 153 .show(childFragmentManager, DeletionConfirmationDialogFragment.TAG) 154 } 155 } 156 157 private fun showAppDeleteConfirmationDialog(isInactiveApp: Boolean = false) { 158 viewModel.isInactiveApp = isInactiveApp 159 DeletionAppDataConfirmationDialogFragment() 160 .show(childFragmentManager, DeletionAppDataConfirmationDialogFragment.TAG) 161 } 162 163 private fun showTimeRagePickerDialog() { 164 TimeRangeDialogFragment().show(childFragmentManager, TimeRangeDialogFragment.TAG) 165 } 166 167 private fun showProgressDialogFragment() { 168 if (progressDialogFragment == null) { 169 progressDialogFragment = 170 ProgressDialogFragment(titleRes = R.string.delete_progress_indicator) 171 } 172 progressDialogFragment?.show(childFragmentManager, ProgressDialogFragment.TAG) 173 } 174 175 private fun showSuccessDialogFragment() { 176 hideProgressDialog() 177 SuccessDialogFragment().show(childFragmentManager, SuccessDialogFragment.TAG) 178 } 179 180 private fun showFailedDialogFragment() { 181 hideProgressDialog() 182 FailedDialogFragment().show(childFragmentManager, FailedDialogFragment.TAG) 183 } 184 185 private fun showFirstDialog(deletionType: DeletionType, isInactiveApp: Boolean) { 186 if (isInactiveApp) { 187 showAppDeleteConfirmationDialog(isInactiveApp) 188 } else { 189 when (deletionType) { 190 is DeletionType.DeleteDataEntry -> showConfirmationDialog() 191 else -> showTimeRagePickerDialog() 192 } 193 } 194 } 195 196 private fun hideProgressDialog() { 197 if (progressDialogFragment != null) { 198 progressDialogFragment?.dismiss() 199 } 200 } 201 } 202