diff --git a/res/xml/location_settings.xml b/res/xml/location_settings.xml index 162dc389824..fb03f4c7f3d 100644 --- a/res/xml/location_settings.xml +++ b/res/xml/location_settings.xml @@ -22,10 +22,16 @@ settings:keywords="@string/keywords_location"> + android:key="recent_location_requests" + android:title="@string/location_category_recent_location_requests" + settings:controller="com.android.settings.location.RecentLocationRequestPreferenceController"/> + + + settings:controller="com.android.settings.location.RecentLocationRequestPreferenceController"/> + + + settings:controller="com.android.settings.location.RecentLocationRequestPreferenceController"/> + + recentLocationAccesses = new ArrayList<>(); - final UserManager userManager = UserManager.get(mContext); - for (RecentLocationAccesses.Access access : mRecentLocationApps.getAppListSorted()) { - if (isRequestMatchesProfileType(userManager, access, mType)) { - recentLocationAccesses.add(access); - } - } - - if (recentLocationAccesses.size() > 0) { - // Add preferences to container in original order (already sorted by recency). - for (RecentLocationAccesses.Access access : recentLocationAccesses) { - mCategoryRecentLocationRequests.addPreference( - createAppPreference(prefContext, access, mFragment)); - } - } else { - // If there's no item to display, add a "No recent apps" item. - final Preference banner = new AppPreference(prefContext); - banner.setTitle(R.string.location_no_recent_accesses); - banner.setSelectable(false); - mCategoryRecentLocationRequests.addPreference(banner); - } + final LayoutPreference preference = screen.findPreference(KEY_APPS_DASHBOARD); + final View view = preference.findViewById(R.id.app_entities_header); + mController = AppEntitiesHeaderController.newInstance(mContext, view) + .setHeaderTitleRes(R.string.location_category_recent_location_access) + .setHeaderDetailsRes(R.string.location_recent_location_access_view_details) + .setHeaderEmptyRes(R.string.location_no_recent_accesses) + .setHeaderDetailsClickListener((View v) -> { + final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSION_USAGE); + intent.putExtra(Intent.EXTRA_PERMISSION_NAME, + Manifest.permission.ACCESS_FINE_LOCATION); + intent.putExtra(Intent.EXTRA_DURATION_MILLIS, DAYS.toMillis(1)); + mContext.startActivity(intent); + }); } @Override - public void onLocationModeChanged(int mode, boolean restricted) { - mCategoryRecentLocationRequests.setEnabled(mLocationEnabler.isEnabled(mode)); + public void updateState(Preference preference) { + updateRecentApps(); } - /** - * Initialize {@link ProfileSelectFragment.ProfileType} of the controller - * - * @param type {@link ProfileSelectFragment.ProfileType} of the controller. - */ - public void setProfileType(@ProfileSelectFragment.ProfileType int type) { - mType = type; - } - - /** - * Create a {@link AppPreference} - */ - public static AppPreference createAppPreference(Context prefContext, - RecentLocationAccesses.Access access, DashboardFragment fragment) { - final AppPreference pref = new AppPreference(prefContext); - pref.setIcon(access.icon); - pref.setTitle(access.label); - pref.setOnPreferenceClickListener(new PackageEntryClickedListener( - fragment.getContext(), access.packageName, access.userHandle)); - return pref; - } - - /** - * Return if the {@link RecentLocationAccesses.Access} matches current UI - * {@ProfileSelectFragment.ProfileType} - */ - public static boolean isRequestMatchesProfileType(UserManager userManager, - RecentLocationAccesses.Access access, @ProfileSelectFragment.ProfileType int type) { - - final boolean isWorkProfile = userManager.isManagedProfile( - access.userHandle.getIdentifier()); - if (isWorkProfile && (type & ProfileSelectFragment.ProfileType.WORK) != 0) { - return true; + private void updateRecentApps() { + final List recentLocationAccesses = + mRecentLocationAccesses.getAppListSorted(); + if (recentLocationAccesses.size() > 0) { + // Display the top 3 preferences to container in original order. + int i = 0; + for (; i < Math.min(recentLocationAccesses.size(), MAXIMUM_APP_COUNT); i++) { + final RecentLocationAccesses.Access access = recentLocationAccesses.get(i); + final AppEntityInfo appEntityInfo = new AppEntityInfo.Builder() + .setIcon(access.icon) + .setTitle(access.label) + .setSummary(StringUtil.formatRelativeTime(mContext, + System.currentTimeMillis() - access.accessFinishTime, false, + RelativeDateTimeFormatter.Style.SHORT)) + .setOnClickListener((v) -> { + final Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSION); + intent.putExtra(Intent.EXTRA_PERMISSION_NAME, + Manifest.permission.ACCESS_FINE_LOCATION); + intent.putExtra(Intent.EXTRA_PACKAGE_NAME, access.packageName); + intent.putExtra(Intent.EXTRA_USER, access.userHandle); + mContext.startActivity(intent); + }) + .build(); + mController.setAppEntity(i, appEntityInfo); + } + for (; i < MAXIMUM_APP_COUNT; i++) { + mController.removeAppEntity(i); + } } - if (!isWorkProfile && (type & ProfileSelectFragment.ProfileType.PERSONAL) != 0) { - return true; - } - return false; + mController.apply(); } } diff --git a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java index 5feee6002e0..71a80de0689 100644 --- a/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/location/RecentLocationAccessPreferenceControllerTest.java @@ -24,17 +24,19 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.graphics.drawable.Drawable; +import android.provider.DeviceConfig; import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; -import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; import com.android.settings.R; -import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.Utils; import com.android.settings.testutils.shadow.ShadowDeviceConfig; import com.android.settingslib.location.RecentLocationAccesses; +import com.android.settingslib.widget.LayoutPreference; import org.junit.After; import org.junit.Before; @@ -53,14 +55,11 @@ import java.util.List; @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowDeviceConfig.class}) public class RecentLocationAccessPreferenceControllerTest { - private static final String PREFERENCE_KEY = "test_preference_key"; @Mock - private PreferenceCategory mLayoutPreference; + private LayoutPreference mLayoutPreference; @Mock private PreferenceScreen mScreen; @Mock - private DashboardFragment mDashboardFragment; - @Mock private RecentLocationAccesses mRecentLocationApps; private Context mContext; @@ -72,16 +71,15 @@ public class RecentLocationAccessPreferenceControllerTest { MockitoAnnotations.initMocks(this); mContext = spy(RuntimeEnvironment.application); mController = spy( - new RecentLocationAccessPreferenceController(mContext, PREFERENCE_KEY, - mRecentLocationApps)); - mController.init(mDashboardFragment); + new RecentLocationAccessPreferenceController(mContext, mRecentLocationApps)); final String key = mController.getPreferenceKey(); mAppEntitiesHeaderView = LayoutInflater.from(mContext).inflate( R.layout.app_entities_header, null /* root */); when(mScreen.findPreference(key)).thenReturn(mLayoutPreference); when(mLayoutPreference.getKey()).thenReturn(key); when(mLayoutPreference.getContext()).thenReturn(mContext); - when(mDashboardFragment.getContext()).thenReturn(mContext); + when(mLayoutPreference.findViewById(R.id.app_entities_header)).thenReturn( + mAppEntitiesHeaderView); } @After @@ -90,7 +88,16 @@ public class RecentLocationAccessPreferenceControllerTest { } @Test - public void isAvailable_shouldReturnTrue() { + public void isAvailable_permissionHubNotSet_shouldReturnFalse() { + // We have not yet set the property to show the Permissions Hub. + assertThat(mController.isAvailable()).isEqualTo(false); + } + + @Test + public void isAvailable_permissionHubEnabled_shouldReturnTrue() { + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, + Utils.PROPERTY_PERMISSIONS_HUB_ENABLED, "true", true); + assertThat(mController.isAvailable()).isEqualTo(true); } @@ -111,6 +118,39 @@ public class RecentLocationAccessPreferenceControllerTest { assertThat(details.hasOnClickListeners()).isTrue(); } + @Test + public void updateState_whenAppListMoreThanThree_shouldDisplayTopThreeApps() { + final List accesses = createMockAccesses(6); + doReturn(accesses).when(mRecentLocationApps).getAppListSorted(); + mController.displayPreference(mScreen); + mController.updateState(mLayoutPreference); + + // The widget can display the top 3 apps from the list when there're more than 3. + final View app1View = mAppEntitiesHeaderView.findViewById(R.id.app1_view); + final ImageView appIconView1 = app1View.findViewById(R.id.app_icon); + final TextView appTitle1 = app1View.findViewById(R.id.app_title); + + assertThat(app1View.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(appIconView1.getDrawable()).isNotNull(); + assertThat(appTitle1.getText()).isEqualTo("appTitle0"); + + final View app2View = mAppEntitiesHeaderView.findViewById(R.id.app2_view); + final ImageView appIconView2 = app2View.findViewById(R.id.app_icon); + final TextView appTitle2 = app2View.findViewById(R.id.app_title); + + assertThat(app2View.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(appIconView2.getDrawable()).isNotNull(); + assertThat(appTitle2.getText()).isEqualTo("appTitle1"); + + final View app3View = mAppEntitiesHeaderView.findViewById(R.id.app3_view); + final ImageView appIconView3 = app3View.findViewById(R.id.app_icon); + final TextView appTitle3 = app3View.findViewById(R.id.app_title); + + assertThat(app3View.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(appIconView3.getDrawable()).isNotNull(); + assertThat(appTitle3.getText()).isEqualTo("appTitle2"); + } + private List createMockAccesses(int count) { final List accesses = new ArrayList<>(); for (int i = 0; i < count; i++) {