diff --git a/res/values/attrs.xml b/res/values/attrs.xml index 579ac35e46b..e4822d8a6b9 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -103,6 +103,11 @@ + + + + + diff --git a/src/com/android/settings/display/AppGridView.java b/src/com/android/settings/display/AppGridView.java index cda14458d0d..a0f2a638c6d 100644 --- a/src/com/android/settings/display/AppGridView.java +++ b/src/com/android/settings/display/AppGridView.java @@ -20,10 +20,12 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.util.AttributeSet; import android.util.IconDrawableFactory; +import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; @@ -31,6 +33,7 @@ import android.widget.GridView; import android.widget.ImageView; import androidx.annotation.VisibleForTesting; +import androidx.core.util.Preconditions; import com.android.settings.R; @@ -38,7 +41,18 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +/** + * The grid view for displaying the application entries. + * + *

The attribute value {@code appCount} from XML should be more than or equal to 1, otherwise + * throws an {@link IllegalArgumentException}.

+ */ public class AppGridView extends GridView { + private static final String TAG = "AppGridView"; + + private static final int APP_COUNT_DEF_VALUE = 6; + private int mAppCount = APP_COUNT_DEF_VALUE; + public AppGridView(Context context) { super(context); init(context); @@ -46,24 +60,36 @@ public class AppGridView extends GridView { public AppGridView(Context context, AttributeSet attrs) { super(context, attrs); + applyAttributeSet(context, attrs); init(context); } public AppGridView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + applyAttributeSet(context, attrs); init(context); } public AppGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleResId) { super(context, attrs, defStyleAttr, defStyleResId); - + applyAttributeSet(context, attrs); init(context); - } private void init(Context context) { setAdapter(new AppsAdapter(context, R.layout.screen_zoom_preview_app_icon, - android.R.id.text1, android.R.id.icon1)); + android.R.id.text1, android.R.id.icon1, mAppCount)); + } + + private void applyAttributeSet(Context context, AttributeSet attrs) { + final TypedArray styledAttrs = + context.obtainStyledAttributes(attrs, R.styleable.AppGridView); + mAppCount = + styledAttrs.getInteger(R.styleable.AppGridView_appCount, APP_COUNT_DEF_VALUE); + Preconditions.checkArgument(mAppCount >= 1, + /* errorMessage= */ "App count may not be negative or zero"); + + styledAttrs.recycle(); } /** @@ -73,12 +99,15 @@ public class AppGridView extends GridView { public static class AppsAdapter extends ArrayAdapter { private final PackageManager mPackageManager; private final int mIconResId; + private final int mAppCount; - public AppsAdapter(Context context, int layout, int textResId, int iconResId) { + public AppsAdapter(Context context, int layout, int textResId, int iconResId, + int appCount) { super(context, layout, textResId); mIconResId = iconResId; mPackageManager = context.getPackageManager(); + mAppCount = appCount; loadAllApps(); } @@ -108,20 +137,24 @@ public class AppGridView extends GridView { } private void loadAllApps() { - final int needAppCount = 6; final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); final PackageManager pm = mPackageManager; final ArrayList results = new ArrayList<>(); final List infos = pm.queryIntentActivities(mainIntent, 0); + + if (mAppCount > infos.size()) { + Log.d(TAG, "Visible app icon count does not meet the target count."); + } + final IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(getContext()); for (ResolveInfo info : infos) { final CharSequence label = info.loadLabel(pm); if (label != null) { results.add(new ActivityEntry(info, label.toString(), iconFactory)); } - if (results.size() >= needAppCount) { + if (results.size() >= mAppCount) { break; } } diff --git a/tests/robotests/src/com/android/settings/display/AppGridViewTest.java b/tests/robotests/src/com/android/settings/display/AppGridViewTest.java index e7daac89750..22e1cc30077 100644 --- a/tests/robotests/src/com/android/settings/display/AppGridViewTest.java +++ b/tests/robotests/src/com/android/settings/display/AppGridViewTest.java @@ -18,26 +18,39 @@ package com.android.settings.display; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; +import android.util.AttributeSet; import android.util.IconDrawableFactory; +import com.android.settings.R; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; +import java.util.ArrayList; +import java.util.List; + +/** + * Tests for {@link AppGridView}. + */ @RunWith(RobolectricTestRunner.class) public class AppGridViewTest { @@ -87,4 +100,72 @@ public class AppGridViewTest { assertThat(entry1.compareTo(entry2)).isEqualTo(0); assertThat(entry1.compareTo(entry3)).isNotEqualTo(0); } + + @Test + public void noAppCountAttribute_matchListSize() { + final int appCountFromSystem = 8; + setUpResolveInfos(appCountFromSystem); + + final AppGridView appGridView = new AppGridView(mContext, /* attrs= */ null); + + assertThat(appGridView.getAdapter().getCount()).isEqualTo(/* expected= */ 6); + } + + @Test + public void setAppCountAttribute_matchListSize() { + final int appCountFromSystem = 8; + final int appCountFromAttr = 7; + setUpResolveInfos(appCountFromSystem); + + final AppGridView appGridView = + new AppGridView(mContext, createAttributeSet(appCountFromAttr)); + + assertThat(appGridView.getAdapter().getCount()).isEqualTo(appCountFromAttr); + } + + @Test(expected = IllegalArgumentException.class) + public void setAppCountAttribute_belowLowerBound_matchListSize() { + final int appCountFromSystem = 9; + final int appCountFromAttr = -1; // The num is just for the test. + setUpResolveInfos(appCountFromSystem); + + new AppGridView(mContext, createAttributeSet(appCountFromAttr)); + } + + @Test + public void setAppCountAttribute_aboveUpperBound_matchListSize() { + final int appCountFromSystem = 10; + final int appCountFromAttr = 15; + setUpResolveInfos(appCountFromSystem); + + final AppGridView appGridView = + new AppGridView(mContext, createAttributeSet(appCountFromAttr)); + + assertThat(appGridView.getAdapter().getCount()).isEqualTo(appCountFromSystem); + } + + private AttributeSet createAttributeSet(int appCount) { + return Robolectric.buildAttributeSet() + .addAttribute(R.attr.appCount, String.valueOf(appCount)) + .build(); + } + + private void setUpResolveInfos(int appCount) { + when(mContext.getPackageManager().queryIntentActivities( + any(Intent.class), anyInt())) + .thenReturn(createFakeResolveInfos(appCount)); + } + + private List createFakeResolveInfos(int count) { + final List list = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + final ResolveInfo info = new ResolveInfo(); + info.nonLocalizedLabel = String.valueOf(i); + + list.add(info); + } + + return list; + } }