Merging from ub-launcher3-master @ build 6925377
Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-master_master_6925377.html Change-Id: I928b100c8f41abff34047df69d988622123f9939
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
package com.android.launcher3;
|
||||
|
||||
import static com.android.launcher3.FastBitmapDrawable.newIcon;
|
||||
import static com.android.launcher3.graphics.IconShape.getShape;
|
||||
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
|
||||
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
||||
|
||||
@@ -27,15 +28,18 @@ import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.BlurMaskFilter;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Property;
|
||||
@@ -50,6 +54,8 @@ import androidx.core.graphics.ColorUtils;
|
||||
|
||||
import com.android.launcher3.Launcher.OnResumeCallback;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.allapps.AllAppsSectionDecorator;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
@@ -60,6 +66,7 @@ import com.android.launcher3.graphics.PreloadIconDrawable;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.icons.IconCache.IconLoadRequest;
|
||||
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
@@ -79,7 +86,7 @@ import java.text.NumberFormat;
|
||||
* too aggressive.
|
||||
*/
|
||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
|
||||
IconLabelDotView, DraggableView, Reorderable {
|
||||
IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
|
||||
|
||||
private static final int DISPLAY_WORKSPACE = 0;
|
||||
private static final int DISPLAY_ALL_APPS = 1;
|
||||
@@ -87,6 +94,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
private static final int DISPLAY_HERO_APP = 5;
|
||||
|
||||
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
|
||||
private static final float HIGHLIGHT_SCALE = 1.16f;
|
||||
|
||||
|
||||
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
|
||||
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
|
||||
@@ -95,6 +104,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
private float mScaleForReorderBounce = 1f;
|
||||
|
||||
protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
private final Path mHighlightPath = new Path();
|
||||
protected int mHighlightColor = Color.TRANSPARENT;
|
||||
private final BlurMaskFilter mHighlightShadowFilter;
|
||||
|
||||
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
|
||||
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
|
||||
@Override
|
||||
@@ -208,6 +222,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
setEllipsize(TruncateAt.END);
|
||||
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
|
||||
setTextAlpha(1f);
|
||||
|
||||
int shadowSize = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.blur_size_click_shadow);
|
||||
mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -421,8 +440,38 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
|
||||
int count = canvas.save();
|
||||
drawFocusHighlight(canvas);
|
||||
canvas.restoreToCount(count);
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
drawDotIfNecessary(canvas);
|
||||
}
|
||||
|
||||
protected void drawFocusHighlight(Canvas canvas) {
|
||||
boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
|
||||
((ItemInfo) getTag()).user);
|
||||
float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
|
||||
canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
|
||||
float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
|
||||
mHighlightPath.reset();
|
||||
mHighlightPaint.reset();
|
||||
getIconBounds(mDotParams.iconBounds);
|
||||
getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
|
||||
outlineSize / 2);
|
||||
if (isBadged) {
|
||||
float borderSize = outlineSize - getIconSize();
|
||||
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
|
||||
float badgeInset = outlineSize - badgeSize;
|
||||
getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
|
||||
mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
|
||||
}
|
||||
mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
|
||||
mHighlightPaint.setColor(mDotParams.color);
|
||||
canvas.drawPath(mHighlightPath, mHighlightPaint);
|
||||
mHighlightPaint.setMaskFilter(null);
|
||||
mHighlightPaint.setColor(mHighlightColor);
|
||||
canvas.drawPath(mHighlightPath, mHighlightPaint);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -627,7 +676,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
/**
|
||||
* Sets the icon for this view based on the layout direction.
|
||||
*/
|
||||
private void setIcon(Drawable icon) {
|
||||
protected void setIcon(Drawable icon) {
|
||||
if (mIsIconVisible) {
|
||||
applyCompoundDrawables(icon);
|
||||
}
|
||||
@@ -787,10 +836,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
@Override
|
||||
public SafeCloseable prepareDrawDragView() {
|
||||
int highlightColor = mHighlightColor;
|
||||
mHighlightColor = Color.TRANSPARENT;
|
||||
resetIconScale();
|
||||
setForceHideDot(true);
|
||||
return () -> {
|
||||
};
|
||||
return () -> mHighlightColor = highlightColor;
|
||||
}
|
||||
|
||||
private void resetIconScale() {
|
||||
@@ -827,4 +877,17 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
});
|
||||
iconUpdateAnimation.start();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void decorate(int color) {
|
||||
mHighlightColor = color;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDecoration() {
|
||||
mHighlightColor = Color.TRANSPARENT;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ import android.view.ViewDebug;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* View class that represents the bottom row of the home screen.
|
||||
*/
|
||||
@@ -34,6 +38,7 @@ public class Hotseat extends CellLayout implements Insettable {
|
||||
private boolean mHasVerticalHotseat;
|
||||
private Workspace mWorkspace;
|
||||
private boolean mSendTouchToWorkspace;
|
||||
@Nullable private Consumer<Boolean> mOnVisibilityAggregatedCallback;
|
||||
|
||||
public Hotseat(Context context) {
|
||||
this(context, null);
|
||||
@@ -129,4 +134,18 @@ public class Hotseat extends CellLayout implements Insettable {
|
||||
}
|
||||
return event.getY() > getCellHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisibilityAggregated(boolean isVisible) {
|
||||
super.onVisibilityAggregated(isVisible);
|
||||
|
||||
if (mOnVisibilityAggregatedCallback != null) {
|
||||
mOnVisibilityAggregatedCallback.accept(isVisible);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets a callback to be called onVisibilityAggregated */
|
||||
public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
|
||||
mOnVisibilityAggregatedCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ public class InvariantDeviceProfile {
|
||||
DisplayController.getDefaultDisplay(context).getInfo(),
|
||||
getPredefinedDeviceProfiles(context, gridName));
|
||||
|
||||
Info myInfo = new Info(context, display);
|
||||
Info myInfo = new Info(display);
|
||||
DisplayOption myDisplayOption = invDistWeightedInterpolate(
|
||||
myInfo, getPredefinedDeviceProfiles(context, gridName));
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
@@ -44,6 +45,7 @@ import android.graphics.RectF;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.InsetDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.Handler;
|
||||
@@ -83,6 +85,7 @@ import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@@ -661,6 +664,18 @@ public final class Utilities {
|
||||
return slop * slop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a content provider
|
||||
*/
|
||||
public static ContentObserver newContentObserver(Handler handler, Consumer<Uri> command) {
|
||||
return new ContentObserver(handler) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
command.accept(uri);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class FixedSizeEmptyDrawable extends ColorDrawable {
|
||||
|
||||
private final int mSize;
|
||||
|
||||
@@ -2078,40 +2078,40 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
mLastReorderY = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
|
||||
* coordinate space. The argument xy is modified with the return result.
|
||||
*/
|
||||
private void mapPointFromSelfToChild(View v, float[] xy) {
|
||||
xy[0] = xy[0] - v.getLeft();
|
||||
xy[1] = xy[1] - v.getTop();
|
||||
}
|
||||
/*
|
||||
*
|
||||
* Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
|
||||
* coordinate space. The argument xy is modified with the return result.
|
||||
*/
|
||||
private void mapPointFromSelfToChild(View v, float[] xy) {
|
||||
xy[0] = xy[0] - v.getLeft();
|
||||
xy[1] = xy[1] - v.getTop();
|
||||
}
|
||||
|
||||
boolean isPointInSelfOverHotseat(int x, int y) {
|
||||
mTempFXY[0] = x;
|
||||
mTempFXY[1] = y;
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
|
||||
View hotseat = mLauncher.getHotseat();
|
||||
return mTempFXY[0] >= hotseat.getLeft() &&
|
||||
mTempFXY[0] <= hotseat.getRight() &&
|
||||
mTempFXY[1] >= hotseat.getTop() &&
|
||||
mTempFXY[1] <= hotseat.getBottom();
|
||||
}
|
||||
boolean isPointInSelfOverHotseat(int x, int y) {
|
||||
mTempFXY[0] = x;
|
||||
mTempFXY[1] = y;
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
|
||||
View hotseat = mLauncher.getHotseat();
|
||||
return mTempFXY[0] >= hotseat.getLeft()
|
||||
&& mTempFXY[0] <= hotseat.getRight()
|
||||
&& mTempFXY[1] >= hotseat.getTop()
|
||||
&& mTempFXY[1] <= hotseat.getBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
|
||||
* @param layout either hotseat of a page in workspace
|
||||
* @param xy the point location in workspace co-ordinate space
|
||||
*/
|
||||
private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
|
||||
if (mLauncher.isHotseatLayout(layout)) {
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
|
||||
mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
|
||||
} else {
|
||||
mapPointFromSelfToChild(layout, xy);
|
||||
}
|
||||
}
|
||||
private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
|
||||
if (mLauncher.isHotseatLayout(layout)) {
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
|
||||
mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
|
||||
} else {
|
||||
mapPointFromSelfToChild(layout, xy);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDragWidget(DragObject d) {
|
||||
return (d.dragInfo instanceof LauncherAppWidgetInfo ||
|
||||
@@ -2393,9 +2393,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||
spanY = mDragInfo.spanY;
|
||||
}
|
||||
|
||||
final int container = mLauncher.isHotseatLayout(cellLayout) ?
|
||||
LauncherSettings.Favorites.CONTAINER_HOTSEAT :
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
final int container = mLauncher.isHotseatLayout(cellLayout)
|
||||
? LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
: LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
final int screenId = getIdForScreen(cellLayout);
|
||||
if (!mLauncher.isHotseatLayout(cellLayout)
|
||||
&& screenId != getScreenIdForPageIndex(mCurrentPage)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
package com.android.launcher3.allapps;
|
||||
|
||||
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import static com.android.launcher3.allapps.AllAppsGridAdapter.SearchAdapterItem;
|
||||
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
|
||||
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
|
||||
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
|
||||
@@ -57,6 +57,7 @@ import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.InsettableFrameLayout;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.keyboard.FocusedItemDecorator;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
@@ -67,9 +68,7 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||
import com.android.launcher3.views.SpringRelativeLayout;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.function.IntConsumer;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
|
||||
/**
|
||||
* The all apps view container.
|
||||
@@ -546,13 +545,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
|
||||
}
|
||||
AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
|
||||
if (focusedItem instanceof AdapterItemWithPayload) {
|
||||
IntConsumer onSelection =
|
||||
((AdapterItemWithPayload) focusedItem).getSelectionHandler();
|
||||
if (onSelection != null) {
|
||||
onSelection.accept(SearchTargetEvent.QUICK_SELECT);
|
||||
return true;
|
||||
}
|
||||
if (focusedItem instanceof SearchAdapterItem) {
|
||||
SearchTarget searchTarget = ((SearchAdapterItem) focusedItem).getSearchTarget();
|
||||
SearchEventTracker.INSTANCE.get(getContext()).quickSelect(searchTarget);
|
||||
}
|
||||
if (focusedItem.appInfo != null) {
|
||||
ItemInfo itemInfo = focusedItem.appInfo;
|
||||
|
||||
@@ -20,8 +20,6 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -37,29 +35,22 @@ import androidx.annotation.Nullable;
|
||||
import androidx.core.view.accessibility.AccessibilityEventCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.widget.SliceLiveData;
|
||||
import androidx.slice.widget.SliceView;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
|
||||
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.views.HeroSearchResultView;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.views.SearchSliceWrapper;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
/**
|
||||
* The grid view adapter of all the apps.
|
||||
@@ -100,9 +91,11 @@ public class AllAppsGridAdapter extends
|
||||
|
||||
public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
|
||||
|
||||
public static final int VIEW_TYPE_SEARCH_ICON = 1 << 14;
|
||||
|
||||
// Common view type masks
|
||||
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
|
||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_SEARCH_ICON;
|
||||
|
||||
/**
|
||||
* ViewHolder for each icon.
|
||||
@@ -192,56 +185,25 @@ public class AllAppsGridAdapter extends
|
||||
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
|
||||
|| viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|
||||
|| viewType == VIEW_TYPE_SEARCH_ICON_ROW
|
||||
|| viewType == VIEW_TYPE_SEARCH_ICON
|
||||
|| viewType == VIEW_TYPE_SEARCH_SUGGEST;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension of AdapterItem that contains an extra payload specific to item
|
||||
*
|
||||
* @param <T> Play load Type
|
||||
*/
|
||||
public static class AdapterItemWithPayload<T> extends AdapterItem {
|
||||
private T mPayload;
|
||||
private String mSearchSessionId;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private IntConsumer mSelectionHandler;
|
||||
public static class SearchAdapterItem extends AdapterItem {
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public AllAppsSearchPlugin getPlugin() {
|
||||
return mPlugin;
|
||||
}
|
||||
|
||||
public void setPlugin(AllAppsSearchPlugin plugin) {
|
||||
mPlugin = plugin;
|
||||
}
|
||||
|
||||
public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) {
|
||||
mPayload = payload;
|
||||
public SearchAdapterItem(SearchTarget searchTarget, int type) {
|
||||
mSearchTarget = searchTarget;
|
||||
viewType = type;
|
||||
mPlugin = plugin;
|
||||
}
|
||||
|
||||
public void setSelectionHandler(IntConsumer runnable) {
|
||||
mSelectionHandler = runnable;
|
||||
public SearchTarget getSearchTarget() {
|
||||
return mSearchTarget;
|
||||
}
|
||||
|
||||
public void setSearchSessionId(String searchSessionId) {
|
||||
mSearchSessionId = searchSessionId;
|
||||
}
|
||||
|
||||
public String getSearchSessionId() {
|
||||
return mSearchSessionId;
|
||||
}
|
||||
|
||||
public IntConsumer getSelectionHandler() {
|
||||
return mSelectionHandler;
|
||||
}
|
||||
|
||||
public T getPayload() {
|
||||
return mPayload;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,11 +388,8 @@ public class AllAppsGridAdapter extends
|
||||
R.layout.all_apps_icon, parent, false);
|
||||
icon.setLongPressTimeoutFactor(1f);
|
||||
icon.setOnFocusChangeListener(mIconFocusListener);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
icon.setOnClickListener(mOnIconClickListener);
|
||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
||||
}
|
||||
|
||||
icon.setOnClickListener(mOnIconClickListener);
|
||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
||||
// Ensure the all apps icon height matches the workspace icons in portrait mode.
|
||||
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
||||
return new ViewHolder(icon);
|
||||
@@ -446,6 +405,9 @@ public class AllAppsGridAdapter extends
|
||||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.all_apps_divider, parent, false));
|
||||
case VIEW_TYPE_SEARCH_ICON:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
R.layout.search_result_icon, parent, false));
|
||||
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
||||
return new ViewHolder(
|
||||
mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
|
||||
@@ -480,6 +442,10 @@ public class AllAppsGridAdapter extends
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()
|
||||
&& holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
|
||||
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
|
||||
}
|
||||
switch (holder.getItemViewType()) {
|
||||
case VIEW_TYPE_ICON:
|
||||
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
|
||||
@@ -487,34 +453,6 @@ public class AllAppsGridAdapter extends
|
||||
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
||||
icon.reset();
|
||||
icon.applyFromApplicationInfo(info);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
break;
|
||||
}
|
||||
//TODO: replace with custom TopHitBubbleTextView with support for both shortcut
|
||||
// and apps
|
||||
if (adapterItem instanceof AdapterItemWithPayload) {
|
||||
AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
|
||||
item.setSelectionHandler(type -> {
|
||||
SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
|
||||
type, item.position, item.getSearchSessionId());
|
||||
e.bundle = HeroSearchResultView.getAppBundle(info);
|
||||
if (item.getPlugin() != null) {
|
||||
item.getPlugin().notifySearchTargetEvent(e);
|
||||
}
|
||||
});
|
||||
icon.setOnClickListener(view -> {
|
||||
item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
|
||||
mOnIconClickListener.onClick(view);
|
||||
});
|
||||
icon.setOnLongClickListener(view -> {
|
||||
item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
|
||||
return mOnIconLongClickListener.onLongClick(view);
|
||||
});
|
||||
}
|
||||
else {
|
||||
icon.setOnClickListener(mOnIconClickListener);
|
||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
||||
}
|
||||
break;
|
||||
case VIEW_TYPE_EMPTY_SEARCH:
|
||||
TextView emptyViewText = (TextView) holder.itemView;
|
||||
@@ -532,39 +470,25 @@ public class AllAppsGridAdapter extends
|
||||
break;
|
||||
case VIEW_TYPE_SEARCH_SLICE:
|
||||
SliceView sliceView = (SliceView) holder.itemView;
|
||||
AdapterItemWithPayload<Uri> slicePayload =
|
||||
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
|
||||
sliceView.setOnSliceActionListener((info1, s) -> {
|
||||
if (slicePayload.getPlugin() != null) {
|
||||
SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
|
||||
SearchTarget.ItemType.SETTINGS_SLICE,
|
||||
SearchTargetEvent.CHILD_SELECT, slicePayload.position,
|
||||
slicePayload.getSearchSessionId());
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
|
||||
slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
});
|
||||
try {
|
||||
LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
|
||||
slicePayload.getPayload());
|
||||
liveData.observe((Launcher) mLauncher, sliceView);
|
||||
sliceView.setTag(liveData);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get(
|
||||
position);
|
||||
SearchTarget searchTarget = slicePayload.getSearchTarget();
|
||||
sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget));
|
||||
|
||||
break;
|
||||
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
||||
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
||||
case VIEW_TYPE_SEARCH_HERO_APP:
|
||||
case VIEW_TYPE_SEARCH_ROW:
|
||||
case VIEW_TYPE_SEARCH_ICON:
|
||||
case VIEW_TYPE_SEARCH_ICON_ROW:
|
||||
case VIEW_TYPE_SEARCH_PEOPLE:
|
||||
case VIEW_TYPE_SEARCH_THUMBNAIL:
|
||||
case VIEW_TYPE_SEARCH_SUGGEST:
|
||||
AdapterItemWithPayload item =
|
||||
(AdapterItemWithPayload) mApps.getAdapterItems().get(position);
|
||||
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
|
||||
payloadResultView.setup(item);
|
||||
SearchAdapterItem item =
|
||||
(SearchAdapterItem) mApps.getAdapterItems().get(position);
|
||||
SearchTargetHandler payloadResultView = (SearchTargetHandler) holder.itemView;
|
||||
payloadResultView.applySearchTarget(item.getSearchTarget());
|
||||
break;
|
||||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||
// nothing to do
|
||||
@@ -576,17 +500,15 @@ public class AllAppsGridAdapter extends
|
||||
public void onViewRecycled(@NonNull ViewHolder holder) {
|
||||
super.onViewRecycled(holder);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
|
||||
if (holder.itemView instanceof BubbleTextView) {
|
||||
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
||||
icon.setOnClickListener(null);
|
||||
icon.setOnLongClickListener(null);
|
||||
} else if (holder.itemView instanceof SliceView) {
|
||||
if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
|
||||
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
|
||||
}
|
||||
if (holder.itemView instanceof SliceView) {
|
||||
SliceView sliceView = (SliceView) holder.itemView;
|
||||
sliceView.setOnSliceActionListener(null);
|
||||
if (sliceView.getTag() instanceof LiveData) {
|
||||
LiveData sliceLiveData = (LiveData) sliceView.getTag();
|
||||
sliceLiveData.removeObservers((Launcher) mLauncher);
|
||||
if (sliceView.getTag() instanceof SearchSliceWrapper) {
|
||||
((SearchSliceWrapper) sliceView.getTag()).destroy();
|
||||
}
|
||||
sliceView.setTag(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,14 +20,16 @@ import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
|
||||
public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
||||
|
||||
final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
|
||||
final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
|
||||
final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
|
||||
static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
|
||||
static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
|
||||
static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
|
||||
|
||||
public AllAppsPagedView(Context context) {
|
||||
public AllAppsPagedView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
@@ -37,6 +39,10 @@ public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
||||
|
||||
public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0
|
||||
: context.getResources().getDimensionPixelOffset(
|
||||
R.dimen.all_apps_header_top_padding);
|
||||
setPadding(0, topPadding, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
@@ -45,6 +46,8 @@ import java.util.List;
|
||||
* A RecyclerView with custom fast scroll support for the all apps view.
|
||||
*/
|
||||
public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||
private static final String TAG = "AllAppsContainerView";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private AlphabeticalAppsList mApps;
|
||||
private final int mNumAppsPerRow;
|
||||
@@ -131,7 +134,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
|
||||
mEmptySearchBackground.draw(c);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
|
||||
}
|
||||
super.onDraw(c);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import androidx.core.graphics.ColorUtils;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
|
||||
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
@@ -53,6 +54,9 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||
int i = 0;
|
||||
while (i < itemCount) {
|
||||
View view = parent.getChildAt(i);
|
||||
if (view instanceof SelfDecoratingView) {
|
||||
((SelfDecoratingView) view).removeDecoration();
|
||||
}
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
|
||||
if (adapterItem.searchSectionInfo != null) {
|
||||
@@ -90,7 +94,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||
if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
|
||||
&& mAppsView.getApps().getFocusedChild() != null) {
|
||||
int index = mAppsView.getApps().getFocusedChildIndex();
|
||||
if (index >= 0 && index < parent.getChildCount()) {
|
||||
AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
|
||||
mAppsView.getActiveRecyclerView().getLayoutManager();
|
||||
if (layoutManager.findFirstVisibleItemPosition() <= index
|
||||
&& index < parent.getChildCount()) {
|
||||
decorationHandler.onFocusDraw(c, parent.getChildAt(index));
|
||||
}
|
||||
}
|
||||
@@ -101,8 +108,8 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||
* Handles grouping and drawing of items in the same all apps sections.
|
||||
*/
|
||||
public static class SectionDecorationHandler {
|
||||
private static final int FILL_ALPHA = (int) (.3f * 255);
|
||||
private static final int FOCUS_ALPHA = (int) (.8f * 255);
|
||||
private static final int FILL_ALPHA = 0;
|
||||
private static final int FOCUS_ALPHA = (int) (.9f * 255);
|
||||
|
||||
protected RectF mBounds = new RectF();
|
||||
private final boolean mIsFullWidth;
|
||||
@@ -152,6 +159,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
if (view instanceof SelfDecoratingView) {
|
||||
((SelfDecoratingView) view).decorate(mFocusColor);
|
||||
return;
|
||||
}
|
||||
mPaint.setColor(mFocusColor);
|
||||
canvas.drawRoundRect(view.getLeft(), view.getTop(),
|
||||
view.getRight(), view.getBottom(), mRadius, mRadius, mPaint);
|
||||
@@ -165,4 +176,18 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for a view to draw highlight indicator
|
||||
*/
|
||||
public interface SelfDecoratingView {
|
||||
/**
|
||||
* Removes decorations drawing if focus is acquired by another view
|
||||
*/
|
||||
void removeDecoration();
|
||||
|
||||
/**
|
||||
* Draws highlight indicator on view.
|
||||
*/
|
||||
void decorate(int focusColor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,16 +68,16 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
||||
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
|
||||
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
|
||||
|
||||
@Override
|
||||
public Float get(AllAppsTransitionController controller) {
|
||||
return controller.mProgress;
|
||||
}
|
||||
@Override
|
||||
public Float get(AllAppsTransitionController controller) {
|
||||
return controller.mProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(AllAppsTransitionController controller, float progress) {
|
||||
controller.setProgress(progress);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void setValue(AllAppsTransitionController controller, float progress) {
|
||||
controller.setProgress(progress);
|
||||
}
|
||||
};
|
||||
|
||||
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
|
||||
|
||||
@@ -133,7 +133,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
||||
* in xml-based animations which also handle updating the appropriate UI.
|
||||
*
|
||||
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
|
||||
*
|
||||
* @see #setState(LauncherState)
|
||||
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
|
||||
*/
|
||||
@@ -238,7 +237,7 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
||||
mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView);
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -178,16 +178,46 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
|
||||
/**
|
||||
* Sets results list for search
|
||||
*/
|
||||
public boolean setSearchResults(ArrayList<AdapterItem> f) {
|
||||
if (f == null || mSearchResults != f) {
|
||||
boolean same = mSearchResults != null && mSearchResults.equals(f);
|
||||
mSearchResults = f;
|
||||
public boolean setSearchResults(ArrayList<AdapterItem> results) {
|
||||
if (results == null || mSearchResults != results) {
|
||||
boolean same = mSearchResults != null && mSearchResults.equals(results);
|
||||
mSearchResults = results;
|
||||
onAppsUpdated();
|
||||
return !same;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean appendSearchResults(ArrayList<AdapterItem> results) {
|
||||
if (mSearchResults != null && results != null && results.size() > 0) {
|
||||
updateSearchAdapterItems(results, mSearchResults.size());
|
||||
refreshRecyclerView();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
|
||||
SearchSectionInfo lastSection = null;
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
AdapterItem adapterItem = list.get(i);
|
||||
adapterItem.position = offset + i;
|
||||
mAdapterItems.add(adapterItem);
|
||||
if (adapterItem.searchSectionInfo != lastSection) {
|
||||
if (adapterItem.searchSectionInfo != null) {
|
||||
adapterItem.searchSectionInfo.setPosStart(adapterItem.position);
|
||||
}
|
||||
if (lastSection != null) {
|
||||
lastSection.setPosEnd(adapterItem.position - 1);
|
||||
}
|
||||
lastSection = adapterItem.searchSectionInfo;
|
||||
}
|
||||
if (adapterItem.isCountedForAccessibility()) {
|
||||
mAccessibilityResultsCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates internals when the set of apps are updated.
|
||||
*/
|
||||
@@ -294,28 +324,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
|
||||
}
|
||||
appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
|
||||
} else {
|
||||
List<AppInfo> appInfos = new ArrayList<>();
|
||||
SearchSectionInfo lastSection = null;
|
||||
for (int i = 0; i < mSearchResults.size(); i++) {
|
||||
AdapterItem adapterItem = mSearchResults.get(i);
|
||||
adapterItem.position = i;
|
||||
mAdapterItems.add(adapterItem);
|
||||
if (adapterItem.searchSectionInfo != lastSection) {
|
||||
if (adapterItem.searchSectionInfo != null) {
|
||||
adapterItem.searchSectionInfo.setPosStart(i);
|
||||
}
|
||||
if (lastSection != null) {
|
||||
lastSection.setPosEnd(i - 1);
|
||||
}
|
||||
lastSection = adapterItem.searchSectionInfo;
|
||||
}
|
||||
if (AllAppsGridAdapter.isIconViewType(adapterItem.viewType)) {
|
||||
appInfos.add(adapterItem.appInfo);
|
||||
}
|
||||
if (adapterItem.isCountedForAccessibility()) {
|
||||
mAccessibilityResultsCount++;
|
||||
}
|
||||
}
|
||||
updateSearchAdapterItems(mSearchResults, 0);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
// Append the search market item
|
||||
if (hasNoFilteredResults()) {
|
||||
|
||||
@@ -111,8 +111,8 @@ public class FloatingHeaderView extends LinearLayout implements
|
||||
|
||||
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mHeaderTopPadding = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
|
||||
mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 :
|
||||
context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -130,6 +130,7 @@ public class FloatingHeaderView extends LinearLayout implements
|
||||
}
|
||||
}
|
||||
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
|
||||
setPadding(0, mHeaderTopPadding, 0, 0);
|
||||
mAllRows = mFixedRows;
|
||||
}
|
||||
|
||||
@@ -247,7 +248,9 @@ public class FloatingHeaderView extends LinearLayout implements
|
||||
|
||||
public int getMaxTranslation() {
|
||||
if (mMaxTranslation == 0 && mTabsHidden) {
|
||||
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
|
||||
int paddingOffset = getResources().getDimensionPixelSize(
|
||||
R.dimen.all_apps_search_bar_bottom_padding);
|
||||
return FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : paddingOffset;
|
||||
} else if (mMaxTranslation > 0 && mTabsHidden) {
|
||||
return mMaxTranslation + getPaddingTop();
|
||||
} else {
|
||||
|
||||
@@ -30,13 +30,11 @@ import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.ExtendedEditText;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -114,7 +112,7 @@ public class AllAppsSearchBarController
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
|
||||
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
|
||||
if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
|
||||
return true;
|
||||
@@ -196,9 +194,16 @@ public class AllAppsSearchBarController
|
||||
/**
|
||||
* Called when the search from primary source is complete.
|
||||
*
|
||||
* @param items sorted list of search result adapter items.
|
||||
* @param items sorted list of search result adapter items
|
||||
*/
|
||||
void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items);
|
||||
void onSearchResult(String query, ArrayList<AdapterItem> items);
|
||||
|
||||
/**
|
||||
* Called when the search from secondary source is complete.
|
||||
*
|
||||
* @param items sorted list of search result adapter items
|
||||
*/
|
||||
void onAppendSearchResult(String query, ArrayList<AdapterItem> items);
|
||||
|
||||
/**
|
||||
* Called when the search results should be cleared.
|
||||
@@ -208,50 +213,20 @@ public class AllAppsSearchBarController
|
||||
|
||||
/**
|
||||
* An interface for supporting dynamic search results
|
||||
*
|
||||
* @param <T> Type of payload
|
||||
*/
|
||||
public interface PayloadResultHandler<T> {
|
||||
/**
|
||||
* Updates View using Adapter's payload
|
||||
*/
|
||||
public interface SearchTargetHandler {
|
||||
|
||||
default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
|
||||
Object[] targetInfo = getTargetInfo();
|
||||
if (targetInfo != null) {
|
||||
targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
|
||||
targetInfo[1] = adapterItemWithPayload.position;
|
||||
}
|
||||
applyAdapterInfo(adapterItemWithPayload);
|
||||
/**
|
||||
* Update view using values from {@link SearchTarget}
|
||||
*/
|
||||
void applySearchTarget(SearchTarget searchTarget);
|
||||
|
||||
/**
|
||||
* Handles selection of SearchTarget
|
||||
*/
|
||||
default void handleSelection(int eventType) {
|
||||
}
|
||||
|
||||
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
|
||||
|
||||
/**
|
||||
* Gets object created by {@link PayloadResultHandler#createTargetInfo()}
|
||||
*/
|
||||
Object[] getTargetInfo();
|
||||
|
||||
/**
|
||||
* Creates a wrapper object to hold searchSessionId and item position
|
||||
*/
|
||||
default Object[] createTargetInfo() {
|
||||
return new Object[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a SearchTargetEvent object for a PayloadHandlerView
|
||||
*/
|
||||
default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
|
||||
int eventType) {
|
||||
Object[] targetInfo = getTargetInfo();
|
||||
if (targetInfo == null) return null;
|
||||
|
||||
String searchSessionId = (String) targetInfo[0];
|
||||
int position = (int) targetInfo[1];
|
||||
return new SearchTargetEvent(itemType, eventType,
|
||||
position, searchSessionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.allapps.AlphabeticalAppsList;
|
||||
import com.android.launcher3.allapps.SearchUiManager;
|
||||
@@ -173,7 +173,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
|
||||
public void onSearchResult(String query, ArrayList<AdapterItem> items) {
|
||||
if (items != null) {
|
||||
mApps.setSearchResults(items);
|
||||
notifyResultChanged();
|
||||
@@ -181,6 +181,14 @@ public class AppsSearchContainerLayout extends ExtendedEditText
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
|
||||
if (items != null) {
|
||||
mApps.appendSearchResults(items);
|
||||
notifyResultChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearSearchResult() {
|
||||
if (mApps.setSearchResults(null)) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.launcher3.allapps.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CancellationSignal;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
@@ -47,11 +48,12 @@ public class AppsSearchPipeline implements SearchPipeline {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performSearch(String query, Consumer<ArrayList<AdapterItem>> callback) {
|
||||
public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
|
||||
CancellationSignal cancellationSignal) {
|
||||
mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
List<AppInfo> matchingResults = getTitleMatchResult(apps.data, query);
|
||||
List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
|
||||
callback.accept(getAdapterItems(matchingResults));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -46,8 +46,10 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
|
||||
@Override
|
||||
public void doSearch(final String query,
|
||||
final AllAppsSearchBarController.Callbacks callback) {
|
||||
mAppsSearchPipeline.performSearch(query,
|
||||
results -> mResultHandler.post(() -> callback.onSearchResult(query, results)));
|
||||
mAppsSearchPipeline.query(query,
|
||||
results -> mResultHandler.post(
|
||||
() -> callback.onSearchResult(query, results)),
|
||||
null);
|
||||
}
|
||||
|
||||
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.launcher3.allapps.search;
|
||||
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* A singleton class to track and report search events to search provider
|
||||
*/
|
||||
public class SearchEventTracker {
|
||||
@Nullable
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private final WeakHashMap<SearchTarget, SearchTargetHandler>
|
||||
mCallbacks = new WeakHashMap<>();
|
||||
|
||||
public static final MainThreadInitializedObject<SearchEventTracker> INSTANCE =
|
||||
new MainThreadInitializedObject<>(SearchEventTracker::new);
|
||||
|
||||
private SearchEventTracker(Context context) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of SearchEventTracker
|
||||
*/
|
||||
public static SearchEventTracker getInstance(Context context) {
|
||||
return SearchEventTracker.INSTANCE.get(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets current connected plugin for event reporting
|
||||
*/
|
||||
public void setPlugin(@Nullable AllAppsSearchPlugin plugin) {
|
||||
mPlugin = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends SearchTargetEvent to search provider
|
||||
*/
|
||||
public void notifySearchTargetEvent(SearchTargetEvent searchTargetEvent) {
|
||||
if (mPlugin != null) {
|
||||
UI_HELPER_EXECUTOR.post(() -> mPlugin.notifySearchTargetEvent(searchTargetEvent));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link SearchTargetHandler} to handle quick launch for specified SearchTarget.
|
||||
*/
|
||||
public void registerWeakHandler(SearchTarget searchTarget, SearchTargetHandler targetHandler) {
|
||||
mCallbacks.put(searchTarget, targetHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles quick select for SearchTarget
|
||||
*/
|
||||
public void quickSelect(SearchTarget searchTarget) {
|
||||
SearchTargetHandler searchTargetHandler = mCallbacks.get(searchTarget);
|
||||
if (searchTargetHandler != null) {
|
||||
searchTargetHandler.handleSelection(SearchTargetEvent.QUICK_SELECT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* flushes all registered quick select handlers
|
||||
*/
|
||||
public void clearHandlers() {
|
||||
mCallbacks.clear();
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.launcher3.allapps.search;
|
||||
|
||||
import android.os.CancellationSignal;
|
||||
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -23,10 +25,13 @@ import java.util.function.Consumer;
|
||||
/**
|
||||
* An interface for handling search within pipeline
|
||||
*/
|
||||
// Remove when System Service API is added.
|
||||
public interface SearchPipeline {
|
||||
|
||||
/**
|
||||
* Perform query
|
||||
*/
|
||||
void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
|
||||
void query(String input,
|
||||
Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
|
||||
CancellationSignal cancellationSignal);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHa
|
||||
*/
|
||||
public class SearchSectionInfo {
|
||||
|
||||
private String mTitle;
|
||||
private String mSectionId;
|
||||
private SectionDecorationHandler mDecorationHandler;
|
||||
|
||||
public int getPosStart() {
|
||||
@@ -48,8 +48,8 @@ public class SearchSectionInfo {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public SearchSectionInfo(String title) {
|
||||
mTitle = title;
|
||||
public SearchSectionInfo(String sectionId) {
|
||||
mSectionId = sectionId;
|
||||
}
|
||||
|
||||
public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
|
||||
@@ -62,9 +62,9 @@ public class SearchSectionInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the section's title
|
||||
* Returns the section's ID
|
||||
*/
|
||||
public String getTitle() {
|
||||
return mTitle == null ? "" : mTitle;
|
||||
public String getSectionId() {
|
||||
return mSectionId == null ? "" : mSectionId;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,10 +156,14 @@ public final class FeatureFlags {
|
||||
"ENABLE_DATABASE_RESTORE", true,
|
||||
"Enable database restore when new restore session is created");
|
||||
|
||||
public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag(
|
||||
"ENABLE_UNIVERSAL_SMARTSPACE", false,
|
||||
public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
|
||||
"ENABLE_SMARTSPACE_UNIVERSAL", false,
|
||||
"Replace Smartspace with a version rendered by System UI.");
|
||||
|
||||
public static final BooleanFlag ENABLE_SMARTSPACE_BLUECHIP = getDebugFlag(
|
||||
"ENABLE_SMARTSPACE_BLUECHIP", false,
|
||||
"Replace Smartspace with the Bluechip version. Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
|
||||
|
||||
public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag(
|
||||
"ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
|
||||
"Use system VelocityTracker's algorithm for motion pause detection.");
|
||||
@@ -178,7 +182,7 @@ public final class FeatureFlags {
|
||||
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
|
||||
|
||||
public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
|
||||
"ENABLE_MINIMAL_DEVICE", true,
|
||||
"ENABLE_MINIMAL_DEVICE", false,
|
||||
"Allow user to toggle minimal device mode in launcher.");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
|
||||
@@ -53,8 +53,8 @@ import com.android.launcher3.R;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.ItemInstallQueue;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.pm.PinRequestHelper;
|
||||
import com.android.launcher3.util.InstantAppResolver;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||
@@ -87,7 +87,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
||||
private Bundle mWidgetOptions;
|
||||
|
||||
private boolean mFinishOnPause = false;
|
||||
private InstantAppResolver mInstantAppResolver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -101,7 +100,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
||||
|
||||
mApp = LauncherAppState.getInstance(this);
|
||||
mIdp = mApp.getInvariantDeviceProfile();
|
||||
mInstantAppResolver = InstantAppResolver.newInstance(this);
|
||||
|
||||
// Use the application context to get the device profile, as in multiwindow-mode, the
|
||||
// confirmation activity might be rotated.
|
||||
@@ -322,6 +320,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
||||
}
|
||||
|
||||
private void logCommand(StatsLogManager.EventEnum command) {
|
||||
getStatsLogManager().logger().log(command);
|
||||
getStatsLogManager().logger()
|
||||
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
|
||||
.log(command);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.util.LongSparseArray;
|
||||
import android.util.TimingLogger;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -315,6 +316,7 @@ public class LoaderTask implements Runnable {
|
||||
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
|
||||
final boolean isSafeMode = pmHelper.isSafeMode();
|
||||
final boolean isSdCardReady = Utilities.isBootCompleted();
|
||||
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
|
||||
|
||||
boolean clearDb = false;
|
||||
try {
|
||||
@@ -402,6 +404,7 @@ public class LoaderTask implements Runnable {
|
||||
|
||||
WorkspaceItemInfo info;
|
||||
LauncherAppWidgetInfo appWidgetInfo;
|
||||
LauncherAppWidgetProviderInfo widgetProviderInfo;
|
||||
Intent intent;
|
||||
String targetPkg;
|
||||
|
||||
@@ -731,6 +734,19 @@ public class LoaderTask implements Runnable {
|
||||
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
|
||||
continue;
|
||||
}
|
||||
widgetProviderInfo =
|
||||
widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
|
||||
if (widgetProviderInfo != null
|
||||
&& (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
|
||||
|| appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
|
||||
// This can happen when display size changes.
|
||||
c.markDeleted("Widget removed, min sizes not met: "
|
||||
+ "span=" + appWidgetInfo.spanX + "x"
|
||||
+ appWidgetInfo.spanY + " minSpan="
|
||||
+ widgetProviderInfo.minSpanX + "x"
|
||||
+ widgetProviderInfo.minSpanY);
|
||||
continue;
|
||||
}
|
||||
if (!c.isOnWorkspaceOrHotseat()) {
|
||||
c.markDeleted("Widget found where container != " +
|
||||
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
|
||||
|
||||
@@ -32,6 +32,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SH
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
|
||||
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
|
||||
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
@@ -50,10 +51,10 @@ import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
|
||||
import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.Shortcut;
|
||||
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
|
||||
import com.android.launcher3.model.ModelWriter;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -282,9 +283,14 @@ public class ItemInfo {
|
||||
case ITEM_TYPE_DEEP_SHORTCUT:
|
||||
itemBuilder
|
||||
.setShortcut(nullableComponent
|
||||
.map(component -> LauncherAtom.Shortcut.newBuilder()
|
||||
.setShortcutName(component.flattenToShortString())
|
||||
.setShortcutId(ShortcutKey.fromItemInfo(this).getId()))
|
||||
.map(component -> {
|
||||
Shortcut.Builder lsb = Shortcut.newBuilder()
|
||||
.setShortcutName(component.flattenToShortString());
|
||||
Optional.ofNullable(getIntent())
|
||||
.map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
|
||||
.ifPresent(lsb::setShortcutId);
|
||||
return lsb;
|
||||
})
|
||||
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
||||
break;
|
||||
case ITEM_TYPE_SHORTCUT:
|
||||
|
||||
@@ -61,4 +61,8 @@ public class RemoteActionItemInfo extends ItemInfoWithIcon {
|
||||
public boolean shouldStartInLauncher() {
|
||||
return mShouldStart;
|
||||
}
|
||||
|
||||
public boolean isEscapeHatch() {
|
||||
return mToken.contains("item_type:[ESCAPE_HATCH]");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +275,8 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
|
||||
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
|
||||
"tutorial_steps",
|
||||
new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchBackTutorialPreference);
|
||||
@@ -284,7 +285,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
|
||||
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
|
||||
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"HOME_NAVIGATION"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchHomeTutorialPreference);
|
||||
@@ -293,7 +296,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
|
||||
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
|
||||
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"OVERVIEW_NAVIGATION"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchOverviewTutorialPreference);
|
||||
@@ -302,7 +307,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
|
||||
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
|
||||
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"ASSISTANT"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchAssistantTutorialPreference);
|
||||
@@ -311,7 +318,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
|
||||
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
|
||||
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"SANDBOX_MODE"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
|
||||
|
||||
@@ -21,14 +21,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -43,16 +38,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||
|
||||
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
|
||||
|
||||
private final ContentResolver mContentResolver;
|
||||
private boolean mSystemAutoRotateEnabled;
|
||||
|
||||
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateAutoRotateSetting();
|
||||
}
|
||||
};
|
||||
|
||||
public static boolean getAllowRotationDefaultValue() {
|
||||
// If the device's pixel density was scaled (usually via settings for A11y), use the
|
||||
// original dimensions to determine if rotation is allowed of not.
|
||||
@@ -106,20 +91,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||
} else {
|
||||
mSharedPrefs = null;
|
||||
}
|
||||
|
||||
mContentResolver = activity.getContentResolver();
|
||||
}
|
||||
|
||||
private void updateAutoRotateSetting() {
|
||||
int autoRotateEnabled = 0;
|
||||
try {
|
||||
autoRotateEnabled = Settings.System.getInt(mContentResolver,
|
||||
Settings.System.ACCELEROMETER_ROTATION);
|
||||
} catch (Settings.SettingNotFoundException e) {
|
||||
Log.e(TAG, "autorotate setting not found", e);
|
||||
}
|
||||
|
||||
mSystemAutoRotateEnabled = autoRotateEnabled == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -129,7 +100,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||
getAllowRotationDefaultValue());
|
||||
if (mHomeRotationEnabled != wasRotationEnabled) {
|
||||
notifyChange();
|
||||
updateAutoRotateSetting();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,11 +135,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||
if (!mInitialized) {
|
||||
mInitialized = true;
|
||||
notifyChange();
|
||||
|
||||
mContentResolver.registerContentObserver(
|
||||
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
|
||||
false, mSystemAutoRotateObserver);
|
||||
updateAutoRotateSetting();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,7 +144,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||
if (mSharedPrefs != null) {
|
||||
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,9 +189,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
|
||||
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b,"
|
||||
+ " mSystemAutoRotateEnabled=%b]",
|
||||
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]",
|
||||
mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
|
||||
mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled);
|
||||
mIgnoreAutoRotateSettings, mHomeRotationEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.launcher3.util;
|
||||
|
||||
import android.os.Looper;
|
||||
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
/**
|
||||
* Utility class to define an object which does most of it's processing on a
|
||||
* dedicated background thread.
|
||||
*/
|
||||
public abstract class BgObjectWithLooper {
|
||||
|
||||
/**
|
||||
* Start initialization of the object
|
||||
*/
|
||||
public final void initializeInBackground(String threadName) {
|
||||
new Thread(this::runOnThread, threadName).start();
|
||||
}
|
||||
|
||||
private void runOnThread() {
|
||||
Looper.prepare();
|
||||
onInitialized(Looper.myLooper());
|
||||
Looper.loop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the background thread to handle initialization
|
||||
*/
|
||||
@WorkerThread
|
||||
protected abstract void onInitialized(Looper looper);
|
||||
}
|
||||
@@ -31,6 +31,8 @@ import android.view.Display;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
@@ -157,13 +159,13 @@ public class DisplayController implements DisplayListener {
|
||||
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
|
||||
private DisplayController.Info mInfo;
|
||||
|
||||
private DisplayHolder(Context displayContext) {
|
||||
private DisplayHolder(Context displayContext, Display display) {
|
||||
mDisplayContext = displayContext;
|
||||
// Note that the Display object must be obtained from DisplayManager which is
|
||||
// associated to the display context, so the Display is isolated from Activity and
|
||||
// Application to provide the actual state of device that excludes the additional
|
||||
// adjustment and override.
|
||||
mInfo = new DisplayController.Info(mDisplayContext);
|
||||
mInfo = new DisplayController.Info(display);
|
||||
mId = mInfo.id;
|
||||
}
|
||||
|
||||
@@ -180,22 +182,31 @@ public class DisplayController implements DisplayListener {
|
||||
}
|
||||
|
||||
protected void handleOnChange() {
|
||||
Display display = Utilities.ATLEAST_R
|
||||
? mDisplayContext.getDisplay()
|
||||
: mDisplayContext
|
||||
.getSystemService(DisplayManager.class)
|
||||
.getDisplay(mId);
|
||||
if (display == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Info oldInfo = mInfo;
|
||||
Info info = new Info(mDisplayContext);
|
||||
Info newInfo = new Info(display);
|
||||
|
||||
int change = 0;
|
||||
if (info.hasDifferentSize(oldInfo)) {
|
||||
if (newInfo.hasDifferentSize(oldInfo)) {
|
||||
change |= CHANGE_SIZE;
|
||||
}
|
||||
if (oldInfo.rotation != info.rotation) {
|
||||
if (newInfo.rotation != oldInfo.rotation) {
|
||||
change |= CHANGE_ROTATION;
|
||||
}
|
||||
if (info.singleFrameMs != oldInfo.singleFrameMs) {
|
||||
if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
|
||||
change |= CHANGE_FRAME_DELAY;
|
||||
}
|
||||
|
||||
if (change != 0) {
|
||||
mInfo = info;
|
||||
mInfo = newInfo;
|
||||
final int flags = change;
|
||||
MAIN_EXECUTOR.execute(() -> notifyChange(flags));
|
||||
}
|
||||
@@ -216,7 +227,7 @@ public class DisplayController implements DisplayListener {
|
||||
// Use application context to create display context so that it can have its own
|
||||
// Resources.
|
||||
Context displayContext = context.getApplicationContext().createDisplayContext(display);
|
||||
return new DisplayHolder(displayContext);
|
||||
return new DisplayHolder(displayContext, display);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,12 +255,7 @@ public class DisplayController implements DisplayListener {
|
||||
this.metrics = metrics;
|
||||
}
|
||||
|
||||
private Info(Context context) {
|
||||
this(context, context.getSystemService(DisplayManager.class)
|
||||
.getDisplay(DEFAULT_DISPLAY));
|
||||
}
|
||||
|
||||
public Info(Context context, Display display) {
|
||||
public Info(Display display) {
|
||||
id = display.getDisplayId();
|
||||
rotation = display.getRotation();
|
||||
|
||||
@@ -262,7 +268,8 @@ public class DisplayController implements DisplayListener {
|
||||
display.getRealSize(realSize);
|
||||
display.getCurrentSizeRange(smallestSize, largestSize);
|
||||
|
||||
metrics = context.getResources().getDisplayMetrics();
|
||||
metrics = new DisplayMetrics();
|
||||
display.getMetrics(metrics);
|
||||
}
|
||||
|
||||
private boolean hasDifferentSize(Info info) {
|
||||
|
||||
@@ -20,10 +20,8 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
@@ -37,8 +35,9 @@ import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||
@@ -48,24 +47,28 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A view representing a high confidence app search result that includes shortcuts
|
||||
* TODO (sfufa@) consolidate this with SearchResultIconRow
|
||||
*/
|
||||
public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||
PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
|
||||
public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetHandler {
|
||||
|
||||
public static final String TARGET_TYPE_HERO_APP = "hero_app";
|
||||
|
||||
public static final int MAX_SHORTCUTS_COUNT = 2;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
BubbleTextView mBubbleTextView;
|
||||
View mIconView;
|
||||
BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
|
||||
AllAppsSearchPlugin mPlugin;
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
private BubbleTextView mBubbleTextView;
|
||||
private View mIconView;
|
||||
private BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
|
||||
|
||||
|
||||
public HeroSearchResultView(Context context) {
|
||||
super(context);
|
||||
@@ -96,8 +99,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||
launcher.getItemOnClickListener().onClick(view);
|
||||
});
|
||||
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
|
||||
setLayoutParams(
|
||||
new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx));
|
||||
|
||||
|
||||
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
|
||||
@@ -108,35 +109,39 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||
grid.allAppsIconSizePx));
|
||||
bubbleTextView.setOnClickListener(view -> {
|
||||
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
|
||||
SearchTargetEvent event = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.APP_HERO,
|
||||
SearchTargetEvent.CHILD_SELECT);
|
||||
event.bundle = getAppBundle(itemInfo);
|
||||
event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId());
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
SearchTargetEvent event = new SearchTargetEvent.Builder(mSearchTarget,
|
||||
SearchTargetEvent.CHILD_SELECT).setShortcutPosition(itemInfo.rank).build();
|
||||
SearchEventTracker.getInstance(getContext()).notifySearchTargetEvent(event);
|
||||
launcher.getItemOnClickListener().onClick(view);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply {@link ItemInfo} for appIcon and shortcut Icons
|
||||
*/
|
||||
@Override
|
||||
public void applyAdapterInfo(
|
||||
AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
|
||||
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore();
|
||||
AppInfo appInfo = apps.getApp(new ComponentKey(searchTarget.getComponentName(),
|
||||
searchTarget.getUserHandle()));
|
||||
List<ShortcutInfo> infos = mSearchTarget.getShortcutInfos();
|
||||
|
||||
ArrayList<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcuts = new ArrayList<>();
|
||||
for (int i = 0; infos != null && i < infos.size() && i < MAX_SHORTCUTS_COUNT; i++) {
|
||||
ShortcutInfo shortcutInfo = infos.get(i);
|
||||
ItemInfoWithIcon si = new WorkspaceItemInfo(shortcutInfo, getContext());
|
||||
si.rank = i;
|
||||
shortcuts.add(new Pair<>(shortcutInfo, si));
|
||||
}
|
||||
|
||||
mBubbleTextView.applyFromApplicationInfo(appInfo);
|
||||
mIconView.setBackground(mBubbleTextView.getIcon());
|
||||
mIconView.setTag(adapterItem.appInfo);
|
||||
List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
|
||||
mIconView.setTag(appInfo);
|
||||
LauncherAppState appState = LauncherAppState.getInstance(getContext());
|
||||
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
|
||||
BubbleTextView shortcutView = mDeepShortcutTextViews[i];
|
||||
mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
|
||||
if (i < shortcutDetails.size()) {
|
||||
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
|
||||
mDeepShortcutTextViews[i].setVisibility(shortcuts.size() > i ? VISIBLE : GONE);
|
||||
if (i < shortcuts.size()) {
|
||||
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcuts.get(i);
|
||||
//apply ItemInfo and prepare view
|
||||
shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
@@ -146,13 +151,7 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||
});
|
||||
}
|
||||
}
|
||||
mPlugin = adapterItem.getPlugin();
|
||||
adapterItem.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,38 +189,21 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
|
||||
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
|
||||
|
||||
SearchTargetEvent event = mContainer.getSearchTargetEvent(
|
||||
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
|
||||
event.bundle = getAppBundle(itemInfo);
|
||||
if (mContainer.mPlugin != null) {
|
||||
mContainer.mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
|
||||
SearchTargetEvent event = new SearchTargetEvent.Builder(mContainer.mSearchTarget,
|
||||
SearchTargetEvent.LONG_PRESS).build();
|
||||
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag();
|
||||
if (itemInfo == null) return;
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
|
||||
|
||||
SearchTargetEvent event = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.APP_HERO, eventType);
|
||||
event.bundle = getAppBundle(itemInfo);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to generate {@link SearchTargetEvent} bundle from {@link ItemInfo}
|
||||
*/
|
||||
public static Bundle getAppBundle(ItemInfo itemInfo) {
|
||||
Bundle b = new Bundle();
|
||||
b.putParcelable(Intent.EXTRA_COMPONENT_NAME, itemInfo.getTargetComponent());
|
||||
b.putParcelable(Intent.EXTRA_USER, itemInfo.user);
|
||||
return b;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.launcher3.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.touch.ItemLongClickListener;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
/**
|
||||
* A {@link BubbleTextView} representing a single cell result in AllApps
|
||||
*/
|
||||
public class SearchResultIcon extends BubbleTextView implements
|
||||
AllAppsSearchBarController.SearchTargetHandler, View.OnClickListener,
|
||||
View.OnLongClickListener {
|
||||
|
||||
|
||||
public static final String TARGET_TYPE_APP = "app";
|
||||
|
||||
private final Launcher mLauncher;
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public SearchResultIcon(Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public SearchResultIcon(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SearchResultIcon(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mLauncher = Launcher.getLauncher(getContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
setLongPressTimeoutFactor(1f);
|
||||
setOnFocusChangeListener(mLauncher.getFocusHandler());
|
||||
setOnClickListener(this);
|
||||
setOnLongClickListener(this);
|
||||
getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
|
||||
SearchEventTracker.getInstance(getContext()).registerWeakHandler(mSearchTarget, this);
|
||||
if (searchTarget.getItemType().equals(TARGET_TYPE_APP)) {
|
||||
AppInfo appInfo = appsStore.getApp(new ComponentKey(searchTarget.getComponentName(),
|
||||
searchTarget.getUserHandle()));
|
||||
applyFromApplicationInfo(appInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
mLauncher.getItemOnClickListener().onClick(this);
|
||||
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
handleSelection(SearchTargetEvent.SELECT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, SearchTargetEvent.LONG_PRESS).build());
|
||||
return ItemLongClickListener.INSTANCE_ALL_APPS.onLongClick(view);
|
||||
}
|
||||
}
|
||||
@@ -22,100 +22,159 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import android.app.RemoteAction;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTarget.ItemType;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
/**
|
||||
* A view representing a stand alone shortcut search result
|
||||
*/
|
||||
public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||
AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
||||
AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
private ShortcutInfo mShortcutInfo;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
||||
|
||||
public static final String TARGET_TYPE_REMOTE_ACTION = "remote_action";
|
||||
public static final String TARGET_TYPE_SUGGEST = "suggest";
|
||||
public static final String TARGET_TYPE_SHORTCUT = "shortcut";
|
||||
|
||||
|
||||
public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
|
||||
public static final String REMOTE_ACTION_TOKEN = "action_token";
|
||||
|
||||
private final int mCustomIconResId;
|
||||
private final boolean mMatchesInset;
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
|
||||
public SearchResultIconRow(@NonNull Context context) {
|
||||
super(context);
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public SearchResultIconRow(@NonNull Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.SearchResultIconRow, defStyleAttr, 0);
|
||||
mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
|
||||
mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery,
|
||||
false);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
if (mMatchesInset && launcher.getAppsView() != null && getParent() != null) {
|
||||
EditText editText = launcher.getAppsView().getSearchUiManager().getEditText();
|
||||
if (editText != null) {
|
||||
int counterOffset = getIconSize() + getCompoundDrawablePadding() / 2;
|
||||
setPadding(editText.getLeft() - counterOffset, getPaddingTop(),
|
||||
getPaddingRight(), getPaddingBottom());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
||||
if (mAdapterItem != null) {
|
||||
mAdapterItem.setSelectionHandler(null);
|
||||
}
|
||||
mAdapterItem = adapterItemWithPayload;
|
||||
SearchTarget payload = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
protected void drawFocusHighlight(Canvas canvas) {
|
||||
mHighlightPaint.setColor(mHighlightColor);
|
||||
float r = Themes.getDialogCornerRadius(getContext());
|
||||
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), r, r, mHighlightPaint);
|
||||
}
|
||||
|
||||
if (payload.mRemoteAction != null) {
|
||||
prepareUsingRemoteAction(payload.mRemoteAction,
|
||||
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
||||
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
||||
} else {
|
||||
prepareUsingShortcutInfo(payload.shortcuts.get(0));
|
||||
|
||||
@Override
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
String type = searchTarget.getItemType();
|
||||
if (type.equals(TARGET_TYPE_REMOTE_ACTION) || type.equals(TARGET_TYPE_SUGGEST)) {
|
||||
prepareUsingRemoteAction(searchTarget.getRemoteAction(),
|
||||
searchTarget.getExtras().getString(REMOTE_ACTION_TOKEN),
|
||||
searchTarget.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START),
|
||||
type.equals(TARGET_TYPE_REMOTE_ACTION));
|
||||
|
||||
} else if (type.equals(TARGET_TYPE_SHORTCUT)) {
|
||||
prepareUsingShortcutInfo(searchTarget.getShortcutInfos().get(0));
|
||||
}
|
||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
|
||||
mShortcutInfo = shortcutInfo;
|
||||
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
|
||||
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
|
||||
applyFromWorkspaceItem(workspaceItemInfo);
|
||||
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
|
||||
reapplyItemInfoAsync(workspaceItemInfo);
|
||||
});
|
||||
if (!loadIconFromResource()) {
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, shortcutInfo);
|
||||
reapplyItemInfoAsync(workspaceItemInfo);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) {
|
||||
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start,
|
||||
boolean useIconToBadge) {
|
||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
|
||||
|
||||
applyFromRemoteActionInfo(itemInfo);
|
||||
UI_HELPER_EXECUTOR.post(() -> {
|
||||
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
|
||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
||||
itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
||||
Build.VERSION.SDK_INT);
|
||||
reapplyItemInfoAsync(itemInfo);
|
||||
}
|
||||
});
|
||||
if (itemInfo.isEscapeHatch() || !loadIconFromResource()) {
|
||||
UI_HELPER_EXECUTOR.post(() -> {
|
||||
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not
|
||||
// work.
|
||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
||||
BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
||||
Build.VERSION.SDK_INT);
|
||||
|
||||
if (useIconToBadge) {
|
||||
BitmapInfo placeholder = li.createIconBitmap(
|
||||
itemInfo.getRemoteAction().getTitle().toString().substring(0, 1),
|
||||
bitmap.color);
|
||||
itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap);
|
||||
} else {
|
||||
itemInfo.bitmap = bitmap;
|
||||
}
|
||||
reapplyItemInfoAsync(itemInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean loadIconFromResource() {
|
||||
if (mCustomIconResId == 0) return false;
|
||||
setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
|
||||
return true;
|
||||
}
|
||||
|
||||
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
|
||||
@@ -123,33 +182,15 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
public void handleSelection(int eventType) {
|
||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
final SearchTargetEvent searchTargetEvent;
|
||||
if (itemInfo instanceof WorkspaceItemInfo) {
|
||||
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
||||
searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
|
||||
eventType);
|
||||
searchTargetEvent.shortcut = mShortcutInfo;
|
||||
} else {
|
||||
RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
|
||||
ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
|
||||
searchTargetEvent = getSearchTargetEvent(ItemType.ACTION,
|
||||
eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
|
||||
searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
|
||||
remoteItemInfo.shouldStartInLauncher());
|
||||
searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
|
||||
remoteItemInfo.getToken());
|
||||
}
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
ItemClickHandler.onClickRemoteAction(launcher, (RemoteActionItemInfo) itemInfo);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,12 @@ import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
@@ -39,10 +42,10 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
@@ -52,7 +55,9 @@ import java.util.ArrayList;
|
||||
* A view representing a single people search result in all apps
|
||||
*/
|
||||
public class SearchResultPeopleView extends LinearLayout implements
|
||||
AllAppsSearchBarController.PayloadResultHandler<Bundle> {
|
||||
AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
public static final String TARGET_TYPE_PEOPLE = "people";
|
||||
|
||||
private final int mIconSize;
|
||||
private final int mButtonSize;
|
||||
@@ -60,9 +65,10 @@ public class SearchResultPeopleView extends LinearLayout implements
|
||||
private View mIconView;
|
||||
private TextView mTitleView;
|
||||
private ImageButton[] mProviderButtons = new ImageButton[3];
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private Intent mIntent;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public SearchResultPeopleView(Context context) {
|
||||
this(context, null, 0);
|
||||
@@ -99,21 +105,19 @@ public class SearchResultPeopleView extends LinearLayout implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(
|
||||
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
||||
Bundle payload = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
Bundle payload = searchTarget.getExtras();
|
||||
mTitleView.setText(payload.getString("title"));
|
||||
mIntent = payload.getParcelable("intent");
|
||||
Bitmap icon = payload.getParcelable("icon");
|
||||
if (icon != null) {
|
||||
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
||||
float radius = Themes.getDialogCornerRadius(getContext());
|
||||
d.setCornerRadius(radius);
|
||||
d.setBounds(0, 0, mIconSize, mIconSize);
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
|
||||
Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false));
|
||||
mIconView.setBackground(d);
|
||||
Bitmap contactIcon = payload.getParcelable("icon");
|
||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||
BitmapInfo badgeInfo = li.createBadgedIconBitmap(
|
||||
getAppIcon(mIntent.getPackage()), Process.myUserHandle(),
|
||||
Build.VERSION.SDK_INT);
|
||||
setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false);
|
||||
} catch (Exception e) {
|
||||
setIcon(contactIcon, true);
|
||||
}
|
||||
|
||||
ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
|
||||
@@ -122,59 +126,80 @@ public class SearchResultPeopleView extends LinearLayout implements
|
||||
if (providers != null && i < providers.size()) {
|
||||
Bundle provider = providers.get(i);
|
||||
Intent intent = provider.getParcelable("intent");
|
||||
setupProviderButton(button, provider, intent, adapterItemWithPayload);
|
||||
String pkg = provider.getString("package_name");
|
||||
setupProviderButton(button, provider, intent);
|
||||
UI_HELPER_EXECUTOR.post(() -> {
|
||||
try {
|
||||
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
|
||||
pkg, 0);
|
||||
Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
|
||||
String pkg = provider.getString("package_name");
|
||||
Drawable appIcon = getAppIcon(pkg);
|
||||
if (appIcon != null) {
|
||||
MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
}
|
||||
|
||||
});
|
||||
button.setVisibility(VISIBLE);
|
||||
} else {
|
||||
button.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
/**
|
||||
* Normalizes the bitmap to look like rounded App Icon
|
||||
* TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
|
||||
* bitmap from it.
|
||||
*/
|
||||
private Bitmap roundBitmap(Bitmap icon) {
|
||||
final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
||||
d.setCornerRadius(R.attr.folderIconRadius);
|
||||
d.setBounds(0, 0, mIconSize, mIconSize);
|
||||
final Bitmap bitmap = Bitmap.createBitmap(d.getBounds().width(), d.getBounds().height(),
|
||||
Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
d.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
private void setupProviderButton(ImageButton button, Bundle provider, Intent intent,
|
||||
AllAppsGridAdapter.AdapterItem adapterItem) {
|
||||
private void setIcon(Bitmap icon, Boolean round) {
|
||||
if (round) {
|
||||
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
||||
d.setCornerRadius(R.attr.folderIconRadius);
|
||||
d.setBounds(0, 0, mIconSize, mIconSize);
|
||||
mIconView.setBackground(d);
|
||||
} else {
|
||||
mIconView.setBackground(new BitmapDrawable(getResources(), icon));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Drawable getAppIcon(String pkg) {
|
||||
try {
|
||||
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
|
||||
pkg, 0);
|
||||
return applicationInfo.loadIcon(mPackageManager);
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
button.setOnClickListener(b -> {
|
||||
launcher.startActivitySafely(b, intent, null);
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.PEOPLE,
|
||||
SearchTargetEvent.CHILD_SELECT);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
||||
searchTargetEvent.bundle.putBundle("provider", provider);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBundle("provider", provider);
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget,
|
||||
SearchTargetEvent.CHILD_SELECT).setExtras(bundle).build());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
if (mIntent != null) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
launcher.startActivitySafely(this, mIntent, null);
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
|
||||
eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,11 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
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.BitmapDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -36,20 +41,28 @@ import androidx.annotation.Nullable;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.icons.BitmapRenderer;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/**
|
||||
* A View representing a PlayStore item.
|
||||
*/
|
||||
public class SearchResultPlayItem extends LinearLayout implements
|
||||
AllAppsSearchBarController.PayloadResultHandler<Bundle> {
|
||||
AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
public static final String TARGET_TYPE_PLAY = "play";
|
||||
|
||||
private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
|
||||
final Paint mIconPaint = new Paint();
|
||||
final Rect mTempRect = new Rect();
|
||||
private final DeviceProfile mDeviceProfile;
|
||||
private View mIconView;
|
||||
private TextView mTitleView;
|
||||
@@ -57,8 +70,8 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||
private Button mPreviewButton;
|
||||
private String mPackageName;
|
||||
private boolean mIsInstantGame;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
|
||||
public SearchResultPlayItem(Context context) {
|
||||
@@ -91,14 +104,35 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||
iconParams.height = mDeviceProfile.allAppsIconSizePx;
|
||||
iconParams.width = mDeviceProfile.allAppsIconSizePx;
|
||||
setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT));
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Bitmap getRoundedBitmap(Bitmap bitmap) {
|
||||
final int iconSize = bitmap.getWidth();
|
||||
final float radius = Themes.getDialogCornerRadius(getContext());
|
||||
|
||||
Bitmap output = 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);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
Bundle bundle = searchTarget.getExtras();
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
if (bundle.getString("package", "").equals(mPackageName)) {
|
||||
return;
|
||||
}
|
||||
@@ -109,17 +143,19 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||
// TODO: Should use a generic type to get values b/165320033
|
||||
showIfNecessary(mDetailViews[0], bundle.getString("price"));
|
||||
showIfNecessary(mDetailViews[1], bundle.getString("rating"));
|
||||
showIfNecessary(mDetailViews[2], bundle.getString("category"));
|
||||
|
||||
mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
try {
|
||||
// TODO: Handle caching
|
||||
URL url = new URL(bundle.getString("icon_url"));
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
|
||||
URLConnection con = url.openConnection();
|
||||
// TODO: monitor memory and investigate if it's better to use glide
|
||||
con.addRequestProperty("Cache-Control", "max-age: 0");
|
||||
con.setUseCaches(true);
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), getRoundedBitmap(
|
||||
Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
|
||||
mDeviceProfile.allAppsIconSizePx, false));
|
||||
mDeviceProfile.allAppsIconSizePx, false)));
|
||||
mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
@@ -127,11 +163,6 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
}
|
||||
|
||||
private void showIfNecessary(TextView textView, @Nullable String string) {
|
||||
if (string == null || string.isEmpty()) {
|
||||
textView.setVisibility(GONE);
|
||||
@@ -141,7 +172,8 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
if (mPackageName == null) return;
|
||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
"https://play.google.com/store/apps/details?id="
|
||||
@@ -167,12 +199,7 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||
}
|
||||
|
||||
private void logSearchEvent(int eventType) {
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.PLAY_RESULTS, eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putString("package_name", mPackageName);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.launcher3.views;
|
||||
|
||||
import static com.android.systemui.plugins.shared.SearchTarget.ItemType.SUGGEST;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
/**
|
||||
* A view representing a fallback search suggestion row.
|
||||
*/
|
||||
public class SearchResultSuggestRow extends LinearLayout implements
|
||||
View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
||||
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
||||
private TextView mTitle;
|
||||
|
||||
|
||||
public SearchResultSuggestRow(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public SearchResultSuggestRow(@NonNull Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public SearchResultSuggestRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mTitle = findViewById(R.id.title);
|
||||
setOnClickListener(this);
|
||||
}
|
||||
@Override
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
||||
mAdapterItem = adapterItemWithPayload;
|
||||
SearchTarget payload = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
|
||||
if (payload.mRemoteAction != null) {
|
||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(payload.mRemoteAction,
|
||||
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
||||
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
||||
setTag(itemInfo);
|
||||
}
|
||||
showIfAvailable(mTitle, payload.mRemoteAction.getTitle().toString());
|
||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
if (itemInfo instanceof RemoteActionItemInfo) return;
|
||||
|
||||
RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
|
||||
ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SUGGEST, eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
|
||||
searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
|
||||
remoteItemInfo.shouldStartInLauncher());
|
||||
searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
|
||||
remoteItemInfo.getToken());
|
||||
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
handleSelection(SearchTargetEvent.SELECT);
|
||||
}
|
||||
|
||||
private void showIfAvailable(TextView view, @Nullable String string) {
|
||||
System.out.println("Plugin suggest string:" + string);
|
||||
if (TextUtils.isEmpty(string)) {
|
||||
view.setVisibility(GONE);
|
||||
} else {
|
||||
System.out.println("Plugin suggest string:" + string);
|
||||
view.setVisibility(VISIBLE);
|
||||
view.setText(string);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,16 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
|
||||
/**
|
||||
* Header text view that shows a title for a given section in All apps search
|
||||
*/
|
||||
public class SearchSectionHeaderView extends TextView implements
|
||||
AllAppsSearchBarController.PayloadResultHandler<String> {
|
||||
AllAppsSearchBarController.SearchTargetHandler {
|
||||
public static final String TARGET_TYPE_SECTION_HEADER = "section_header";
|
||||
|
||||
public SearchSectionHeaderView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
@@ -43,8 +45,8 @@ public class SearchSectionHeaderView extends TextView implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload<String> adapterItem) {
|
||||
String title = adapterItem.getPayload();
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
String title = searchTarget.getExtras().getString("title");
|
||||
if (title == null || !title.isEmpty()) {
|
||||
setText(title);
|
||||
setVisibility(VISIBLE);
|
||||
@@ -52,9 +54,4 @@ public class SearchSectionHeaderView extends TextView implements
|
||||
setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,8 @@ import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
@@ -41,14 +40,16 @@ import java.util.ArrayList;
|
||||
* A row of tappable TextViews with a breadcrumb for settings search.
|
||||
*/
|
||||
public class SearchSettingsRowView extends LinearLayout implements
|
||||
View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<Bundle> {
|
||||
View.OnClickListener, AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
public static final String TARGET_TYPE_SETTINGS_ROW = "settings_row";
|
||||
|
||||
|
||||
private TextView mTitleView;
|
||||
private TextView mDescriptionView;
|
||||
private TextView mBreadcrumbsView;
|
||||
private Intent mIntent;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
|
||||
public SearchSettingsRowView(@NonNull Context context) {
|
||||
@@ -75,10 +76,9 @@ public class SearchSettingsRowView extends LinearLayout implements
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(
|
||||
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
Bundle bundle = searchTarget.getExtras();
|
||||
mIntent = bundle.getParcelable("intent");
|
||||
showIfAvailable(mTitleView, bundle.getString("title"));
|
||||
showIfAvailable(mDescriptionView, bundle.getString("description"));
|
||||
@@ -86,12 +86,7 @@ public class SearchSettingsRowView extends LinearLayout implements
|
||||
//TODO: implement RTL friendly breadcrumbs view
|
||||
showIfAvailable(mBreadcrumbsView, breadcrumbs != null
|
||||
? String.join(" > ", breadcrumbs) : null);
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
private void showIfAvailable(TextView view, @Nullable String string) {
|
||||
@@ -108,19 +103,15 @@ public class SearchSettingsRowView extends LinearLayout implements
|
||||
handleSelection(SearchTargetEvent.SELECT);
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
if (mIntent == null) return;
|
||||
// TODO: create ItemInfo object and then use it to call startActivityForResult for proper
|
||||
// WW logging
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
launcher.startActivityForResult(mIntent, 0);
|
||||
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.SETTINGS_ROW, eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.launcher3.views;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceItem;
|
||||
import androidx.slice.widget.EventInfo;
|
||||
import androidx.slice.widget.SliceLiveData;
|
||||
import androidx.slice.widget.SliceView;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
/**
|
||||
* A Wrapper class for {@link SliceView} search results
|
||||
*/
|
||||
public class SearchSliceWrapper implements SliceView.OnSliceActionListener {
|
||||
|
||||
public static final String TARGET_TYPE_SLICE = "settings_slice";
|
||||
|
||||
private static final String TAG = "SearchSliceController";
|
||||
private static final String URI_EXTRA_KEY = "slice_uri";
|
||||
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final SearchTarget mSearchTarget;
|
||||
private final SliceView mSliceView;
|
||||
private LiveData<Slice> mSliceLiveData;
|
||||
|
||||
public SearchSliceWrapper(Context context, SliceView sliceView, SearchTarget searchTarget) {
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mSearchTarget = searchTarget;
|
||||
mSliceView = sliceView;
|
||||
sliceView.setOnSliceActionListener(this);
|
||||
try {
|
||||
mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri());
|
||||
mSliceLiveData.observe((Launcher) mLauncher, sliceView);
|
||||
} catch (Exception ex) {
|
||||
Log.e(TAG, "unable to bind slice", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters event handlers and removes lifecycle observer
|
||||
*/
|
||||
public void destroy() {
|
||||
mSliceView.setOnSliceActionListener(null);
|
||||
mSliceLiveData.removeObservers(mLauncher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSliceAction(@NonNull EventInfo info, @NonNull SliceItem item) {
|
||||
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget,
|
||||
SearchTargetEvent.CHILD_SELECT).build());
|
||||
}
|
||||
|
||||
private Uri getSliceUri() {
|
||||
return mSearchTarget.getExtras().getParcelable(URI_EXTRA_KEY);
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
package com.android.launcher3.views;
|
||||
|
||||
import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_SHOULD_START;
|
||||
import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_TOKEN;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -26,14 +29,13 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
@@ -41,11 +43,12 @@ import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
* A view representing a high confidence app search result that includes shortcuts
|
||||
*/
|
||||
public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
|
||||
implements AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
||||
implements AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
AllAppsSearchPlugin mPlugin;
|
||||
int mPosition;
|
||||
public static final String TARGET_TYPE_SCREENSHOT = "screenshot";
|
||||
public static final String TARGET_TYPE_SCREENSHOT_LEGACY = "screenshot_legacy";
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public ThumbnailSearchResultView(Context context) {
|
||||
super(context);
|
||||
@@ -59,7 +62,8 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||
if (itemInfo instanceof RemoteActionItemInfo) {
|
||||
@@ -68,33 +72,29 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
||||
} else {
|
||||
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
||||
}
|
||||
if (mPlugin != null) {
|
||||
SearchTargetEvent event = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.SCREENSHOT, eventType);
|
||||
mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItem) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
mPosition = adapterItem.position;
|
||||
|
||||
SearchTarget target = adapterItem.getPayload();
|
||||
public void applySearchTarget(SearchTarget target) {
|
||||
mSearchTarget = target;
|
||||
Bitmap bitmap;
|
||||
if (target.mRemoteAction != null) {
|
||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction,
|
||||
target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
||||
target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
||||
ItemClickHandler.onClickRemoteAction(launcher, itemInfo);
|
||||
bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon()
|
||||
if (target.getRemoteAction() != null) {
|
||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.getRemoteAction(),
|
||||
target.getExtras().getString(REMOTE_ACTION_TOKEN),
|
||||
target.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START));
|
||||
bitmap = ((BitmapDrawable) target.getRemoteAction().getIcon()
|
||||
.loadDrawable(getContext())).getBitmap();
|
||||
setTag(itemInfo);
|
||||
// crop
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0,
|
||||
bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
|
||||
bitmap.getWidth(), bitmap.getWidth());
|
||||
} else {
|
||||
bitmap = (Bitmap) target.bundle.getParcelable("bitmap");
|
||||
bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
|
||||
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
|
||||
itemInfo.intent = new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse(target.bundle.getString("uri")))
|
||||
.setData(Uri.parse(target.getExtras().getString("uri")))
|
||||
.setType("image/*")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
setTag(itemInfo);
|
||||
@@ -103,12 +103,6 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
||||
drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
|
||||
setImageDrawable(drawable);
|
||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
||||
mPlugin = adapterItem.getPlugin();
|
||||
adapterItem.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(target, this);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user