1 /* <lambda>null2 * Copyright (C) 2021 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 @file:Suppress("DEPRECATION") 17 18 package com.android.permissioncontroller.permission.ui.auto 19 20 import android.annotation.SuppressLint 21 import android.content.Intent 22 import android.os.Bundle 23 import android.os.UserHandle 24 import androidx.lifecycle.ViewModelProvider 25 import androidx.preference.Preference 26 import androidx.preference.PreferenceCategory 27 import androidx.preference.PreferenceGroup 28 import com.android.car.ui.preference.CarUiPreference 29 import com.android.permissioncontroller.Constants 30 import com.android.permissioncontroller.DumpableLog 31 import com.android.permissioncontroller.PermissionControllerStatsLog 32 import com.android.permissioncontroller.PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED 33 import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION 34 import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED 35 import com.android.permissioncontroller.PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED 36 import com.android.permissioncontroller.R 37 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment 38 import com.android.permissioncontroller.auto.DrivingDecisionReminderService 39 import com.android.permissioncontroller.permission.data.v33.PermissionDecision 40 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity 41 import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModel 42 import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModelFactory 43 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid 44 import com.android.permissioncontroller.permission.utils.Utils 45 import kotlin.math.min 46 47 /** Shows summary of recent permission decisions. */ 48 @SuppressLint("NewApi") 49 class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { 50 51 companion object { 52 const val EXTRA_SOURCE = "source" 53 const val EXTRA_SOURCE_NOTIFICATION = "notification" 54 private const val LOG_TAG = "AutoReviewPermissionDecisionsFragment" 55 private const val MAX_DECISIONS = 3 56 57 /** Creates a new instance of [AutoReviewPermissionDecisionsFragment]. */ 58 fun newInstance( 59 sessionId: Long, 60 userHandle: UserHandle, 61 source: String? 62 ): AutoReviewPermissionDecisionsFragment { 63 return AutoReviewPermissionDecisionsFragment().apply { 64 arguments = 65 Bundle().apply { 66 putLong(Constants.EXTRA_SESSION_ID, sessionId) 67 putParcelable(Intent.EXTRA_USER, userHandle) 68 putString(EXTRA_SOURCE, source) 69 } 70 } 71 } 72 } 73 74 private lateinit var user: UserHandle 75 private lateinit var viewModel: ReviewPermissionDecisionsViewModel 76 private lateinit var recentPermissionsGroup: PreferenceCategory 77 private var sessionId: Long = Constants.INVALID_SESSION_ID 78 79 override fun onCreate(savedInstanceState: Bundle?) { 80 super.onCreate(savedInstanceState) 81 if (arguments == null) { 82 DumpableLog.e(LOG_TAG, "Missing arguments") 83 activity?.finish() 84 return 85 } 86 if (!requireArguments().containsKey(Intent.EXTRA_USER)) { 87 DumpableLog.e(LOG_TAG, "Missing argument ${Intent.EXTRA_USER}") 88 activity?.finish() 89 return 90 } 91 if (!requireArguments().containsKey(Constants.EXTRA_SESSION_ID)) { 92 DumpableLog.e(LOG_TAG, "Missing argument ${Constants.EXTRA_SESSION_ID}") 93 activity?.finish() 94 return 95 } 96 user = requireArguments().getParcelable<UserHandle>(Intent.EXTRA_USER)!! 97 sessionId = requireArguments().getLong(Constants.EXTRA_SESSION_ID) 98 if ( 99 requireArguments().containsKey(EXTRA_SOURCE) && 100 (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION) 101 ) { 102 DrivingDecisionReminderService.cancelNotification(requireActivity()) 103 logDecisionReminderNotificationClicked() 104 } 105 val factory = 106 ReviewPermissionDecisionsViewModelFactory(requireActivity().getApplication()!!, user) 107 viewModel = ViewModelProvider(this, factory)[ReviewPermissionDecisionsViewModel::class.java] 108 109 addPrivacyDashboardPreference() 110 addPermissionManagerPreference() 111 preferenceScreen.addPreference(AutoDividerPreference(context)) 112 recentPermissionsGroup = 113 PreferenceCategory(context!!).apply { 114 title = getString(R.string.review_permission_decisions) 115 } 116 preferenceScreen.addPreference(recentPermissionsGroup) 117 118 viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions -> 119 onRecentDecisionsChanged(recentDecisions) 120 } 121 headerLabel = getString(R.string.app_permissions) 122 123 logScreenViewed() 124 } 125 126 override fun onCreatePreferences(bundle: Bundle?, s: String?) { 127 preferenceScreen = preferenceManager.createPreferenceScreen(context!!) 128 } 129 130 private fun onRecentDecisionsChanged(recentDecisions: List<PermissionDecision>) { 131 recentPermissionsGroup.removeAll() 132 133 if (recentDecisions.isEmpty()) { 134 addNoRecentDecisionsPreference(recentPermissionsGroup) 135 } else { 136 addRecentDecisionPreferences(recentPermissionsGroup, recentDecisions) 137 } 138 if (recentDecisions.size > MAX_DECISIONS) { 139 addViewAllPreference(recentPermissionsGroup) 140 } 141 } 142 143 private fun addPrivacyDashboardPreference() { 144 val preference = 145 CarUiPreference(context).apply { 146 title = getString(R.string.permission_usage_title) 147 summary = getString(R.string.auto_permission_usage_summary) 148 onPreferenceClickListener = 149 Preference.OnPreferenceClickListener { _ -> 150 val intent = 151 Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply { 152 putExtra(Constants.EXTRA_SESSION_ID, sessionId) 153 } 154 startActivity(intent) 155 true 156 } 157 } 158 preferenceScreen.addPreference(preference) 159 } 160 161 private fun addPermissionManagerPreference() { 162 val preference = 163 CarUiPreference(context).apply { 164 title = getString(R.string.app_permission_manager) 165 summary = getString(R.string.auto_permission_manager_summary) 166 onPreferenceClickListener = 167 Preference.OnPreferenceClickListener { _ -> 168 val intent = 169 Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply { 170 putExtra(Intent.EXTRA_USER, user) 171 putExtra( 172 ManagePermissionsActivity.EXTRA_CALLER_NAME, 173 javaClass.name 174 ) 175 putExtra(Constants.EXTRA_SESSION_ID, sessionId) 176 } 177 startActivity(intent) 178 true 179 } 180 } 181 preferenceScreen.addPreference(preference) 182 } 183 184 private fun addRecentDecisionPreferences( 185 preferenceGroup: PreferenceGroup, 186 recentDecisions: List<PermissionDecision> 187 ) { 188 for (i in 0 until min(recentDecisions.size, MAX_DECISIONS)) { 189 val recentDecision = recentDecisions[i] 190 val decisionPreference = 191 CarUiPreference(context).apply { 192 icon = viewModel.getAppIcon(recentDecision.packageName) 193 title = viewModel.createPreferenceTitle(recentDecision) 194 summary = viewModel.createSummaryText(recentDecision) 195 onPreferenceClickListener = 196 Preference.OnPreferenceClickListener { 197 viewModel.createManageAppPermissionIntent(recentDecision).also { 198 startActivity(it) 199 } 200 logPermissionDecisionClicked( 201 recentDecision.packageName, 202 recentDecision.permissionGroupName 203 ) 204 true 205 } 206 } 207 preferenceGroup.addPreference(decisionPreference) 208 } 209 } 210 211 private fun addViewAllPreference(preferenceGroup: PreferenceGroup) { 212 val viewAllIcon = requireContext().getDrawable(R.drawable.car_ic_apps) 213 val preference = 214 CarUiPreference(context).apply { 215 icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal) 216 title = getString(R.string.review_permission_decisions_view_all) 217 onPreferenceClickListener = 218 Preference.OnPreferenceClickListener { 219 val frag = 220 AutoReviewPermissionDecisionsViewAllFragment.newInstance( 221 sessionId, 222 user 223 ) 224 getParentFragmentManager() 225 .beginTransaction() 226 .replace(android.R.id.content, frag) 227 .addToBackStack(null) 228 .commit() 229 logViewAllClicked() 230 true 231 } 232 } 233 preferenceGroup.addPreference(preference) 234 } 235 236 private fun addNoRecentDecisionsPreference(preferenceGroup: PreferenceGroup) { 237 val preference = 238 CarUiPreference(context).apply { 239 title = getString(R.string.review_permission_decisions_empty) 240 } 241 preferenceGroup.addPreference(preference) 242 } 243 244 private fun logScreenViewed() { 245 PermissionControllerStatsLog.write( 246 PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED, 247 sessionId, 248 RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED, 249 0, 250 null 251 ) 252 } 253 254 private fun logViewAllClicked() { 255 PermissionControllerStatsLog.write( 256 PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED, 257 sessionId, 258 RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED, 259 0, 260 null 261 ) 262 } 263 264 private fun logPermissionDecisionClicked(packageName: String, permissionGroupName: String) { 265 val uid = getPackageUid(requireActivity().getApplication(), packageName, user) ?: return 266 PermissionControllerStatsLog.write( 267 PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED, 268 sessionId, 269 RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION, 270 uid, 271 permissionGroupName 272 ) 273 } 274 275 private fun logDecisionReminderNotificationClicked() { 276 PermissionControllerStatsLog.write( 277 PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED, 278 sessionId, 279 PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED 280 ) 281 } 282 } 283