1 /* 2 * 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 17 package com.android.car.settings.privacy; 18 19 import android.car.drivingstate.CarUxRestrictions; 20 import android.content.Context; 21 import android.hardware.SensorPrivacyManager; 22 23 import androidx.preference.PreferenceCategory; 24 25 import com.android.car.settings.R; 26 import com.android.car.settings.common.FragmentController; 27 import com.android.car.settings.common.PreferenceController; 28 import com.android.car.ui.preference.CarUiPreference; 29 import com.android.internal.annotations.VisibleForTesting; 30 import com.android.settingslib.applications.RecentAppOpsAccess; 31 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Set; 35 36 /** 37 * This controller displays a list of apps that recently access the microphone. Only non-system apps 38 * are displayed. 39 */ 40 public class MicrophoneRecentAccessesPreferenceController extends 41 PreferenceController<PreferenceCategory> { 42 43 private final SensorPrivacyManager mSensorPrivacyManager; 44 private final SensorPrivacyManager.OnSensorPrivacyChangedListener mListener = 45 (sensor, enabled) -> refreshUi(); 46 private final Set<CarUiPreference> mAddedPreferences = new HashSet<>(); 47 48 private final RecentAppOpsAccess mRecentMicrophoneAccesses; 49 private final int mRecentAppsMaxCount; 50 MicrophoneRecentAccessesPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)51 public MicrophoneRecentAccessesPreferenceController(Context context, String preferenceKey, 52 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 53 this(context, preferenceKey, fragmentController, uxRestrictions, 54 RecentAppOpsAccess.createForMicrophone(context), 55 context.getResources() 56 .getInteger(R.integer.recent_microphone_access_apps_list_count), 57 SensorPrivacyManager.getInstance(context)); 58 } 59 60 @VisibleForTesting MicrophoneRecentAccessesPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions, RecentAppOpsAccess recentMicrophoneAccesses, int recentAppsMaxCount, SensorPrivacyManager sensorPrivacyManager)61 MicrophoneRecentAccessesPreferenceController(Context context, String preferenceKey, 62 FragmentController fragmentController, CarUxRestrictions uxRestrictions, 63 RecentAppOpsAccess recentMicrophoneAccesses, int recentAppsMaxCount, 64 SensorPrivacyManager sensorPrivacyManager) { 65 super(context, preferenceKey, fragmentController, uxRestrictions); 66 mRecentMicrophoneAccesses = recentMicrophoneAccesses; 67 mRecentAppsMaxCount = recentAppsMaxCount; 68 mSensorPrivacyManager = sensorPrivacyManager; 69 } 70 71 @Override getPreferenceType()72 protected Class<PreferenceCategory> getPreferenceType() { 73 return PreferenceCategory.class; 74 } 75 76 @Override onStartInternal()77 protected void onStartInternal() { 78 mSensorPrivacyManager.addSensorPrivacyListener( 79 SensorPrivacyManager.Sensors.MICROPHONE, mListener); 80 } 81 82 @Override onStopInternal()83 protected void onStopInternal() { 84 mSensorPrivacyManager.removeSensorPrivacyListener(SensorPrivacyManager.Sensors.MICROPHONE, 85 mListener); 86 } 87 88 @Override updateState(PreferenceCategory preference)89 public void updateState(PreferenceCategory preference) { 90 super.updateState(preference); 91 if (mSensorPrivacyManager.isSensorPrivacyEnabled( 92 SensorPrivacyManager.Sensors.MICROPHONE)) { 93 getPreference().setVisible(false); 94 return; 95 } 96 getPreference().setVisible(true); 97 List<RecentAppOpsAccess.Access> sortedRecentMicrophoneAccesses = loadData(); 98 updateUi(sortedRecentMicrophoneAccesses); 99 } 100 loadData()101 private List<RecentAppOpsAccess.Access> loadData() { 102 return mRecentMicrophoneAccesses.getAppListSorted(/* showSystem= */ false); 103 } 104 hasAtLeastOneRecentAppAccess()105 private boolean hasAtLeastOneRecentAppAccess() { 106 return !mRecentMicrophoneAccesses.getAppListSorted(/* showSystem= */ true).isEmpty(); 107 } 108 updateUi(List<RecentAppOpsAccess.Access> sortedRecentMicrophoneAccesses)109 private void updateUi(List<RecentAppOpsAccess.Access> sortedRecentMicrophoneAccesses) { 110 // remove any already added preferences 111 for (CarUiPreference addedPreference : mAddedPreferences) { 112 getPreference().removePreference(addedPreference); 113 } 114 mAddedPreferences.clear(); 115 116 if (sortedRecentMicrophoneAccesses.isEmpty()) { 117 CarUiPreference emptyPreference = createNoRecentAccessPreference(); 118 getPreference().addPreference(emptyPreference); 119 mAddedPreferences.add(emptyPreference); 120 } else { 121 int count = Math.min(sortedRecentMicrophoneAccesses.size(), mRecentAppsMaxCount); 122 for (int i = 0; i < count; i++) { 123 RecentAppOpsAccess.Access request = sortedRecentMicrophoneAccesses.get(i); 124 CarUiPreference appPreference = MicrophoneRecentAccessUtil.createAppPreference( 125 getContext(), 126 request); 127 getPreference().addPreference(appPreference); 128 mAddedPreferences.add(appPreference); 129 } 130 } 131 132 if (hasAtLeastOneRecentAppAccess()) { 133 CarUiPreference viewAllPreference = createViewAllPreference(); 134 getPreference().addPreference(viewAllPreference); 135 mAddedPreferences.add(viewAllPreference); 136 } 137 } 138 createNoRecentAccessPreference()139 private CarUiPreference createNoRecentAccessPreference() { 140 CarUiPreference preference = new CarUiPreference(getContext()); 141 preference.setTitle(R.string.microphone_no_recent_access); 142 preference.setSelectable(false); 143 return preference; 144 } 145 createViewAllPreference()146 private CarUiPreference createViewAllPreference() { 147 CarUiPreference preference = new CarUiPreference(getContext()); 148 preference.setTitle(R.string.microphone_settings_recent_requests_view_all_title); 149 preference.setIcon(R.drawable.ic_apps); 150 preference.setOnPreferenceClickListener(p -> { 151 getFragmentController().launchFragment(new MicrophoneRecentAccessViewAllFragment()); 152 return true; 153 }); 154 return preference; 155 } 156 } 157