1 /**
<lambda>null2  * Copyright (C) 2023 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  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package com.android.healthconnect.controller.permissions.shared
15 
16 import android.app.Dialog
17 import android.os.Bundle
18 import android.view.View
19 import android.widget.CheckBox
20 import android.widget.ImageView
21 import android.widget.TextView
22 import androidx.core.os.bundleOf
23 import androidx.core.view.isVisible
24 import androidx.fragment.app.DialogFragment
25 import androidx.fragment.app.activityViewModels
26 import androidx.fragment.app.setFragmentResult
27 import com.android.healthconnect.controller.R
28 import com.android.healthconnect.controller.permissions.additionalaccess.AdditionalAccessViewModel
29 import com.android.healthconnect.controller.shared.dialog.AlertDialogBuilder
30 import com.android.healthconnect.controller.utils.AttributeResolver
31 import com.android.healthconnect.controller.utils.logging.DisconnectAppDialogElement
32 import com.android.healthconnect.controller.utils.logging.HealthConnectLogger
33 import dagger.hilt.android.AndroidEntryPoint
34 import javax.inject.Inject
35 
36 /** A Dialog Fragment to get confirmation from user for disconnecting from Health Connect. */
37 @AndroidEntryPoint(DialogFragment::class)
38 class DisconnectDialogFragment constructor() : Hilt_DisconnectDialogFragment() {
39 
40     private val viewModel: AdditionalAccessViewModel by activityViewModels()
41 
42     constructor(appName: String, enableDeleteData: Boolean = true) : this() {
43         this.appName = appName
44         this.enableDeleteData = enableDeleteData
45     }
46 
47     companion object {
48         const val TAG = "DisconnectDialogFragment"
49         const val DISCONNECT_CANCELED_EVENT = "DISCONNECT_CANCELED_EVENT"
50         const val DISCONNECT_ALL_EVENT = "DISCONNECT_ALL_EVENT"
51         const val KEY_DELETE_DATA = "KEY_DELETE_DATA"
52         const val KEY_APP_NAME = "KEY_APP_NAME"
53         const val KEY_ENABLE_DELETE_DATA = "KEY_ENABLE_DELETE_DATA"
54         const val KEY_INCLUDE_BACKGROUND_READ = "KEY_INCLUDE_BACKGROUND_READ"
55         const val KEY_INCLUDE_HISTORY_READ = "KEY_INCLUDE_HISTORY_READ"
56     }
57 
58     lateinit var appName: String
59     private var enableDeleteData: Boolean = true
60     private var includeBackgroundRead: Boolean = false
61     private var includeHistoryRead: Boolean = false
62 
63     @Inject lateinit var logger: HealthConnectLogger
64 
65     override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
66         if (savedInstanceState != null) {
67             appName = savedInstanceState.getString(KEY_APP_NAME, "")
68             enableDeleteData = savedInstanceState.getBoolean(KEY_ENABLE_DELETE_DATA, true)
69             includeBackgroundRead =
70                 savedInstanceState.getBoolean(KEY_INCLUDE_BACKGROUND_READ, false)
71             includeHistoryRead = savedInstanceState.getBoolean(KEY_INCLUDE_HISTORY_READ, false)
72         }
73 
74         val additionalPermissionsState =
75             viewModel.additionalAccessState.value ?: AdditionalAccessViewModel.State()
76         includeHistoryRead = additionalPermissionsState.historyReadUIState.isDeclared
77         includeBackgroundRead = additionalPermissionsState.backgroundReadUIState.isDeclared
78 
79         val body = layoutInflater.inflate(R.layout.dialog_message_with_checkbox, null)
80         body.findViewById<TextView>(R.id.dialog_message).apply {
81             text =
82                 if (includeBackgroundRead && includeHistoryRead) {
83                     getString(R.string.permissions_disconnect_dialog_message_combined, appName)
84                 } else if (includeBackgroundRead) {
85                     getString(R.string.permissions_disconnect_dialog_message_background, appName)
86                 } else if (includeHistoryRead) {
87                     getString(R.string.permissions_disconnect_dialog_message_history, appName)
88                 } else {
89                     getString(R.string.permissions_disconnect_dialog_message, appName)
90                 }
91         }
92         body.findViewById<TextView>(R.id.dialog_title).apply {
93             text = getString(R.string.permissions_disconnect_dialog_title)
94         }
95         val iconView = body.findViewById(R.id.dialog_icon) as ImageView
96         val iconDrawable =
97             AttributeResolver.getNullableDrawable(body.context, R.attr.disconnectIcon)
98         iconDrawable?.let {
99             iconView.setImageDrawable(it)
100             iconView.visibility = View.VISIBLE
101         }
102         val checkBox =
103             body.findViewById<CheckBox>(R.id.dialog_checkbox).apply {
104                 text = getString(R.string.permissions_disconnect_dialog_checkbox, appName)
105                 isVisible = enableDeleteData
106             }
107         checkBox.setOnCheckedChangeListener { _, _ ->
108             logger.logInteraction(DisconnectAppDialogElement.DISCONNECT_APP_DIALOG_DELETE_CHECKBOX)
109         }
110 
111         val dialog =
112             AlertDialogBuilder(this, DisconnectAppDialogElement.DISCONNECT_APP_DIALOG_CONTAINER)
113                 .setView(body)
114                 .setNeutralButton(
115                     android.R.string.cancel,
116                     DisconnectAppDialogElement.DISCONNECT_APP_DIALOG_CANCEL_BUTTON) { _, _ ->
117                         setFragmentResult(DISCONNECT_CANCELED_EVENT, bundleOf())
118                     }
119                 .setPositiveButton(
120                     R.string.permissions_disconnect_dialog_disconnect,
121                     DisconnectAppDialogElement.DISCONNECT_APP_DIALOG_CONFIRM_BUTTON) { _, _ ->
122                         setFragmentResult(
123                             DISCONNECT_ALL_EVENT, bundleOf(KEY_DELETE_DATA to checkBox.isChecked))
124                     }
125                 .setAdditionalLogging {
126                     logger.logImpression(
127                         DisconnectAppDialogElement.DISCONNECT_APP_DIALOG_DELETE_CHECKBOX)
128                 }
129                 .create()
130         dialog.setCanceledOnTouchOutside(false)
131         return dialog
132     }
133 
134     override fun onSaveInstanceState(outState: Bundle) {
135         super.onSaveInstanceState(outState)
136         outState.putString(KEY_APP_NAME, appName)
137         outState.putBoolean(KEY_ENABLE_DELETE_DATA, enableDeleteData)
138         outState.putBoolean(KEY_INCLUDE_BACKGROUND_READ, includeBackgroundRead)
139         outState.putBoolean(KEY_INCLUDE_HISTORY_READ, includeHistoryRead)
140     }
141 }
142