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