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 @AndroidEntryPoint(Fragment::class) 55 class DeletionFragment : Hilt_DeletionFragment() { 56 57 private val viewModel: DeletionViewModel by activityViewModels() 58 59 override fun onCreate(savedInstanceState: Bundle?) { 60 super.onCreate(savedInstanceState) 61 62 // set event listeners 63 // start deletion 64 parentFragmentManager.setFragmentResultListener(START_DELETION_EVENT, this) { _, bundle -> 65 val deletionType = bundle.getParcelable(DELETION_TYPE, DeletionType::class.java) 66 val startTime = bundle.getParcelable(START_TIME, Instant::class.java) 67 val endTime = bundle.getParcelable(END_TIME, Instant::class.java) 68 viewModel.setDeletionType(deletionType!!) 69 if (startTime != null && endTime != null) { 70 viewModel.setStartTime(startTime) 71 viewModel.setEndTime(endTime) 72 } 73 showFirstDialog(deletionType, false) 74 } 75 76 parentFragmentManager.setFragmentResultListener(START_INACTIVE_APP_DELETION_EVENT, this) { 77 _, 78 bundle -> 79 val deletionType = bundle.getParcelable(DELETION_TYPE, DeletionType::class.java) 80 viewModel.setDeletionType(deletionType!!) 81 showFirstDialog(deletionType, true) 82 } 83 84 // time range selection 85 childFragmentManager.setFragmentResultListener(TIME_RANGE_SELECTION_EVENT, this) { _, _ -> 86 showConfirmationDialog() 87 } 88 89 // confirmation dialog 90 childFragmentManager.setFragmentResultListener(GO_BACK_EVENT, this) { _, _ -> 91 showTimeRagePickerDialog() 92 } 93 94 // deletion in progress 95 childFragmentManager.setFragmentResultListener(CONFIRMATION_EVENT, this) { _, _ -> 96 // start deletion from here which will trigger the progressDialog from observable 97 viewModel.delete() 98 } 99 100 // try again 101 childFragmentManager.setFragmentResultListener(TRY_AGAIN_EVENT, this) { _, _ -> 102 showConfirmationDialog() 103 } 104 } 105 106 override fun onCreateView( 107 inflater: LayoutInflater, 108 container: ViewGroup?, 109 savedInstanceState: Bundle? 110 ): View? { 111 return inflater.inflate(R.layout.fragment_deletion, container, false) 112 } 113 114 override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 115 super.onViewCreated(view, savedInstanceState) 116 117 viewModel.deletionParameters.observe(viewLifecycleOwner) { deletion -> 118 deletion?.let { render(deletion) } 119 } 120 } 121 122 private fun render(deletionParameters: DeletionParameters) { 123 when (deletionParameters.deletionState) { 124 DeletionState.STATE_NO_DELETION_IN_PROGRESS -> { 125 hideProgressDialog() 126 } 127 DeletionState.STATE_PROGRESS_INDICATOR_STARTED -> { 128 showProgressDialogFragment() 129 } 130 DeletionState.STATE_PROGRESS_INDICATOR_CAN_END -> { 131 hideProgressDialog() 132 } 133 DeletionState.STATE_DELETION_SUCCESSFUL -> { 134 showSuccessDialogFragment() 135 } 136 DeletionState.STATE_DELETION_FAILED -> { 137 showFailedDialogFragment() 138 } 139 else -> { 140 // do nothing 141 } 142 } 143 } 144 145 private fun showConfirmationDialog() { 146 if (viewModel.deletionParameters.value?.deletionType is DeletionType.DeletionTypeAppData && 147 viewModel.deletionParameters.value?.chosenRange == ChosenRange.DELETE_RANGE_ALL_DATA) { 148 showAppDeleteConfirmationDialog() 149 } else { 150 DeletionConfirmationDialogFragment() 151 .show(childFragmentManager, DeletionConfirmationDialogFragment.TAG) 152 } 153 } 154 155 private fun showAppDeleteConfirmationDialog() { 156 DeletionAppDataConfirmationDialogFragment() 157 .show(childFragmentManager, DeletionAppDataConfirmationDialogFragment.TAG) 158 } 159 160 private fun showTimeRagePickerDialog() { 161 TimeRangeDialogFragment().show(childFragmentManager, TimeRangeDialogFragment.TAG) 162 } 163 164 private fun showProgressDialogFragment() { 165 ProgressDialogFragment(titleRes = R.string.delete_progress_indicator) 166 .show(childFragmentManager, ProgressDialogFragment.TAG) 167 } 168 169 private fun showSuccessDialogFragment() { 170 hideProgressDialog() 171 SuccessDialogFragment().show(childFragmentManager, SuccessDialogFragment.TAG) 172 } 173 174 private fun showFailedDialogFragment() { 175 hideProgressDialog() 176 FailedDialogFragment().show(childFragmentManager, FailedDialogFragment.TAG) 177 } 178 179 private fun showFirstDialog(deletionType: DeletionType, isInactiveApp: Boolean) { 180 if (isInactiveApp) { 181 showAppDeleteConfirmationDialog() 182 } else { 183 when (deletionType) { 184 is DeletionType.DeleteDataEntry -> showConfirmationDialog() 185 else -> showTimeRagePickerDialog() 186 } 187 } 188 } 189 190 private fun hideProgressDialog() { 191 (childFragmentManager.findFragmentByTag(ProgressDialogFragment.TAG) 192 as ProgressDialogFragment?) 193 ?.dismiss() 194 } 195 } 196