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.permission.data.v33.PermissionDecision 39 import com.android.permissioncontroller.permission.ui.ManagePermissionsActivity 40 import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModel 41 import com.android.permissioncontroller.permission.ui.model.v33.ReviewPermissionDecisionsViewModelFactory 42 import com.android.permissioncontroller.permission.utils.KotlinUtils.getPackageUid 43 import com.android.permissioncontroller.permission.utils.Utils 44 import kotlin.math.min 45 46 /** Shows summary of recent permission decisions. */ 47 @SuppressLint("NewApi") 48 class AutoReviewPermissionDecisionsFragment : AutoSettingsFrameFragment() { 49 50 companion object { 51 const val EXTRA_SOURCE = "source" 52 const val EXTRA_SOURCE_NOTIFICATION = "notification" 53 private const val LOG_TAG = "AutoReviewPermissionDecisionsFragment" 54 private const val MAX_DECISIONS = 3 55 56 /** 57 * Creates a new instance of [AutoReviewPermissionDecisionsFragment]. 58 */ 59 fun newInstance( 60 sessionId: Long, 61 userHandle: UserHandle, 62 source: String? 63 ): AutoReviewPermissionDecisionsFragment { 64 return AutoReviewPermissionDecisionsFragment().apply { 65 arguments = 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 (requireArguments().containsKey(EXTRA_SOURCE) && 99 (requireArguments().getString(EXTRA_SOURCE) == EXTRA_SOURCE_NOTIFICATION)) { 100 logDecisionReminderNotificationClicked() 101 } 102 val factory = ReviewPermissionDecisionsViewModelFactory( 103 requireActivity().getApplication()!!, user) 104 viewModel = ViewModelProvider(this, 105 factory)[ReviewPermissionDecisionsViewModel::class.java] 106 107 addPrivacyDashboardPreference() 108 addPermissionManagerPreference() 109 preferenceScreen.addPreference(AutoDividerPreference(context)) 110 recentPermissionsGroup = PreferenceCategory(context!!).apply { 111 title = getString(R.string.review_permission_decisions) 112 } 113 preferenceScreen.addPreference(recentPermissionsGroup) 114 115 viewModel.recentPermissionDecisionsLiveData.observe(this) { recentDecisions -> 116 onRecentDecisionsChanged(recentDecisions) 117 } 118 headerLabel = getString(R.string.app_permissions) 119 120 logScreenViewed() 121 } 122 123 override fun onCreatePreferences(bundle: Bundle?, s: String?) { 124 preferenceScreen = preferenceManager.createPreferenceScreen(context!!) 125 } 126 127 private fun onRecentDecisionsChanged(recentDecisions: List<PermissionDecision>) { 128 recentPermissionsGroup.removeAll() 129 130 if (recentDecisions.isEmpty()) { 131 addNoRecentDecisionsPreference(recentPermissionsGroup) 132 } else { 133 addRecentDecisionPreferences(recentPermissionsGroup, recentDecisions) 134 } 135 if (recentDecisions.size > MAX_DECISIONS) { 136 addViewAllPreference(recentPermissionsGroup) 137 } 138 } 139 140 private fun addPrivacyDashboardPreference() { 141 val preference = CarUiPreference(context).apply { 142 title = getString(R.string.permission_usage_title) 143 summary = getString(R.string.auto_permission_usage_summary) 144 onPreferenceClickListener = Preference.OnPreferenceClickListener { _ -> 145 val intent = Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE).apply { 146 putExtra(Constants.EXTRA_SESSION_ID, sessionId) 147 } 148 startActivity(intent) 149 true 150 } 151 } 152 preferenceScreen.addPreference(preference) 153 } 154 155 private fun addPermissionManagerPreference() { 156 val preference = CarUiPreference(context).apply { 157 title = getString(R.string.app_permission_manager) 158 summary = getString(R.string.auto_permission_manager_summary) 159 onPreferenceClickListener = Preference.OnPreferenceClickListener { _ -> 160 val intent = Intent(Intent.ACTION_MANAGE_PERMISSIONS).apply { 161 putExtra(Intent.EXTRA_USER, user) 162 putExtra(ManagePermissionsActivity.EXTRA_CALLER_NAME, javaClass.name) 163 putExtra(Constants.EXTRA_SESSION_ID, sessionId) 164 } 165 startActivity(intent) 166 true 167 } 168 } 169 preferenceScreen.addPreference(preference) 170 } 171 172 private fun addRecentDecisionPreferences( 173 preferenceGroup: PreferenceGroup, 174 recentDecisions: List<PermissionDecision> 175 ) { 176 for (i in 0 until min(recentDecisions.size, MAX_DECISIONS)) { 177 val recentDecision = recentDecisions[i] 178 val decisionPreference = CarUiPreference(context).apply { 179 icon = viewModel.getAppIcon(recentDecision.packageName) 180 title = viewModel.createPreferenceTitle(recentDecision) 181 summary = viewModel.createSummaryText(recentDecision) 182 onPreferenceClickListener = Preference.OnPreferenceClickListener { 183 viewModel.createManageAppPermissionIntent(recentDecision).also { 184 startActivity(it) 185 } 186 logPermissionDecisionClicked(recentDecision.packageName, 187 recentDecision.permissionGroupName) 188 true 189 } 190 } 191 preferenceGroup.addPreference(decisionPreference) 192 } 193 } 194 195 private fun addViewAllPreference(preferenceGroup: PreferenceGroup) { 196 val viewAllIcon = requireContext().getDrawable(R.drawable.car_ic_apps) 197 val preference = CarUiPreference(context).apply { 198 icon = Utils.applyTint(context, viewAllIcon, android.R.attr.colorControlNormal) 199 title = getString(R.string.review_permission_decisions_view_all) 200 onPreferenceClickListener = Preference.OnPreferenceClickListener { 201 val frag = AutoReviewPermissionDecisionsViewAllFragment.newInstance(sessionId, 202 user) 203 getParentFragmentManager().beginTransaction() 204 .replace(android.R.id.content, frag) 205 .addToBackStack(null) 206 .commit() 207 logViewAllClicked() 208 true 209 } 210 } 211 preferenceGroup.addPreference(preference) 212 } 213 214 private fun addNoRecentDecisionsPreference(preferenceGroup: PreferenceGroup) { 215 val preference = CarUiPreference(context).apply { 216 title = getString(R.string.review_permission_decisions_empty) 217 } 218 preferenceGroup.addPreference(preference) 219 } 220 221 private fun logScreenViewed() { 222 PermissionControllerStatsLog.write( 223 PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED, 224 sessionId, 225 RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__SCREEN_VIEWED, 226 0, 227 null) 228 } 229 230 private fun logViewAllClicked() { 231 PermissionControllerStatsLog.write( 232 PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED, 233 sessionId, 234 RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__VIEW_ALL_CLICKED, 235 0, 236 null) 237 } 238 239 private fun logPermissionDecisionClicked(packageName: String, permissionGroupName: String) { 240 val uid = getPackageUid(requireActivity().getApplication(), packageName, user) ?: return 241 PermissionControllerStatsLog.write( 242 PermissionControllerStatsLog.RECENT_PERMISSION_DECISIONS_INTERACTED, 243 sessionId, 244 RECENT_PERMISSION_DECISIONS_INTERACTED__ACTION__REVIEW_DECISION, 245 uid, 246 permissionGroupName) 247 } 248 249 private fun logDecisionReminderNotificationClicked() { 250 PermissionControllerStatsLog.write( 251 PermissionControllerStatsLog.PERMISSION_REMINDER_NOTIFICATION_INTERACTED, 252 sessionId, PERMISSION_REMINDER_NOTIFICATION_INTERACTED__RESULT__NOTIFICATION_CLICKED) 253 } 254 } 255