Support people search results

+ Introduce temporary workaround icon loading with URLConnection for play results

Manual: go/search-action-icon

Bug: 177223401
Test: Manual
Change-Id: I3b597faaad38e8cd1419065b80b359720baf52b0
This commit is contained in:
Samuel Fufa
2021-01-28 05:52:25 -06:00
parent d462965698
commit 1ab83de48e
17 changed files with 144 additions and 390 deletions

View File

@@ -15,12 +15,9 @@
*/
package com.android.launcher3.search;
import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_BADGE_FROM_ICON;
import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_BADGE_WITH_PACKAGE;
import static com.android.launcher3.model.data.SearchActionItemInfo.FLAG_PRIMARY_ICON_FROM_TITLE;
import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_BADGE_FROM_ICON;
import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE;
import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START;
import static com.android.launcher3.search.SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START_FOR_RESULT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -30,9 +27,14 @@ import android.app.search.SearchTargetEvent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.graphics.drawable.Drawable;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.AttributeSet;
@@ -45,6 +47,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
@@ -54,14 +57,25 @@ import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Themes;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.function.Consumer;
/**
* A {@link BubbleTextView} representing a single cell result in AllApps
*/
public class SearchResultIcon extends BubbleTextView implements SearchTargetHandler {
public class SearchResultIcon extends BubbleTextView implements
SearchTargetHandler, View.OnClickListener,
View.OnLongClickListener {
//Play store thumbnail process workaround
private final Rect mTempRect = new Rect();
private final Paint mIconPaint = new Paint();
private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
private final Launcher mLauncher;
@@ -99,7 +113,7 @@ public class SearchResultIcon extends BubbleTextView implements SearchTargetHand
* Applies {@link SearchTarget} to view. registers a consumer after a corresponding
* {@link ItemInfoWithIcon} is created
*/
public void applySearchTarget(SearchTarget searchTarget, List<SearchTarget> inlineItems,
public void apply(SearchTarget searchTarget, List<SearchTarget> inlineItems,
Consumer<ItemInfoWithIcon> cb) {
mOnItemInfoChanged = cb;
apply(searchTarget, inlineItems);
@@ -115,8 +129,8 @@ public class SearchResultIcon extends BubbleTextView implements SearchTargetHand
prepareUsingSearchAction(parentTarget);
mLongPressSupported = false;
} else {
prepareUsingApp(new ComponentName(parentTarget.getPackageName(),
parentTarget.getExtras().getString(SearchTargetUtil.EXTRA_CLASS)),
String className = parentTarget.getExtras().getString(SearchTargetUtil.EXTRA_CLASS);
prepareUsingApp(new ComponentName(parentTarget.getPackageName(), className),
parentTarget.getUserHandle());
mLongPressSupported = true;
}
@@ -136,14 +150,17 @@ public class SearchResultIcon extends BubbleTextView implements SearchTargetHand
//TODO: remove this after flags are introduced in SearchAction. Settings results require
// startActivityForResult
boolean isSettingsResult = searchTarget.getResultType() == ResultType.SETTING;
if ((extras != null && extras.getBoolean(BUNDLE_EXTRA_SHOULD_START_FOR_RESULT))
if ((extras != null && extras.getBoolean(
SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START_FOR_RESULT))
|| isSettingsResult) {
itemInfo.setFlags(SearchActionItemInfo.FLAG_SHOULD_START_FOR_RESULT);
} else if (extras != null && extras.getBoolean(BUNDLE_EXTRA_SHOULD_START)) {
} else if (extras != null && extras.getBoolean(
SearchTargetUtil.BUNDLE_EXTRA_SHOULD_START)) {
itemInfo.setFlags(SearchActionItemInfo.FLAG_SHOULD_START);
}
if (extras != null && extras.getBoolean(BUNDLE_EXTRA_BADGE_FROM_ICON)) {
itemInfo.setFlags(FLAG_BADGE_FROM_ICON);
if (extras != null && extras.getBoolean(
SearchTargetUtil.BUNDLE_EXTRA_BADGE_WITH_PACKAGE)) {
itemInfo.setFlags(FLAG_BADGE_WITH_PACKAGE);
}
if (extras != null && extras.getBoolean(BUNDLE_EXTRA_PRIMARY_ICON_FROM_TITLE)) {
itemInfo.setFlags(FLAG_PRIMARY_ICON_FROM_TITLE);
@@ -154,39 +171,76 @@ public class SearchResultIcon extends BubbleTextView implements SearchTargetHand
MODEL_EXECUTOR.post(() -> {
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
Icon icon = searchTarget.getSearchAction().getIcon();
Drawable d;
// This bitmapInfo can be used as main icon or as a badge
BitmapInfo bitmapInfo;
if (icon == null) {
PackageItemInfo pkgInfo = new PackageItemInfo(searchTarget.getPackageName());
pkgInfo.user = searchTarget.getUserHandle();
appState.getIconCache().getTitleAndIconForApp(pkgInfo, false);
bitmapInfo = pkgInfo.bitmap;
} else {
d = itemInfo.getIcon().loadDrawable(getContext());
bitmapInfo = li.createBadgedIconBitmap(d, itemInfo.user,
Build.VERSION.SDK_INT);
}
BitmapInfo bitmapMainIcon;
BitmapInfo pkgBitmap = getPackageBitmap(appState, searchTarget);
if (itemInfo.hasFlags(FLAG_PRIMARY_ICON_FROM_TITLE)) {
bitmapMainIcon = li.createIconBitmap(
String.valueOf(itemInfo.title.charAt(0)),
bitmapInfo.color);
// create a bitmap with first char if FLAG_PRIMARY_ICON_FROM_TITLE is set
itemInfo.bitmap = li.createIconBitmap(String.valueOf(itemInfo.title.charAt(0)),
pkgBitmap.color);
} else if (icon == null) {
// Use default icon from package name
itemInfo.bitmap = pkgBitmap;
} else {
bitmapMainIcon = bitmapInfo;
}
if (itemInfo.hasFlags(FLAG_BADGE_FROM_ICON)) {
itemInfo.bitmap = li.badgeBitmap(bitmapMainIcon.icon, bitmapInfo);
} else {
itemInfo.bitmap = bitmapInfo;
boolean isPlayResult = searchTarget.getResultType() == ResultType.PLAY;
if (isPlayResult) {
Bitmap b = getPlayResultBitmap(searchAction.getIcon());
itemInfo.bitmap = b == null
? BitmapInfo.LOW_RES_INFO : BitmapInfo.fromBitmap(b);
} else {
itemInfo.bitmap = li.createBadgedIconBitmap(icon.loadDrawable(getContext()),
itemInfo.user, false);
}
}
// badge with package name
if (itemInfo.hasFlags(FLAG_BADGE_WITH_PACKAGE) && itemInfo.bitmap != pkgBitmap) {
itemInfo.bitmap = li.badgeBitmap(itemInfo.bitmap.icon, pkgBitmap);
}
}
MAIN_EXECUTOR.post(() -> applyFromSearchActionItemInfo(itemInfo));
});
}
private static BitmapInfo getPackageBitmap(LauncherAppState appState, SearchTarget target) {
PackageItemInfo pkgInfo = new PackageItemInfo(target.getPackageName());
pkgInfo.user = target.getUserHandle();
appState.getIconCache().getTitleAndIconForApp(pkgInfo, false);
return pkgInfo.bitmap;
}
private Bitmap getPlayResultBitmap(Icon icon) {
try {
int iconSize = getIconSize();
URL url = new URL(icon.getUri().toString());
URLConnection con = url.openConnection();
con.addRequestProperty("Cache-Control", "max-age: 0");
con.setUseCaches(true);
Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
return getRoundedBitmap(Bitmap.createScaledBitmap(bitmap, iconSize, iconSize, false));
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Bitmap getRoundedBitmap(Bitmap bitmap) {
final int iconSize = bitmap.getWidth();
final float radius = Themes.getDialogCornerRadius(getContext());
return BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
mTempRect.set(0, 0, iconSize, iconSize);
final RectF rectF = new RectF(mTempRect);
mIconPaint.setAntiAlias(true);
mIconPaint.reset();
canvas.drawARGB(0, 0, 0, 0);
mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
});
}
private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
AppInfo appInfo = appsStore.getApp(new ComponentKey(componentName, userHandle));
@@ -233,7 +287,6 @@ public class SearchResultIcon extends BubbleTextView implements SearchTargetHand
}
private void notifyItemInfoChanged(ItemInfoWithIcon itemInfoWithIcon) {
if (mOnItemInfoChanged != null) {
mOnItemInfoChanged.accept(itemInfoWithIcon);