1 /* 2 * Copyright (C) 2016 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.settings.display; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.content.pm.ResolveInfo; 23 import android.content.res.TypedArray; 24 import android.graphics.drawable.Drawable; 25 import android.os.UserHandle; 26 import android.util.AttributeSet; 27 import android.util.IconDrawableFactory; 28 import android.util.Log; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.widget.ArrayAdapter; 32 import android.widget.GridView; 33 import android.widget.ImageView; 34 35 import androidx.annotation.VisibleForTesting; 36 import androidx.core.util.Preconditions; 37 38 import com.android.settings.R; 39 40 import java.util.ArrayList; 41 import java.util.Collections; 42 import java.util.List; 43 44 /** 45 * The grid view for displaying the application entries. 46 * 47 * <p> The attribute value {@code appCount} from XML should be more than or equal to 1, otherwise 48 * throws an {@link IllegalArgumentException}.</p> 49 */ 50 public class AppGridView extends GridView { 51 private static final String TAG = "AppGridView"; 52 53 private static final int APP_COUNT_DEF_VALUE = 6; 54 private int mAppCount = APP_COUNT_DEF_VALUE; 55 AppGridView(Context context)56 public AppGridView(Context context) { 57 super(context); 58 init(context); 59 } 60 AppGridView(Context context, AttributeSet attrs)61 public AppGridView(Context context, AttributeSet attrs) { 62 super(context, attrs); 63 applyAttributeSet(context, attrs); 64 init(context); 65 } 66 AppGridView(Context context, AttributeSet attrs, int defStyleAttr)67 public AppGridView(Context context, AttributeSet attrs, int defStyleAttr) { 68 super(context, attrs, defStyleAttr); 69 applyAttributeSet(context, attrs); 70 init(context); 71 } 72 AppGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleResId)73 public AppGridView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleResId) { 74 super(context, attrs, defStyleAttr, defStyleResId); 75 applyAttributeSet(context, attrs); 76 init(context); 77 } 78 init(Context context)79 private void init(Context context) { 80 setAdapter(new AppsAdapter(context, R.layout.screen_zoom_preview_app_icon, 81 android.R.id.text1, android.R.id.icon1, mAppCount)); 82 } 83 applyAttributeSet(Context context, AttributeSet attrs)84 private void applyAttributeSet(Context context, AttributeSet attrs) { 85 final TypedArray styledAttrs = 86 context.obtainStyledAttributes(attrs, R.styleable.AppGridView); 87 mAppCount = 88 styledAttrs.getInteger(R.styleable.AppGridView_appCount, APP_COUNT_DEF_VALUE); 89 Preconditions.checkArgument(mAppCount >= 1, 90 /* errorMessage= */ "App count may not be negative or zero"); 91 92 styledAttrs.recycle(); 93 } 94 95 /** 96 * Loads application labels and icons. 97 */ 98 @VisibleForTesting 99 public static class AppsAdapter extends ArrayAdapter<ActivityEntry> { 100 private final PackageManager mPackageManager; 101 private final int mIconResId; 102 private final int mAppCount; 103 AppsAdapter(Context context, int layout, int textResId, int iconResId, int appCount)104 public AppsAdapter(Context context, int layout, int textResId, int iconResId, 105 int appCount) { 106 super(context, layout, textResId); 107 108 mIconResId = iconResId; 109 mPackageManager = context.getPackageManager(); 110 mAppCount = appCount; 111 112 loadAllApps(); 113 } 114 115 @Override getView(int position, View convertView, ViewGroup parent)116 public View getView(int position, View convertView, ViewGroup parent) { 117 final View view = super.getView(position, convertView, parent); 118 final ActivityEntry entry = getItem(position); 119 final ImageView iconView = view.findViewById(mIconResId); 120 iconView.setImageDrawable(entry.getIcon()); 121 return view; 122 } 123 124 @Override hasStableIds()125 public boolean hasStableIds() { 126 return true; 127 } 128 129 @Override getItemId(int position)130 public long getItemId(int position) { 131 return position; 132 } 133 134 @Override isEnabled(int position)135 public boolean isEnabled(int position) { 136 return false; 137 } 138 loadAllApps()139 private void loadAllApps() { 140 final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); 141 mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); 142 143 final PackageManager pm = mPackageManager; 144 final ArrayList<ActivityEntry> results = new ArrayList<>(); 145 final List<ResolveInfo> infos = pm.queryIntentActivities(mainIntent, 0); 146 147 if (mAppCount > infos.size()) { 148 Log.d(TAG, "Visible app icon count does not meet the target count."); 149 } 150 151 final IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(getContext()); 152 for (ResolveInfo info : infos) { 153 final CharSequence label = info.loadLabel(pm); 154 if (label != null) { 155 results.add(new ActivityEntry(info, label.toString(), iconFactory)); 156 } 157 if (results.size() >= mAppCount) { 158 break; 159 } 160 } 161 162 Collections.sort(results); 163 164 addAll(results); 165 } 166 } 167 168 /** 169 * Class used for caching the activity label and icon. 170 */ 171 @VisibleForTesting 172 public static class ActivityEntry implements Comparable<ActivityEntry> { 173 174 public final ResolveInfo info; 175 public final String label; 176 private final IconDrawableFactory mIconFactory; 177 private final int mUserId; 178 ActivityEntry(ResolveInfo info, String label, IconDrawableFactory iconFactory)179 public ActivityEntry(ResolveInfo info, String label, IconDrawableFactory iconFactory) { 180 this.info = info; 181 this.label = label; 182 mIconFactory = iconFactory; 183 mUserId = UserHandle.myUserId(); 184 } 185 186 @Override compareTo(ActivityEntry entry)187 public int compareTo(ActivityEntry entry) { 188 return label.compareToIgnoreCase(entry.label); 189 } 190 191 @Override toString()192 public String toString() { 193 return label; 194 } 195 getIcon()196 public Drawable getIcon() { 197 return mIconFactory.getBadgedIcon( 198 info.activityInfo, info.activityInfo.applicationInfo, mUserId); 199 } 200 } 201 } 202