/** * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * ``` * http://www.apache.org/licenses/LICENSE-2.0 * ``` * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.android.healthconnect.controller.deletion import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import com.android.healthconnect.controller.R import com.android.healthconnect.controller.deletion.DeletionConstants.CONFIRMATION_EVENT import com.android.healthconnect.controller.deletion.DeletionConstants.DELETION_TYPE import com.android.healthconnect.controller.deletion.DeletionConstants.END_TIME import com.android.healthconnect.controller.deletion.DeletionConstants.GO_BACK_EVENT import com.android.healthconnect.controller.deletion.DeletionConstants.START_DELETION_EVENT import com.android.healthconnect.controller.deletion.DeletionConstants.START_INACTIVE_APP_DELETION_EVENT import com.android.healthconnect.controller.deletion.DeletionConstants.START_TIME import com.android.healthconnect.controller.deletion.DeletionConstants.TIME_RANGE_SELECTION_EVENT import com.android.healthconnect.controller.deletion.DeletionConstants.TRY_AGAIN_EVENT import com.android.healthconnect.controller.shared.dialog.ProgressDialogFragment import dagger.hilt.android.AndroidEntryPoint import java.time.Instant /** * Invisible fragment that handles every deletion flow with the deletion dialogs. * *

This fragment needs to be added to every page that performs deletion. Then the deletion flow * can be started via {@link StartDeletionEvent}. * *

It can be added to the parent fragment without attaching to a view via the following snippet: *

 if (childFragmentManager.findFragmentByTag(FRAGMENT_TAG_DELETION) == null) {
 * ```
 *      childFragmentManager
 *          .commitNow {
 *              add({@link DeletionFragment}(), FRAGMENT_TAG_DELETION)
 *          }
 * ```
 * } 
*/ @Deprecated("This won't be used once the NEW_INFORMATION_ARCHITECTURE feature is enabled.") @AndroidEntryPoint(Fragment::class) class DeletionFragment : Hilt_DeletionFragment() { private val viewModel: DeletionViewModel by activityViewModels() private var progressDialogFragment: ProgressDialogFragment? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // set event listeners // start deletion parentFragmentManager.setFragmentResultListener(START_DELETION_EVENT, this) { _, bundle -> val deletionType = bundle.getParcelable(DELETION_TYPE, DeletionType::class.java) val startTime = bundle.getParcelable(START_TIME, Instant::class.java) val endTime = bundle.getParcelable(END_TIME, Instant::class.java) viewModel.setDeletionType(deletionType!!) if (startTime != null && endTime != null) { viewModel.setStartTime(startTime) viewModel.setEndTime(endTime) } showFirstDialog(deletionType, false) } parentFragmentManager.setFragmentResultListener(START_INACTIVE_APP_DELETION_EVENT, this) { _, bundle -> val deletionType = bundle.getParcelable(DELETION_TYPE, DeletionType::class.java) viewModel.setDeletionType(deletionType!!) showFirstDialog(deletionType, true) } // time range selection childFragmentManager.setFragmentResultListener(TIME_RANGE_SELECTION_EVENT, this) { _, _ -> showConfirmationDialog() } // confirmation dialog childFragmentManager.setFragmentResultListener(GO_BACK_EVENT, this) { _, _ -> showTimeRagePickerDialog() } // deletion in progress childFragmentManager.setFragmentResultListener(CONFIRMATION_EVENT, this) { _, _ -> // start deletion from here which will trigger the progressDialog from observable viewModel.delete() } // try again childFragmentManager.setFragmentResultListener(TRY_AGAIN_EVENT, this) { _, _ -> showConfirmationDialog() } } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_deletion, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewModel.deletionParameters.observe(viewLifecycleOwner) { deletion -> deletion?.let { render(deletion) } } } private fun render(deletionParameters: DeletionParameters) { when (deletionParameters.deletionState) { DeletionState.STATE_NO_DELETION_IN_PROGRESS -> { hideProgressDialog() } DeletionState.STATE_PROGRESS_INDICATOR_STARTED -> { showProgressDialogFragment() } DeletionState.STATE_PROGRESS_INDICATOR_CAN_END -> { hideProgressDialog() } DeletionState.STATE_DELETION_SUCCESSFUL -> { showSuccessDialogFragment() } DeletionState.STATE_DELETION_FAILED -> { showFailedDialogFragment() } else -> { // do nothing } } } private fun showConfirmationDialog() { if (viewModel.deletionParameters.value?.deletionType is DeletionType.DeletionTypeAppData && viewModel.deletionParameters.value?.chosenRange == ChosenRange.DELETE_RANGE_ALL_DATA) { showAppDeleteConfirmationDialog() } else { DeletionConfirmationDialogFragment() .show(childFragmentManager, DeletionConfirmationDialogFragment.TAG) } } private fun showAppDeleteConfirmationDialog(isInactiveApp: Boolean = false) { viewModel.isInactiveApp = isInactiveApp DeletionAppDataConfirmationDialogFragment() .show(childFragmentManager, DeletionAppDataConfirmationDialogFragment.TAG) } private fun showTimeRagePickerDialog() { TimeRangeDialogFragment().show(childFragmentManager, TimeRangeDialogFragment.TAG) } private fun showProgressDialogFragment() { if (progressDialogFragment == null) { progressDialogFragment = ProgressDialogFragment(titleRes = R.string.delete_progress_indicator) } progressDialogFragment?.show(childFragmentManager, ProgressDialogFragment.TAG) } private fun showSuccessDialogFragment() { hideProgressDialog() SuccessDialogFragment().show(childFragmentManager, SuccessDialogFragment.TAG) } private fun showFailedDialogFragment() { hideProgressDialog() FailedDialogFragment().show(childFragmentManager, FailedDialogFragment.TAG) } private fun showFirstDialog(deletionType: DeletionType, isInactiveApp: Boolean) { if (isInactiveApp) { showAppDeleteConfirmationDialog(isInactiveApp) } else { when (deletionType) { is DeletionType.DeleteDataEntry -> showConfirmationDialog() else -> showTimeRagePickerDialog() } } } private fun hideProgressDialog() { if (progressDialogFragment != null) { progressDialogFragment?.dismiss() } } }