• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.permissioncontroller.safetycenter.ui
18 
19 import android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE
20 import android.os.Bundle
21 import android.safetycenter.SafetyCenterEntryGroup
22 import android.util.Log
23 import androidx.annotation.RequiresApi
24 import androidx.lifecycle.ViewModelProvider
25 import androidx.preference.Preference
26 import androidx.preference.PreferenceGroup
27 import com.android.permissioncontroller.Constants.EXTRA_SESSION_ID
28 import com.android.permissioncontroller.R
29 import com.android.permissioncontroller.safetycenter.SafetyCenterConstants.PRIVACY_SOURCES_GROUP_ID
30 import com.android.permissioncontroller.safetycenter.ui.SafetyBrandChipPreference.Companion.closeSubpage
31 import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel
32 import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel.Pref
33 import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModel.PrefState
34 import com.android.permissioncontroller.safetycenter.ui.model.PrivacyControlsViewModelFactory
35 import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterUiData
36 import com.android.safetycenter.internaldata.SafetyCenterIds
37 
38 /** A fragment that represents the privacy subpage in Safety Center. */
39 @RequiresApi(UPSIDE_DOWN_CAKE)
40 class PrivacySubpageFragment : SafetyCenterFragment() {
41 
42     private lateinit var subpageBrandChip: SafetyBrandChipPreference
43     private lateinit var subpageIssueGroup: PreferenceGroup
44     private lateinit var subpageGenericEntryGroup: PreferenceGroup
45     private lateinit var subpageControlsExtraEntryGroup: PreferenceGroup
46     private lateinit var privacyControlsViewModel: PrivacyControlsViewModel
47 
48     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
49         super.onCreatePreferences(savedInstanceState, rootKey)
50         setPreferencesFromResource(R.xml.privacy_subpage, rootKey)
51 
52         subpageBrandChip = getPreferenceScreen().findPreference(BRAND_CHIP_KEY)!!
53         subpageIssueGroup = getPreferenceScreen().findPreference(ISSUE_GROUP_KEY)!!
54         subpageGenericEntryGroup = getPreferenceScreen().findPreference(GENERIC_ENTRY_GROUP_KEY)!!
55         subpageControlsExtraEntryGroup =
56             getPreferenceScreen().findPreference(CONTROLS_EXTRA_ENTRY_GROUP_KEY)!!
57         subpageBrandChip.setupListener(requireActivity(), safetyCenterSessionId)
58 
59         val factory = PrivacyControlsViewModelFactory(requireActivity().getApplication())
60         privacyControlsViewModel =
61             ViewModelProvider(this, factory).get(PrivacyControlsViewModel::class.java)
62         privacyControlsViewModel.controlStateLiveData.observe(this) {
63             prefStates: Map<Pref, PrefState> ->
64             renderPrivacyControls(prefStates)
65         }
66 
67         prerenderCurrentSafetyCenterData()
68     }
69 
70     override fun configureInteractionLogger() {
71         val logger = safetyCenterViewModel.interactionLogger
72         logger.sessionId = safetyCenterSessionId
73         logger.navigationSource = NavigationSource.fromIntent(requireActivity().getIntent())
74         logger.viewType = ViewType.SUBPAGE
75         logger.groupId = PRIVACY_SOURCES_GROUP_ID
76     }
77 
78     override fun onResume() {
79         super.onResume()
80         safetyCenterViewModel.pageOpen(PRIVACY_SOURCES_GROUP_ID)
81     }
82 
83     override fun renderSafetyCenterData(uiData: SafetyCenterUiData?) {
84         Log.d(TAG, "renderSafetyCenterEntryGroup called with $uiData")
85         val entryGroup = uiData?.getMatchingGroup(PRIVACY_SOURCES_GROUP_ID)
86         if (entryGroup == null) {
87             Log.w(
88                 TAG,
89                 "$PRIVACY_SOURCES_GROUP_ID doesn't match any of the existing SafetySourcesGroup IDs"
90             )
91             closeSubpage(requireActivity(), requireContext(), safetyCenterSessionId)
92             return
93         }
94 
95         requireActivity().setTitle(entryGroup.title)
96         updateSafetyCenterIssues(uiData)
97         updateSafetyCenterEntries(entryGroup)
98     }
99 
100     private fun updateSafetyCenterIssues(uiData: SafetyCenterUiData?) {
101         subpageIssueGroup.removeAll()
102         val subpageIssues = uiData?.getMatchingIssues(PRIVACY_SOURCES_GROUP_ID)
103         val subpageDismissedIssues = uiData?.getMatchingDismissedIssues(PRIVACY_SOURCES_GROUP_ID)
104         if (subpageIssues.isNullOrEmpty() && subpageDismissedIssues.isNullOrEmpty()) {
105             Log.w(TAG, "$PRIVACY_SOURCES_GROUP_ID doesn't have any matching SafetyCenterIssues")
106             return
107         }
108 
109         collapsableIssuesCardHelper.addIssues(
110             requireContext(),
111             safetyCenterViewModel,
112             getChildFragmentManager(),
113             subpageIssueGroup,
114             subpageIssues,
115             subpageDismissedIssues,
116             uiData.resolvedIssues,
117             requireActivity().getTaskId())
118     }
119 
120     private fun updateSafetyCenterEntries(entryGroup: SafetyCenterEntryGroup) {
121         Log.d(TAG, "updateSafetyCenterEntries called with $entryGroup")
122         subpageGenericEntryGroup.removeAll()
123         subpageControlsExtraEntryGroup.removeAll()
124 
125         for (entry in entryGroup.entries) {
126             val entryId = entry.id
127             val sourceId = SafetyCenterIds.entryIdFromString(entryId).getSafetySourceId()
128 
129             val subpageEntry =
130                 SafetySubpageEntryPreference(
131                     requireContext(),
132                     PendingIntentSender.getTaskIdForEntry(
133                         entryId, sameTaskSourceIds, requireActivity()),
134                     entry,
135                     safetyCenterViewModel)
136 
137             if (sourceId == "AndroidPrivacyControls") {
138                 // No action required here because the privacy controls are rendered separately
139                 // by this fragment as generic preferences.
140             } else if (sourceId.endsWith("ActivityControls")) {
141                 subpageControlsExtraEntryGroup.addPreference(subpageEntry)
142             } else {
143                 subpageGenericEntryGroup.addPreference(subpageEntry)
144             }
145         }
146     }
147 
148     private fun renderPrivacyControls(prefStates: Map<Pref, PrefState>) {
149         fun setSwitchPreference(prefType: Pref) {
150             val switchPreference: ClickableDisabledSwitchPreference? = findPreference(prefType.key)
151             switchPreference?.setupState(
152                 prefStates[prefType], prefType, privacyControlsViewModel, this)
153         }
154 
155         setSwitchPreference(Pref.MIC)
156         setSwitchPreference(Pref.CAMERA)
157         setSwitchPreference(Pref.CLIPBOARD)
158         setSwitchPreference(Pref.SHOW_PASSWORD)
159 
160         val locationEntry: Preference? = findPreference(Pref.LOCATION.key)
161         locationEntry?.setOnPreferenceClickListener {
162             privacyControlsViewModel.handlePrefClick(this, Pref.LOCATION, null)
163             true
164         }
165     }
166 
167     companion object {
168         private val TAG: String = PrivacySubpageFragment::class.java.simpleName
169         private const val BRAND_CHIP_KEY: String = "subpage_brand_chip"
170         private const val ISSUE_GROUP_KEY: String = "subpage_issue_group"
171         private const val GENERIC_ENTRY_GROUP_KEY: String = "subpage_generic_entry_group"
172         private const val CONTROLS_EXTRA_ENTRY_GROUP_KEY: String =
173             "subpage_controls_extra_entry_group"
174         /** Creates an instance of PrivacySubpageFragment with the arguments set */
175         @JvmStatic
176         fun newInstance(sessionId: Long): PrivacySubpageFragment {
177             val args = Bundle()
178             args.putLong(EXTRA_SESSION_ID, sessionId)
179 
180             val subpageFragment = PrivacySubpageFragment()
181             subpageFragment.setArguments(args)
182             return subpageFragment
183         }
184     }
185 }
186