diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-07-25 15:48:22 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-07-25 15:48:22 +0000 |
commit | cf49f66e16d9dcbf4fbecc6917a31156c86dc178 (patch) | |
tree | 960ccb8b68e9fd72b883444bca15e94250fd6ff1 | |
parent | bced3294893b3ad108b9b4b33f37cdabf9873772 (diff) | |
parent | 8e9c95fb2e77f922f6499e64b8803823daae9f30 (diff) | |
download | Settings-android13-mainline-go-os-statsd-release.tar.gz |
Snap for 8866753 from 8e9c95fb2e77f922f6499e64b8803823daae9f30 to mainline-go-os-statsd-releaseaml_go_sta_330911000android13-mainline-go-os-statsd-release
Change-Id: I8e3025a3723b4947628f1a67603b24c3cfe38d38
21 files changed, 1005 insertions, 557 deletions
diff --git a/res/values/integers.xml b/res/values/integers.xml index 436948a83..0467d3bc5 100644 --- a/res/values/integers.xml +++ b/res/values/integers.xml @@ -61,6 +61,9 @@ <!-- Maximum number of apps to be shown in RecentNotificationsAppsPreferenceController --> <integer name="recent_notifications_apps_list_count">3</integer> + <!-- Maximum number of apps to be shown in LocationRecentAccessesPreferenceController --> + <integer name="recent_location_access_apps_list_count">2</integer> + <!-- Maximum number of apps to be shown in MicrophoneRecentAccessesPreferenceController --> <integer name="recent_microphone_access_apps_list_count">2</integer> diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml index b1ff8e7e8..e4f2a48c7 100644 --- a/res/values/overlayable.xml +++ b/res/values/overlayable.xml @@ -543,6 +543,7 @@ REGENERATE USING packages/apps/Car/libs/tools/rro/generate-overlayable.py <item type="integer" name="recent_apps_days_threshold"/> <item type="integer" name="recent_apps_max_count"/> <item type="integer" name="recent_camera_access_apps_list_count"/> + <item type="integer" name="recent_location_access_apps_list_count"/> <item type="integer" name="recent_microphone_access_apps_list_count"/> <item type="integer" name="recent_notifications_apps_list_count"/> <item type="integer" name="recent_notifications_days_threshold"/> @@ -1054,8 +1055,11 @@ REGENERATE USING packages/apps/Car/libs/tools/rro/generate-overlayable.py <item type="string" name="location_driver_assistance_toggle_title"/> <item type="string" name="location_settings_app_permissions_title"/> <item type="string" name="location_settings_footer"/> - <item type="string" name="location_settings_recent_requests_empty_message"/> - <item type="string" name="location_settings_recent_requests_title"/> + <item type="string" name="location_settings_recently_accessed_title"/> + <item type="string" name="location_recently_accessed"/> + <item type="string" name="location_settings_recently_accessed_view_all_title"/> + <item type="string" name="location_no_recent_access"/> + <item type="string" name="driver_assistance_label"/> <item type="string" name="location_settings_services_title"/> <item type="string" name="location_settings_title"/> <item type="string" name="location_state_switch_content_description"/> @@ -1605,8 +1609,8 @@ REGENERATE USING packages/apps/Car/libs/tools/rro/generate-overlayable.py <item type="xml" name="language_picker_fragment"/> <item type="xml" name="languages_and_input_fragment"/> <item type="xml" name="legal_information_fragment"/> - <item type="xml" name="location_recent_requests_fragment"/> <item type="xml" name="location_settings_fragment"/> + <item type="xml" name="location_recent_accessed_view_all_fragment"/> <item type="xml" name="manage_domain_urls_fragment"/> <item type="xml" name="microphone_recent_requests_view_all_fragment"/> <item type="xml" name="mobile_network_fragment"/> diff --git a/res/values/preference_keys.xml b/res/values/preference_keys.xml index 7b94fc929..3373f624a 100644 --- a/res/values/preference_keys.xml +++ b/res/values/preference_keys.xml @@ -370,11 +370,11 @@ <string name="pk_units_pressure" translatable="false">units_pressure</string> <!-- Location Settings --> - <string name="pk_location_recent_requests_entry" translatable="false"> - location_recent_requests_entry + <string name="pk_location_recent_accesses_category" translatable="false"> + location_recent_accesses_category </string> - <string name="pk_location_recent_requests" translatable="false"> - location_recent_requests + <string name="pk_location_recently_accessed" translatable="false"> + location_recently_accessed </string> <string name="pk_location_app_permissions" translatable="false">location_app_permissions </string> diff --git a/res/values/preference_screen_keys.xml b/res/values/preference_screen_keys.xml index cd1fa8223..a03c32487 100644 --- a/res/values/preference_screen_keys.xml +++ b/res/values/preference_screen_keys.xml @@ -64,7 +64,7 @@ <string name="psk_language_picker" translatable="false">language_picker_screen</string> <string name="psk_languages_and_input" translatable="false">languages_and_input_screen</string> <string name="psk_legal_information" translatable="false">legal_information_screen</string> - <string name="psk_location_recent_requests" translatable="false">location_recent_requests_screen</string> + <string name="psk_location_recently_accessed" translatable="false">location_recently_accessed_screen</string> <string name="psk_location_settings" translatable="false">location_settings_screen</string> <string name="psk_adas_settings" translatable="false">adas_settings_screen</string> <string name="psk_microphone_recent_requests" translatable="false">microphone_recent_requests_screen</string> diff --git a/res/values/strings.xml b/res/values/strings.xml index d32b9124d..0e310a839 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -922,10 +922,16 @@ <string name="driver_assistance_warning_confirm_label"> Turn off anyway </string> - <!-- Title of Recent Location Requests setting [CHAR LIMIT=60] --> - <string name="location_settings_recent_requests_title">Recent Location Requests</string> - <!-- Message indicating that no apps have requested location recently [CHAR LIMIT=60] --> - <string name="location_settings_recent_requests_empty_message">No recent location requests</string> + <!-- Title of Recent Location Accesses category [CHAR LIMIT=60] --> + <string name="location_recently_accessed">Recently accessed</string> + <!-- Title of Location Recently Accessed setting [CHAR LIMIT=60] --> + <string name="location_settings_recently_accessed_title">Recently accessed</string> + <!-- Title of preference name to view all apps that recently accessed location [CHAR LIMIT=60] --> + <string name="location_settings_recently_accessed_view_all_title">View all</string> + <!-- Message indicating that no apps have accessed location recently [CHAR LIMIT=60] --> + <string name="location_no_recent_access">No recent apps</string> + <!-- Label for driver assistance apps that recently accessed location [CHAR LIMIT=60] --> + <string name="driver_assistance_label"><xliff:g id="time" example="1 min. ago">%1$s</xliff:g> \u2022 Driver Assistance</string> <!-- Title of Location App-level Permissions setting [CHAR LIMIT=60] --> <string name="location_settings_app_permissions_title">App-level permissions</string> <!-- Title of Location Services category [CHAR LIMIT=60] --> @@ -978,7 +984,7 @@ </string> <!-- Title of Recent Microphone Requests setting [CHAR LIMIT=60] --> <string name="microphone_settings_recent_requests_title">Recently accessed</string> - <!-- Title of Recent Microphone Requests setting [CHAR LIMIT=60] --> + <!-- Title of preference name to view all apps that recently accessed microphone [CHAR LIMIT=60] --> <string name="microphone_settings_recent_requests_view_all_title">View all</string> <!-- Microphone settings, loading the number of apps which have microphone permission [CHAR LIMIT=30] --> <string name="microphone_settings_loading_app_permission_stats">Loading\u2026</string> diff --git a/res/xml/location_recent_accessed_view_all_fragment.xml b/res/xml/location_recent_accessed_view_all_fragment.xml new file mode 100644 index 000000000..c29a1ec8c --- /dev/null +++ b/res/xml/location_recent_accessed_view_all_fragment.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:settings="http://schemas.android.com/apk/res-auto" + android:key="@string/psk_location_recently_accessed" + android:title="@string/location_settings_recently_accessed_title"> + <com.android.car.settings.common.LogicalPreferenceGroup + android:key="@string/pk_location_recently_accessed" + settings:controller="com.android.car.settings.location.LocationRecentAccessViewAllPreferenceController"/> +</PreferenceScreen>
\ No newline at end of file diff --git a/res/xml/location_recent_requests_fragment.xml b/res/xml/location_recent_requests_fragment.xml deleted file mode 100644 index af494cc63..000000000 --- a/res/xml/location_recent_requests_fragment.xml +++ /dev/null @@ -1,24 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2018 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:settings="http://schemas.android.com/apk/res-auto" - android:key="@string/psk_location_recent_requests" - android:title="@string/location_settings_recent_requests_title"> - <com.android.car.settings.common.LogicalPreferenceGroup - android:key="@string/pk_location_recent_requests" - settings:controller="com.android.car.settings.location.RecentLocationRequestsPreferenceController"/> -</PreferenceScreen>
\ No newline at end of file diff --git a/res/xml/location_settings_fragment.xml b/res/xml/location_settings_fragment.xml index 2dd4bb100..95c25dac0 100644 --- a/res/xml/location_settings_fragment.xml +++ b/res/xml/location_settings_fragment.xml @@ -35,11 +35,13 @@ settings:controller="com.android.car.settings.location.AdasLocationSwitchPreferenceController" settings:searchable="true"/> <com.android.car.settings.common.DividerPreference/> - <Preference - android:fragment="com.android.car.settings.location.RecentLocationRequestsFragment" - android:key="@string/pk_location_recent_requests_entry" - android:title="@string/location_settings_recent_requests_title" - settings:controller="com.android.car.settings.location.RecentLocationRequestsEntryPreferenceController"/> + <PreferenceCategory + android:key="@string/pk_location_recent_accesses_category" + android:title="@string/location_recently_accessed" + settings:controller="com.android.car.settings.location.LocationRecentAccessesPreferenceController"> + <com.android.car.settings.common.DividerPreference + android:order="999"/> + </PreferenceCategory> <Preference android:key="@string/pk_location_app_permissions" android:title="@string/location_settings_app_permissions_title" diff --git a/src/com/android/car/settings/location/LocationRecentAccessUtil.java b/src/com/android/car/settings/location/LocationRecentAccessUtil.java new file mode 100644 index 000000000..25e92a423 --- /dev/null +++ b/src/com/android/car/settings/location/LocationRecentAccessUtil.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.car.settings.location; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.icu.text.RelativeDateTimeFormatter; + +import com.android.car.settings.R; +import com.android.car.ui.preference.CarUiPreference; +import com.android.internal.util.ArrayUtils; +import com.android.settingslib.applications.RecentAppOpsAccess; +import com.android.settingslib.utils.StringUtil; + +/** Utilities related to location recent access. */ +public final class LocationRecentAccessUtil { + private LocationRecentAccessUtil() {} + + /** + * Create a {@link CarUiPreference} for an app with it's last access time and a link to its + * location permission settings. + */ + public static CarUiPreference createAppPreference( + Context prefContext, RecentAppOpsAccess.Access access) { + CarUiPreference pref = new CarUiPreference(prefContext); + pref.setIcon(access.icon); + pref.setTitle(access.label); + String summary = + StringUtil.formatRelativeTime( + prefContext, + System.currentTimeMillis() - access.accessFinishTime, + /* withSeconds= */ false, + RelativeDateTimeFormatter.Style.SHORT) + .toString(); + if (ArrayUtils.contains( + prefContext + .getResources() + .getStringArray( + com.android.internal.R.array + .config_locationDriverAssistancePackageNames), + access.packageName)) { + summary = + prefContext.getResources().getString(R.string.driver_assistance_label, summary); + } + pref.setSummary(summary); + pref.setOnPreferenceClickListener( + preference -> { + Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION); + intent.putExtra( + Intent.EXTRA_PERMISSION_GROUP_NAME, Manifest.permission_group.LOCATION); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, access.packageName); + intent.putExtra(Intent.EXTRA_USER, access.userHandle); + prefContext.startActivity(intent); + return true; + }); + return pref; + } +} diff --git a/src/com/android/car/settings/location/LocationRecentAccessViewAllFragment.java b/src/com/android/car/settings/location/LocationRecentAccessViewAllFragment.java new file mode 100644 index 000000000..7e2196d32 --- /dev/null +++ b/src/com/android/car/settings/location/LocationRecentAccessViewAllFragment.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car.settings.location; + +import android.content.Context; +import android.os.Bundle; + +import androidx.annotation.Nullable; + +import com.android.car.settings.R; +import com.android.car.settings.common.SettingsFragment; +import com.android.car.ui.toolbar.MenuItem; + +import java.util.Arrays; +import java.util.List; + +/** All apps that have recently accessed location. */ +public class LocationRecentAccessViewAllFragment extends SettingsFragment { + + private boolean mShowSystem = false; + private MenuItem mShowHideSystemMenu; + private LocationRecentAccessViewAllPreferenceController mController; + + @Override + protected int getPreferenceScreenResId() { + return R.xml.location_recent_accessed_view_all_fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mShowHideSystemMenu = + new MenuItem.Builder(getContext()) + .setTitle(R.string.show_system) + .setOnClickListener(i -> setShowSystem(!mShowSystem)) + .build(); + } + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mController = + use( + LocationRecentAccessViewAllPreferenceController.class, + R.string.pk_location_recently_accessed); + } + + @Override + protected List<MenuItem> getToolbarMenuItems() { + return Arrays.asList(mShowHideSystemMenu); + } + + private void setShowSystem(boolean showSystem) { + if (showSystem != mShowSystem) { + mShowSystem = showSystem; + mController.setShowSystem(showSystem); + updateMenu(); + } + } + + private void updateMenu() { + mShowHideSystemMenu.setTitle(mShowSystem ? R.string.hide_system : R.string.show_system); + } +} diff --git a/src/com/android/car/settings/location/LocationRecentAccessViewAllPreferenceController.java b/src/com/android/car/settings/location/LocationRecentAccessViewAllPreferenceController.java new file mode 100644 index 000000000..fa9e1b1dd --- /dev/null +++ b/src/com/android/car/settings/location/LocationRecentAccessViewAllPreferenceController.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car.settings.location; + +import android.car.drivingstate.CarUxRestrictions; +import android.content.Context; + +import com.android.car.settings.R; +import com.android.car.settings.common.FragmentController; +import com.android.car.settings.common.LogicalPreferenceGroup; +import com.android.car.settings.common.PreferenceController; +import com.android.car.ui.preference.CarUiPreference; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.applications.RecentAppOpsAccess; + +import java.util.List; + +/** + * This controller displays a list of apps recently accessing location. Driver assistance apps are + * also included. + */ +public class LocationRecentAccessViewAllPreferenceController + extends PreferenceController<LogicalPreferenceGroup> { + + private final RecentAppOpsAccess mRecentLocationAccesses; + private boolean mShowSystem = false; + + public LocationRecentAccessViewAllPreferenceController( + Context context, + String preferenceKey, + FragmentController fragmentController, + CarUxRestrictions uxRestrictions) { + this( + context, + preferenceKey, + fragmentController, + uxRestrictions, + RecentAppOpsAccess.createForLocation(context)); + } + + @VisibleForTesting + LocationRecentAccessViewAllPreferenceController( + Context context, + String preferenceKey, + FragmentController fragmentController, + CarUxRestrictions uxRestrictions, + RecentAppOpsAccess recentLocationAccesses) { + super(context, preferenceKey, fragmentController, uxRestrictions); + mRecentLocationAccesses = recentLocationAccesses; + } + + @Override + protected Class<LogicalPreferenceGroup> getPreferenceType() { + return LogicalPreferenceGroup.class; + } + + @Override + public void updateState(LogicalPreferenceGroup preference) { + super.updateState(preference); + List<RecentAppOpsAccess.Access> recentLocationAccesses = loadData(); + updateUi(recentLocationAccesses); + } + + /** + * Rebuilds the preference list to show system applications if {@code showSystem} is true. + * System applications will be hidden otherwise. + */ + public void setShowSystem(boolean showSystem) { + if (mShowSystem != showSystem) { + mShowSystem = showSystem; + refreshUi(); + } + } + + private List<RecentAppOpsAccess.Access> loadData() { + return mRecentLocationAccesses.getAppListSorted(mShowSystem); + } + + private void updateUi(List<RecentAppOpsAccess.Access> recentLocationAccesses) { + getPreference().removeAll(); + if (recentLocationAccesses.isEmpty()) { + getPreference().addPreference(createNoRecentAccessPreference()); + } else { + for (RecentAppOpsAccess.Access access : recentLocationAccesses) { + CarUiPreference appPreference = + LocationRecentAccessUtil.createAppPreference(getContext(), access); + getPreference().addPreference(appPreference); + } + } + } + + private CarUiPreference createNoRecentAccessPreference() { + CarUiPreference preference = new CarUiPreference(getContext()); + preference.setTitle(R.string.location_no_recent_access); + preference.setSelectable(false); + return preference; + } +} diff --git a/src/com/android/car/settings/location/LocationRecentAccessesPreferenceController.java b/src/com/android/car/settings/location/LocationRecentAccessesPreferenceController.java new file mode 100644 index 000000000..a1b141512 --- /dev/null +++ b/src/com/android/car/settings/location/LocationRecentAccessesPreferenceController.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car.settings.location; + +import android.car.drivingstate.CarUxRestrictions; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.location.LocationManager; + +import androidx.preference.PreferenceCategory; + +import com.android.car.settings.R; +import com.android.car.settings.common.FragmentController; +import com.android.car.settings.common.PreferenceController; +import com.android.car.ui.preference.CarUiPreference; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settingslib.applications.RecentAppOpsAccess; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * This controller displays a list of apps that recently access the location. Driver assistance apps + * are also included. + */ +public class LocationRecentAccessesPreferenceController + extends PreferenceController<PreferenceCategory> { + + private final LocationManager mLocationManager; + private final BroadcastReceiver mAdasReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + refreshUi(); + } + }; + private final BroadcastReceiver mLocationReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + refreshUi(); + } + }; + + private static final IntentFilter INTENT_FILTER_ADAS_GNSS_ENABLED_CHANGED = + new IntentFilter(LocationManager.ACTION_ADAS_GNSS_ENABLED_CHANGED); + + private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED = + new IntentFilter(LocationManager.MODE_CHANGED_ACTION); + + private final Set<CarUiPreference> mAddedPreferences = new HashSet<>(); + + private final RecentAppOpsAccess mRecentLocationAccesses; + private final int mRecentAppsMaxCount; + + public LocationRecentAccessesPreferenceController( + Context context, + String preferenceKey, + FragmentController fragmentController, + CarUxRestrictions uxRestrictions) { + this( + context, + preferenceKey, + fragmentController, + uxRestrictions, + RecentAppOpsAccess.createForLocation(context), + context.getResources().getInteger(R.integer.recent_location_access_apps_list_count), + context.getSystemService(LocationManager.class)); + } + + @VisibleForTesting + LocationRecentAccessesPreferenceController( + Context context, + String preferenceKey, + FragmentController fragmentController, + CarUxRestrictions uxRestrictions, + RecentAppOpsAccess recentLocationAccesses, + int recentAppsMaxCount, + LocationManager locationManager) { + super(context, preferenceKey, fragmentController, uxRestrictions); + mRecentLocationAccesses = recentLocationAccesses; + mRecentAppsMaxCount = recentAppsMaxCount; + mLocationManager = locationManager; + } + + @Override + protected Class<PreferenceCategory> getPreferenceType() { + return PreferenceCategory.class; + } + + @Override + protected void onStartInternal() { + getContext().registerReceiver(mAdasReceiver, INTENT_FILTER_ADAS_GNSS_ENABLED_CHANGED); + getContext().registerReceiver(mLocationReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED); + } + + @Override + protected void onStopInternal() { + getContext().unregisterReceiver(mAdasReceiver); + getContext().unregisterReceiver(mLocationReceiver); + } + + @Override + public void updateState(PreferenceCategory preference) { + super.updateState(preference); + + if (!mLocationManager.isLocationEnabled() + && !mLocationManager.isAdasGnssLocationEnabled()) { + getPreference().setVisible(false); + return; + } + getPreference().setVisible(true); + updateUi(loadData()); + } + + private List<RecentAppOpsAccess.Access> loadData() { + return mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false); + } + + private boolean hasAtLeastOneRecentAppAccess() { + return !mRecentLocationAccesses.getAppListSorted(/* showSystem= */ true).isEmpty(); + } + + private void updateUi(List<RecentAppOpsAccess.Access> sortedRecentLocationAccesses) { + // remove any already added preferences + for (CarUiPreference addedPreference : mAddedPreferences) { + getPreference().removePreference(addedPreference); + } + mAddedPreferences.clear(); + + if (sortedRecentLocationAccesses.isEmpty()) { + CarUiPreference emptyPreference = createNoRecentAccessPreference(); + getPreference().addPreference(emptyPreference); + mAddedPreferences.add(emptyPreference); + } else { + int count = Math.min(sortedRecentLocationAccesses.size(), mRecentAppsMaxCount); + for (int i = 0; i < count; i++) { + RecentAppOpsAccess.Access request = sortedRecentLocationAccesses.get(i); + CarUiPreference appPreference = + LocationRecentAccessUtil.createAppPreference(getContext(), request); + getPreference().addPreference(appPreference); + mAddedPreferences.add(appPreference); + } + } + + if (hasAtLeastOneRecentAppAccess()) { + CarUiPreference viewAllPreference = createViewAllPreference(); + getPreference().addPreference(viewAllPreference); + mAddedPreferences.add(viewAllPreference); + } + } + + private CarUiPreference createNoRecentAccessPreference() { + CarUiPreference preference = new CarUiPreference(getContext()); + preference.setTitle(R.string.location_no_recent_access); + preference.setSelectable(false); + return preference; + } + + private CarUiPreference createViewAllPreference() { + CarUiPreference preference = new CarUiPreference(getContext()); + preference.setTitle(R.string.location_settings_recently_accessed_view_all_title); + preference.setIcon(R.drawable.ic_apps); + preference.setOnPreferenceClickListener( + p -> { + getFragmentController() + .launchFragment(new LocationRecentAccessViewAllFragment()); + return true; + }); + return preference; + } +} diff --git a/src/com/android/car/settings/location/RecentLocationRequestsEntryPreferenceController.java b/src/com/android/car/settings/location/RecentLocationRequestsEntryPreferenceController.java deleted file mode 100644 index c6025dde2..000000000 --- a/src/com/android/car/settings/location/RecentLocationRequestsEntryPreferenceController.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.car.settings.location; - -import android.car.drivingstate.CarUxRestrictions; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.location.LocationManager; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.car.settings.common.FragmentController; -import com.android.car.settings.common.PreferenceController; - -/** - * Disables Recent Location Requests entry when location is off. - */ -public class RecentLocationRequestsEntryPreferenceController extends - PreferenceController<Preference> { - - private final LocationManager mLocationManager; - - @VisibleForTesting - static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED = - new IntentFilter(LocationManager.MODE_CHANGED_ACTION); - - @VisibleForTesting - final BroadcastReceiver mReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - refreshUi(); - } - }; - - public RecentLocationRequestsEntryPreferenceController(Context context, String preferenceKey, - FragmentController fragmentController, CarUxRestrictions uxRestrictions) { - super(context, preferenceKey, fragmentController, uxRestrictions); - mLocationManager = getContext().getSystemService(LocationManager.class); - } - - - @Override - protected Class<Preference> getPreferenceType() { - return Preference.class; - } - - @Override - protected void updateState(Preference preference) { - preference.setEnabled(mLocationManager.isLocationEnabled()); - } - - @Override - protected void onStartInternal() { - getContext().registerReceiver(mReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED); - } - - @Override - protected void onStopInternal() { - getContext().unregisterReceiver(mReceiver); - } -} diff --git a/src/com/android/car/settings/location/RecentLocationRequestsFragment.java b/src/com/android/car/settings/location/RecentLocationRequestsFragment.java deleted file mode 100644 index 592225a57..000000000 --- a/src/com/android/car/settings/location/RecentLocationRequestsFragment.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.car.settings.location; - -import androidx.annotation.XmlRes; - -import com.android.car.settings.R; -import com.android.car.settings.common.SettingsFragment; - -/** - * Shows a list of applications that have requested location recently. - */ -public class RecentLocationRequestsFragment extends SettingsFragment { - - @Override - @XmlRes - protected int getPreferenceScreenResId() { - return R.xml.location_recent_requests_fragment; - } -} diff --git a/src/com/android/car/settings/location/RecentLocationRequestsPreferenceController.java b/src/com/android/car/settings/location/RecentLocationRequestsPreferenceController.java deleted file mode 100644 index 69036efc0..000000000 --- a/src/com/android/car/settings/location/RecentLocationRequestsPreferenceController.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.car.settings.location; - -import android.car.drivingstate.CarUxRestrictions; -import android.content.Context; -import android.content.pm.PackageManager; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; -import androidx.preference.PreferenceGroup; - -import com.android.car.settings.R; -import com.android.car.settings.applications.ApplicationDetailsFragment; -import com.android.car.settings.common.FragmentController; -import com.android.car.settings.common.PreferenceController; -import com.android.car.ui.preference.CarUiPreference; -import com.android.settingslib.location.RecentLocationApps; -import com.android.settingslib.location.RecentLocationApps.Request; - -import java.util.List; - -/** - * Displays all apps that have requested location recently. - */ -public class RecentLocationRequestsPreferenceController extends - PreferenceController<PreferenceGroup> { - private final PackageManager mPackageManager; - private RecentLocationApps mRecentLocationApps; - // This list will always be sorted by most recent first. - private List<Request> mRecentLocationRequests; - - public RecentLocationRequestsPreferenceController(Context context, String preferenceKey, - FragmentController fragmentController, CarUxRestrictions uxRestrictions) { - super(context, preferenceKey, fragmentController, uxRestrictions); - mRecentLocationApps = new RecentLocationApps(context); - mPackageManager = context.getPackageManager(); - } - - @VisibleForTesting - void setRecentLocationApps(RecentLocationApps apps) { - mRecentLocationApps = apps; - } - - @Override - protected Class<PreferenceGroup> getPreferenceType() { - return PreferenceGroup.class; - } - - @Override - protected void updateState(PreferenceGroup group) { - if (mRecentLocationRequests == null) { - // First time displaying a list. - mRecentLocationRequests = - mRecentLocationApps.getAppListSorted(/* showSystemApps= */ true); - } else { - // If preferences were already added to the screen, get a new list - // and only update the displayed app-list if there is a difference. - List<Request> newRequests = mRecentLocationApps.getAppListSorted(true); - // listsEqual compares by elements' package names only, using List.equals() will - // not work because it will always return false since it also compares the time. - if (listsEqual(newRequests, mRecentLocationRequests)) { - return; - } - mRecentLocationRequests = newRequests; - } - if (mRecentLocationRequests.isEmpty()) { - CarUiPreference emptyMessagePref = new CarUiPreference(getContext()); - emptyMessagePref.setTitle(R.string.location_settings_recent_requests_empty_message); - group.addPreference(emptyMessagePref); - } else { - group.removeAll(); - for (Request request : mRecentLocationRequests) { - Preference appPref = createPreference(request); - group.addPreference(appPref); - } - } - } - - private Preference createPreference(Request request) { - CarUiPreference pref = new CarUiPreference(getContext()); - pref.setSummary(request.contentDescription); - pref.setIcon(request.icon); - pref.setTitle(request.label); - if (isPackageInstalled(request.packageName)) { - pref.setOnPreferenceClickListener(p -> { - getFragmentController().launchFragment( - ApplicationDetailsFragment.getInstance( - request.packageName)); - return true; - }); - } - return pref; - } - - private boolean isPackageInstalled(String packageName) { - try { - mPackageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - return true; - } - - /** - * Compares two {@link Request} lists by the elements' package names. - * - * @param a The first list. - * @param b The second list. - * @return {@code true} if both lists have the same elements (by package name) and order. - */ - private boolean listsEqual(List<Request> a, List<Request> b) { - if (a.size() != b.size()) { - return false; - } - for (int i = 0; i < a.size(); i++) { - if (!a.get(i).packageName.equals(b.get(i).packageName)) { - return false; - } - } - return true; - } -} diff --git a/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java b/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java index 7a9a7c8d7..20b65a55c 100644 --- a/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java +++ b/src/com/android/car/settings/wifi/WifiRequestToggleActivity.java @@ -107,10 +107,10 @@ public class WifiRequestToggleActivity extends FragmentActivity { try { PackageManager pm = getPackageManager(); - ApplicationInfo ai = pm.getApplicationInfo(getCallingPackage(), /* flags= */ 0); + ApplicationInfo ai = pm.getApplicationInfo(getLaunchedFromPackage(), /* flags= */ 0); mAppLabel = pm.getApplicationLabel(ai); } catch (PackageManager.NameNotFoundException e) { - LOG.e("Couldn't find app with package name " + getCallingPackage()); + LOG.e("Couldn't find app with package name " + getLaunchedFromPackage()); finish(); return; } diff --git a/tests/unit/src/com/android/car/settings/location/LocationRecentAccessViewAllPreferenceControllerTest.java b/tests/unit/src/com/android/car/settings/location/LocationRecentAccessViewAllPreferenceControllerTest.java new file mode 100644 index 000000000..ee519b8de --- /dev/null +++ b/tests/unit/src/com/android/car/settings/location/LocationRecentAccessViewAllPreferenceControllerTest.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car.settings.location; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.Manifest; +import android.car.drivingstate.CarUxRestrictions; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.UserHandle; + +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.test.annotation.UiThreadTest; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.car.settings.R; +import com.android.car.settings.common.FragmentController; +import com.android.car.settings.common.LogicalPreferenceGroup; +import com.android.car.settings.common.PreferenceControllerTestUtil; +import com.android.car.settings.testutils.TestLifecycleOwner; +import com.android.settingslib.applications.RecentAppOpsAccess; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class LocationRecentAccessViewAllPreferenceControllerTest { + + private final Context mContext = Mockito.spy(ApplicationProvider.getApplicationContext()); + private LifecycleOwner mLifecycleOwner; + private LogicalPreferenceGroup mPreference; + private LocationRecentAccessViewAllPreferenceController mPreferenceController; + + @Mock private FragmentController mFragmentController; + @Mock private RecentAppOpsAccess mRecentLocationAccesses; + + @Before + @UiThreadTest + public void setUp() { + MockitoAnnotations.initMocks(this); + mLifecycleOwner = new TestLifecycleOwner(); + CarUxRestrictions carUxRestrictions = + new CarUxRestrictions.Builder( + /* reqOpt= */ true, + CarUxRestrictions.UX_RESTRICTIONS_BASELINE, + /* timestamp= */ 0) + .build(); + + PreferenceManager preferenceManager = new PreferenceManager(mContext); + PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext); + mPreference = new LogicalPreferenceGroup(mContext); + screen.addPreference(mPreference); + mPreferenceController = + new LocationRecentAccessViewAllPreferenceController( + mContext, + "key", + mFragmentController, + carUxRestrictions, + mRecentLocationAccesses); + PreferenceControllerTestUtil.assignPreference(mPreferenceController, mPreference); + doNothing().when(mContext).startActivity(any()); + } + + @Test + public void updateState_noSystemAppsByDefault() { + initializePreference(); + verify(mRecentLocationAccesses, never()).getAppListSorted(/* showSystem= */ true); + } + + @Test + public void setShowSystem_showsSystemApps() { + initializePreference(); + mPreferenceController.setShowSystem(true); + verify(mRecentLocationAccesses).getAppListSorted(/* showSystem= */ true); + } + + @Test + public void refreshUi_noRecentAccesses_showsEmptyState() { + initializePreference(); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)) + .thenReturn(Collections.emptyList()); + mPreferenceController.refreshUi(); + + assertThat(mPreference.getPreference(0).getTitle()) + .isEqualTo(mContext.getString(R.string.location_no_recent_access)); + } + + @Test + public void refreshUi_someRecentAccesses_displaysAppInformation() { + initializePreference(); + + String fakeLabel = "Test app 1"; + RecentAppOpsAccess.Access fakeAccess = + new RecentAppOpsAccess.Access( + "com.test", + UserHandle.CURRENT, + mock(Drawable.class), + fakeLabel, + "fake contentDescription", + Clock.systemDefaultZone().millis()); + List<RecentAppOpsAccess.Access> list = Collections.singletonList(fakeAccess); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list); + mPreferenceController.refreshUi(); + + assertThat(mPreference.getPreference(0).getTitle()).isEqualTo(fakeLabel); + assertThat(mPreference.getPreference(0).getSummary().toString()).contains("min. ago"); + } + + @Test + public void refreshUi_recentAccesses_launchLocationSettings() { + initializePreference(); + + List<RecentAppOpsAccess.Access> list = + Collections.singletonList(mock(RecentAppOpsAccess.Access.class)); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list); + mPreferenceController.refreshUi(); + + mPreference.getPreference(0).performClick(); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivity(captor.capture()); + + Intent intent = captor.getValue(); + assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSION); + assertThat(intent.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME)) + .isEqualTo(Manifest.permission_group.LOCATION); + } + + @Test + public void refreshUi_newRecentAccesses_listIsUpdated() { + initializePreference(); + + List<RecentAppOpsAccess.Access> list1 = + Collections.singletonList(mock(RecentAppOpsAccess.Access.class)); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list1); + + List<RecentAppOpsAccess.Access> list2 = new ArrayList<>(list1); + list2.add(mock(RecentAppOpsAccess.Access.class)); + + mPreferenceController.refreshUi(); + assertThat(mPreference.getPreferenceCount()).isEqualTo(list1.size()); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list2); + mPreferenceController.refreshUi(); + assertThat(mPreference.getPreferenceCount()).isEqualTo(list2.size()); + } + + private void initializePreference() { + mPreferenceController.onCreate(mLifecycleOwner); + mPreferenceController.onStart(mLifecycleOwner); + } +} diff --git a/tests/unit/src/com/android/car/settings/location/LocationRecentAccessesPreferenceControllerTest.java b/tests/unit/src/com/android/car/settings/location/LocationRecentAccessesPreferenceControllerTest.java new file mode 100644 index 000000000..d07b8975e --- /dev/null +++ b/tests/unit/src/com/android/car/settings/location/LocationRecentAccessesPreferenceControllerTest.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.car.settings.location; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.car.drivingstate.CarUxRestrictions; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.drawable.Drawable; +import android.location.LocationManager; +import android.os.UserHandle; + +import androidx.lifecycle.LifecycleOwner; +import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceManager; +import androidx.preference.PreferenceScreen; +import androidx.test.annotation.UiThreadTest; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.car.settings.R; +import com.android.car.settings.common.FragmentController; +import com.android.car.settings.common.PreferenceControllerTestUtil; +import com.android.car.settings.testutils.TestLifecycleOwner; +import com.android.settingslib.applications.RecentAppOpsAccess; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.time.Clock; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class LocationRecentAccessesPreferenceControllerTest { + private static final int RECENT_APPS_MAX_COUNT = 2; + + private final Context mContext = Mockito.spy(ApplicationProvider.getApplicationContext()); + private LifecycleOwner mLifecycleOwner; + private PreferenceCategory mPreference; + private LocationRecentAccessesPreferenceController mPreferenceController; + + @Mock private FragmentController mFragmentController; + @Mock private RecentAppOpsAccess mRecentLocationAccesses; + @Mock private LocationManager mMockLocationManager; + @Captor private ArgumentCaptor<BroadcastReceiver> mAdasReceiver; + @Captor private ArgumentCaptor<BroadcastReceiver> mLocationReceiver; + + @Before + @UiThreadTest + public void setUp() { + MockitoAnnotations.initMocks(this); + mLifecycleOwner = new TestLifecycleOwner(); + CarUxRestrictions carUxRestrictions = + new CarUxRestrictions.Builder( + /* reqOpt= */ true, + CarUxRestrictions.UX_RESTRICTIONS_BASELINE, + /* timestamp= */ 0) + .build(); + + PreferenceManager preferenceManager = new PreferenceManager(mContext); + PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext); + mPreference = new PreferenceCategory(mContext); + screen.addPreference(mPreference); + mPreferenceController = + new LocationRecentAccessesPreferenceController( + mContext, + "key", + mFragmentController, + carUxRestrictions, + mRecentLocationAccesses, + RECENT_APPS_MAX_COUNT, + mMockLocationManager); + PreferenceControllerTestUtil.assignPreference(mPreferenceController, mPreference); + doNothing().when(mContext).startActivity(any()); + mPreferenceController.onCreate(mLifecycleOwner); + } + + @Test + public void locationAndAdasOff_preferenceIsHidden() { + setIsLocationEnabled(false); + setIsAdasGnssLocationEnabled(false); + + initializePreference(); + + assertThat(mPreference.isVisible()).isFalse(); + } + + @Test + public void locationAndAdasOn_preferenceIsShown() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + initializePreference(); + + assertThat(mPreference.isVisible()).isTrue(); + } + + @Test + public void adasOnOnly_preferenceIsShown() { + setIsLocationEnabled(false); + setIsAdasGnssLocationEnabled(true); + + initializePreference(); + + assertThat(mPreference.isVisible()).isTrue(); + } + + @Test + public void refreshUi_noRecentRequests_messageDisplayed() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)) + .thenReturn(Collections.emptyList()); + mPreferenceController.refreshUi(); + + assertThat(mPreference.getPreference(0).getTitle()) + .isEqualTo(mContext.getString(R.string.location_no_recent_access)); + } + + @Test + public void refreshUi_noRecentRequests_exceptForSomeRecentSystemAppRequests_showsViewAll() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)) + .thenReturn(Collections.emptyList()); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ true)) + .thenReturn(Collections.singletonList(mock(RecentAppOpsAccess.Access.class))); + mPreferenceController.refreshUi(); + + // includes preference for "View all" + assertThat(mPreference.getPreferenceCount()).isEqualTo(2); + assertThat(mPreference.getPreference(1).getTitle()) + .isEqualTo( + mContext.getString( + R.string.location_settings_recently_accessed_view_all_title)); + } + + @Test + @UiThreadTest + public void refreshUi_clickViewAll_launchesFragment() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ true)) + .thenReturn(Collections.singletonList(mock(RecentAppOpsAccess.Access.class))); + mPreferenceController.refreshUi(); + + // click on the "View all" preference + mPreference.getPreference(1).performClick(); + verify(mFragmentController).launchFragment(any(LocationRecentAccessViewAllFragment.class)); + } + + @Test + public void refreshUi_noRecentAccesses_includingNoSystemAppAccesses_doesNotShowViewAll() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)) + .thenReturn(Collections.emptyList()); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ true)) + .thenReturn(Collections.emptyList()); + mPreferenceController.refreshUi(); + + // no preference for "View all" + assertThat(mPreference.getPreferenceCount()).isEqualTo(1); + assertThat(mPreference.getPreference(0).getTitle()) + .isEqualTo(mContext.getString(R.string.location_no_recent_access)); + } + + @Test + public void refreshUi_someRecentAccesses_displaysAppInformation() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + String fakeLabel = "Test app 1"; + RecentAppOpsAccess.Access fakeAccess = + new RecentAppOpsAccess.Access( + "com.test", + UserHandle.CURRENT, + mock(Drawable.class), + fakeLabel, + "fake contentDescription", + Clock.systemDefaultZone().millis()); + List<RecentAppOpsAccess.Access> list = Collections.singletonList(fakeAccess); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list); + mPreferenceController.refreshUi(); + + assertThat(mPreference.getPreference(0).getTitle()).isEqualTo(fakeLabel); + assertThat(mPreference.getPreference(0).getSummary().toString()).contains("min. ago"); + } + + @Test + public void refreshUi_someRecentAcesses_preferencesAddedToScreen_capsAtMax() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + List<RecentAppOpsAccess.Access> list = + Arrays.asList( + mock(RecentAppOpsAccess.Access.class), + mock(RecentAppOpsAccess.Access.class), + mock(RecentAppOpsAccess.Access.class)); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list); + mPreferenceController.refreshUi(); + + assertThat(mPreference.getPreferenceCount()).isEqualTo(RECENT_APPS_MAX_COUNT); + } + + @Test + public void refreshUi_recentRequests_launchLocationSettings() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + List<RecentAppOpsAccess.Access> list = + Collections.singletonList(mock(RecentAppOpsAccess.Access.class)); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list); + mPreferenceController.refreshUi(); + + mPreference.getPreference(0).performClick(); + + ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivity(captor.capture()); + + Intent intent = captor.getValue(); + assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSION); + } + + @Test + public void refreshUi_newRecentRequests_listIsUpdated() { + setIsLocationEnabled(true); + setIsAdasGnssLocationEnabled(true); + + List<RecentAppOpsAccess.Access> list1 = + Collections.singletonList(mock(RecentAppOpsAccess.Access.class)); + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list1); + + List<RecentAppOpsAccess.Access> list2 = new ArrayList<>(list1); + list2.add(mock(RecentAppOpsAccess.Access.class)); + + mPreferenceController.refreshUi(); + assertThat(mPreference.getPreferenceCount()).isEqualTo(list1.size()); + + when(mRecentLocationAccesses.getAppListSorted(/* showSystem= */ false)).thenReturn(list2); + mPreferenceController.refreshUi(); + assertThat(mPreference.getPreferenceCount()).isEqualTo(list2.size()); + } + + private void initializePreference() { + mPreferenceController.onCreate(mLifecycleOwner); + mPreferenceController.onStart(mLifecycleOwner); + + ArgumentCaptor<BroadcastReceiver> broadcastReceiverArgumentCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + ArgumentCaptor<IntentFilter> intentFilterCaptor = + ArgumentCaptor.forClass(IntentFilter.class); + + verify(mContext, times(2)) + .registerReceiver( + broadcastReceiverArgumentCaptor.capture(), intentFilterCaptor.capture()); + } + + private void setIsLocationEnabled(boolean isEnabled) { + when(mMockLocationManager.isLocationEnabled()).thenReturn(isEnabled); + } + + private void setIsAdasGnssLocationEnabled(boolean isEnabled) { + when(mMockLocationManager.isAdasGnssLocationEnabled()).thenReturn(isEnabled); + } +} diff --git a/tests/unit/src/com/android/car/settings/location/RecentLocationRequestsEntryPreferenceControllerTest.java b/tests/unit/src/com/android/car/settings/location/RecentLocationRequestsEntryPreferenceControllerTest.java deleted file mode 100644 index 3e41c87d1..000000000 --- a/tests/unit/src/com/android/car/settings/location/RecentLocationRequestsEntryPreferenceControllerTest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.car.settings.location; - -import static com.android.car.settings.location.RecentLocationRequestsEntryPreferenceController.INTENT_FILTER_LOCATION_MODE_CHANGED; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.car.drivingstate.CarUxRestrictions; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; - -import androidx.lifecycle.LifecycleOwner; -import androidx.preference.Preference; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.car.settings.common.FragmentController; -import com.android.car.settings.common.PreferenceControllerTestUtil; -import com.android.car.settings.testutils.TestLifecycleOwner; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class RecentLocationRequestsEntryPreferenceControllerTest { - private static final long TIMEOUT_MS = 5000; - - private Context mContext = spy(ApplicationProvider.getApplicationContext()); - private LifecycleOwner mLifecycleOwner; - private Preference mPreference; - private RecentLocationRequestsEntryPreferenceController mPreferenceController; - private CarUxRestrictions mCarUxRestrictions; - - @Mock - private FragmentController mFragmentController; - @Mock - private LocationManager mLocationManager; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mLifecycleOwner = new TestLifecycleOwner(); - mCarUxRestrictions = new CarUxRestrictions.Builder(/* reqOpt= */ true, - CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build(); - - when(mContext.getSystemService(LocationManager.class)).thenReturn(mLocationManager); - - mPreference = new Preference(mContext); - mPreferenceController = new RecentLocationRequestsEntryPreferenceController(mContext, - "key", mFragmentController, mCarUxRestrictions); - PreferenceControllerTestUtil.assignPreference(mPreferenceController, mPreference); - - mPreferenceController.onCreate(mLifecycleOwner); - } - - @Test - public void onStart_registersBroadcastReceiver() { - mPreferenceController.onStart(mLifecycleOwner); - verify(mContext).registerReceiver(any(BroadcastReceiver.class), - eq(INTENT_FILTER_LOCATION_MODE_CHANGED)); - } - - @Test - public void onStop_unregistersBroadcastReceiver() { - mPreferenceController.onStart(mLifecycleOwner); - ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass( - BroadcastReceiver.class); - verify(mContext).registerReceiver(captor.capture(), - eq(INTENT_FILTER_LOCATION_MODE_CHANGED)); - - mPreferenceController.onStop(mLifecycleOwner); - verify(mContext).unregisterReceiver(captor.getValue()); - } - - @Test - public void refreshUi_locationOn_preferenceIsEnabled() { - when(mLocationManager.isLocationEnabled()).thenReturn(true); - mPreferenceController.refreshUi(); - - assertThat(mPreference.isEnabled()).isTrue(); - } - - @Test - public void refreshUi_locationOff_preferenceIsDisabled() { - when(mLocationManager.isLocationEnabled()).thenReturn(false); - mPreferenceController.refreshUi(); - - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void locationModeChangedBroadcastSent_locationOff_preferenceIsDisabled() { - mPreferenceController.onStart(mLifecycleOwner); - when(mLocationManager.isLocationEnabled()).thenReturn(true); - mPreferenceController.refreshUi(); - when(mLocationManager.isLocationEnabled()).thenReturn(false); - mPreferenceController.mReceiver.onReceive(mContext, new Intent()); - - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void locationModeChangedBroadcastSent_locationOn_preferenceIsEnabled() { - mPreferenceController.onStart(mLifecycleOwner); - when(mLocationManager.isLocationEnabled()).thenReturn(false); - mPreferenceController.refreshUi(); - when(mLocationManager.isLocationEnabled()).thenReturn(true); - mPreferenceController.mReceiver.onReceive(mContext, new Intent()); - - assertThat(mPreference.isEnabled()).isTrue(); - } -} diff --git a/tests/unit/src/com/android/car/settings/location/RecentLocationRequestsPreferenceControllerTest.java b/tests/unit/src/com/android/car/settings/location/RecentLocationRequestsPreferenceControllerTest.java deleted file mode 100644 index e3e686693..000000000 --- a/tests/unit/src/com/android/car/settings/location/RecentLocationRequestsPreferenceControllerTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.car.settings.location; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.car.drivingstate.CarUxRestrictions; -import android.content.Context; - -import androidx.lifecycle.LifecycleOwner; -import androidx.preference.PreferenceGroup; -import androidx.preference.PreferenceManager; -import androidx.preference.PreferenceScreen; -import androidx.test.annotation.UiThreadTest; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.car.settings.R; -import com.android.car.settings.common.FragmentController; -import com.android.car.settings.common.LogicalPreferenceGroup; -import com.android.car.settings.common.PreferenceControllerTestUtil; -import com.android.car.settings.testutils.TestLifecycleOwner; -import com.android.settingslib.location.RecentLocationApps; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -@RunWith(AndroidJUnit4.class) -public class RecentLocationRequestsPreferenceControllerTest { - private Context mContext = ApplicationProvider.getApplicationContext(); - private LifecycleOwner mLifecycleOwner; - private PreferenceGroup mPreferenceGroup; - private RecentLocationRequestsPreferenceController mPreferenceController; - private CarUxRestrictions mCarUxRestrictions; - - @Mock - private FragmentController mFragmentController; - @Mock - private RecentLocationApps mRecentLocationApps; - - @Before - @UiThreadTest - public void setUp() { - MockitoAnnotations.initMocks(this); - mLifecycleOwner = new TestLifecycleOwner(); - mCarUxRestrictions = new CarUxRestrictions.Builder(/* reqOpt= */ true, - CarUxRestrictions.UX_RESTRICTIONS_BASELINE, /* timestamp= */ 0).build(); - - PreferenceManager preferenceManager = new PreferenceManager(mContext); - PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext); - mPreferenceGroup = new LogicalPreferenceGroup(mContext); - screen.addPreference(mPreferenceGroup); - mPreferenceController = new RecentLocationRequestsPreferenceController(mContext, - "key", mFragmentController, mCarUxRestrictions); - mPreferenceController.setRecentLocationApps(mRecentLocationApps); - PreferenceControllerTestUtil.assignPreference(mPreferenceController, mPreferenceGroup); - mPreferenceController.onCreate(mLifecycleOwner); - } - - @Test - public void refreshUi_noRecentRequests_messageDisplayed() { - when(mRecentLocationApps.getAppListSorted(true)).thenReturn(Collections.emptyList()); - mPreferenceController.refreshUi(); - - assertThat(mPreferenceGroup.getPreference(0).getTitle()).isEqualTo( - mContext.getString(R.string.location_settings_recent_requests_empty_message)); - } - - @Test - public void refreshUi_someRecentRequests_preferencesAddedToScreen() { - List<RecentLocationApps.Request> list = Arrays.asList( - mock(RecentLocationApps.Request.class), - mock(RecentLocationApps.Request.class), - mock(RecentLocationApps.Request.class)); - when(mRecentLocationApps.getAppListSorted(true)).thenReturn(list); - mPreferenceController.refreshUi(); - - assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(list.size()); - } - - @Test - public void refreshUi_newRecentRequests_listIsUpdated() { - List<RecentLocationApps.Request> list1 = Arrays.asList( - mock(RecentLocationApps.Request.class), - mock(RecentLocationApps.Request.class), - mock(RecentLocationApps.Request.class)); - when(mRecentLocationApps.getAppListSorted(true)).thenReturn(list1); - - List<RecentLocationApps.Request> list2 = new ArrayList<>(list1); - list2.add(mock(RecentLocationApps.Request.class)); - - mPreferenceController.refreshUi(); - assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(list1.size()); - - when(mRecentLocationApps.getAppListSorted(true)).thenReturn(list2); - mPreferenceController.refreshUi(); - - assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(list2.size()); - } -} diff --git a/tests/unit/src/com/android/car/settings/privacy/MicrophoneRecentAccessViewAllPreferenceControllerTest.java b/tests/unit/src/com/android/car/settings/privacy/MicrophoneRecentAccessViewAllPreferenceControllerTest.java index 7a543890b..99b6f280f 100644 --- a/tests/unit/src/com/android/car/settings/privacy/MicrophoneRecentAccessViewAllPreferenceControllerTest.java +++ b/tests/unit/src/com/android/car/settings/privacy/MicrophoneRecentAccessViewAllPreferenceControllerTest.java @@ -25,6 +25,7 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.Manifest; import android.car.drivingstate.CarUxRestrictions; import android.content.Context; import android.content.Intent; @@ -149,6 +150,8 @@ public class MicrophoneRecentAccessViewAllPreferenceControllerTest { Intent intent = captor.getValue(); assertThat(intent.getAction()).isEqualTo(Intent.ACTION_MANAGE_APP_PERMISSION); + assertThat(intent.getStringExtra(Intent.EXTRA_PERMISSION_GROUP_NAME)) + .isEqualTo(Manifest.permission_group.MICROPHONE); } @Test |