Merge "Checking mRecentsAnimationController for null-ness"
diff --git a/proguard.flags b/proguard.flags
index 37b8093..a450183 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -45,9 +45,10 @@
# BUG(70852369): Surpress additional warnings after changing from Proguard to R8
-dontwarn android.app.**
--dontwarn android.view.**
--dontwarn android.os.**
-dontwarn android.graphics.**
+-dontwarn android.os.**
+-dontwarn android.view.**
+-dontwarn android.window.**
# Ignore warnings for hidden utility classes referenced from the shared lib
-dontwarn com.android.internal.util.**
diff --git a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
index 8e6a5b6..2d9d092 100644
--- a/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/QuickstepAppTransitionManagerImpl.java
@@ -56,6 +56,7 @@
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
+import android.os.SystemProperties;
import android.util.Pair;
import android.view.View;
@@ -74,6 +75,7 @@
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RemoteAnimationTargets;
+import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.StaggeredWorkspaceAnim;
@@ -86,6 +88,7 @@
import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -99,6 +102,9 @@
private static final String TAG = "QuickstepTransition";
+ private static final boolean ENABLE_SHELL_STARTING_SURFACE =
+ SystemProperties.getBoolean("persist.debug.shell_starting_surface", false);
+
/** Duration of status bar animations. */
public static final int STATUS_BAR_TRANSITION_DURATION = 120;
@@ -159,6 +165,9 @@
private WrappedAnimationRunnerImpl mAppLaunchRunner;
private WrappedAnimationRunnerImpl mKeyguardGoingAwayRunner;
+ private WrappedAnimationRunnerImpl mWallpaperOpenTransitionRunner;
+ private RemoteTransitionCompat mLauncherOpenTransition;
+
private final AnimatorListenerAdapter mForceInvisibleListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -442,9 +451,9 @@
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect windowTargetBounds, boolean toggleVisibility) {
- RectF bounds = new RectF();
+ RectF launcherIconBounds = new RectF();
FloatingIconView floatingView = FloatingIconView.getFloatingIconView(mLauncher, v,
- toggleVisibility, bounds, true /* isOpening */);
+ toggleVisibility, launcherIconBounds, true /* isOpening */);
Rect crop = new Rect();
Matrix matrix = new Matrix();
@@ -458,11 +467,17 @@
mDragLayer.getLocationOnScreen(dragLayerBounds);
AnimOpenProperties prop = new AnimOpenProperties(mLauncher.getResources(), mDeviceProfile,
- windowTargetBounds, bounds, v, dragLayerBounds);
+ windowTargetBounds, launcherIconBounds, v, dragLayerBounds[0], dragLayerBounds[1]);
+ int left = (int) (prop.cropCenterXStart - prop.cropWidthStart / 2);
+ int top = (int) (prop.cropCenterYStart - prop.cropHeightStart / 2);
+ int right = (int) (left + prop.cropWidthStart);
+ int bottom = (int) (top + prop.cropHeightStart);
+ // Set the crop here so we can calculate the corner radius below.
+ crop.set(left, top, right, bottom);
RectF targetBounds = new RectF(windowTargetBounds);
- RectF iconBounds = new RectF();
- RectF temp = new RectF();
+ RectF floatingIconBounds = new RectF();
+ RectF tmpRectF = new RectF();
Point tmpPos = new Point();
AnimatorSet animatorSet = new AnimatorSet();
@@ -481,68 +496,79 @@
});
final float initialWindowRadius = supportsRoundedCornersOnWindows(mLauncher.getResources())
- ? prop.startCrop / 2f : 0f;
+ ? Math.max(crop.width(), crop.height()) / 2f
+ : 0f;
final float finalWindowRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
FloatProp mDx = new FloatProp(0, prop.dX, 0, prop.xDuration, AGGRESSIVE_EASE);
FloatProp mDy = new FloatProp(0, prop.dY, 0, prop.yDuration, AGGRESSIVE_EASE);
- FloatProp mScale = new FloatProp(prop.initialAppIconScale,
+
+ FloatProp mIconScaleToFitScreen = new FloatProp(prop.initialAppIconScale,
prop.finalAppIconScale, 0, APP_LAUNCH_DURATION, EXAGGERATED_EASE);
FloatProp mIconAlpha = new FloatProp(prop.iconAlphaStart, 0f,
APP_LAUNCH_ALPHA_START_DELAY, prop.alphaDuration, LINEAR);
- FloatProp mCroppedSize = new FloatProp(prop.startCrop, prop.endCrop, 0, CROP_DURATION,
- EXAGGERATED_EASE);
+
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, finalWindowRadius, 0,
RADIUS_DURATION, EXAGGERATED_EASE);
FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0,
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectCenterX = new FloatProp(prop.cropCenterXStart, prop.cropCenterXEnd,
+ 0, CROP_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectCenterY = new FloatProp(prop.cropCenterYStart, prop.cropCenterYEnd,
+ 0, CROP_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectWidth = new FloatProp(prop.cropWidthStart, prop.cropWidthEnd, 0,
+ CROP_DURATION, EXAGGERATED_EASE);
+ FloatProp mCropRectHeight = new FloatProp(prop.cropHeightStart, prop.cropHeightEnd, 0,
+ CROP_DURATION, EXAGGERATED_EASE);
+
@Override
public void onUpdate(float percent) {
// Calculate the size of the scaled icon.
- float width = bounds.width() * mScale.value;
- float height = bounds.height() * mScale.value;
+ float iconWidth = launcherIconBounds.width() * mIconScaleToFitScreen.value;
+ float iconHeight = launcherIconBounds.height() * mIconScaleToFitScreen.value;
- // Animate the crop so that it starts off as a square.
- final int cropWidth;
- final int cropHeight;
- if (mDeviceProfile.isVerticalBarLayout()) {
- cropWidth = (int) mCroppedSize.value;
- cropHeight = windowTargetBounds.height();
- } else {
- cropWidth = windowTargetBounds.width();
- cropHeight = (int) mCroppedSize.value;
- }
- crop.set(0, 0, cropWidth, cropHeight);
+ int left = (int) (mCropRectCenterX.value - mCropRectWidth.value / 2);
+ int top = (int) (mCropRectCenterY.value - mCropRectHeight.value / 2);
+ int right = (int) (left + mCropRectWidth.value);
+ int bottom = (int) (top + mCropRectHeight.value);
+ crop.set(left, top, right, bottom);
+
+ final int windowCropWidth = crop.width();
+ final int windowCropHeight = crop.height();
// Scale the size of the icon to match the size of the window crop.
- float scaleX = width / cropWidth;
- float scaleY = height / cropHeight;
+ float scaleX = iconWidth / windowCropWidth;
+ float scaleY = iconHeight / windowCropHeight;
float scale = Math.min(1f, Math.max(scaleX, scaleY));
- float scaledCropWidth = cropWidth * scale;
- float scaledCropHeight = cropHeight * scale;
- float offsetX = (scaledCropWidth - width) / 2;
- float offsetY = (scaledCropHeight - height) / 2;
+ float scaledCropWidth = windowCropWidth * scale;
+ float scaledCropHeight = windowCropHeight * scale;
+ float offsetX = (scaledCropWidth - iconWidth) / 2;
+ float offsetY = (scaledCropHeight - iconHeight) / 2;
// Calculate the window position to match the icon position.
- temp.set(bounds);
- temp.offset(dragLayerBounds[0], dragLayerBounds[1]);
- temp.offset(mDx.value, mDy.value);
- Utilities.scaleRectFAboutCenter(temp, mScale.value);
- float windowTransX0 = temp.left - offsetX;
- float windowTransY0 = temp.top - offsetY;
+ tmpRectF.set(launcherIconBounds);
+ tmpRectF.offset(dragLayerBounds[0], dragLayerBounds[1]);
+ tmpRectF.offset(mDx.value, mDy.value);
+ Utilities.scaleRectFAboutCenter(tmpRectF, mIconScaleToFitScreen.value);
+ float windowTransX0 = tmpRectF.left - offsetX;
+ float windowTransY0 = tmpRectF.top - offsetY;
+ if (ENABLE_SHELL_STARTING_SURFACE) {
+ windowTransX0 -= crop.left * scale;
+ windowTransY0 -= crop.top * scale;
+ }
// Calculate the icon position.
- iconBounds.set(bounds);
- iconBounds.offset(mDx.value, mDy.value);
- Utilities.scaleRectFAboutCenter(iconBounds, mScale.value);
- iconBounds.left -= offsetX;
- iconBounds.top -= offsetY;
- iconBounds.right += offsetX;
- iconBounds.bottom += offsetY;
+ floatingIconBounds.set(launcherIconBounds);
+ floatingIconBounds.offset(mDx.value, mDy.value);
+ Utilities.scaleRectFAboutCenter(floatingIconBounds, mIconScaleToFitScreen.value);
+ floatingIconBounds.left -= offsetX;
+ floatingIconBounds.top -= offsetY;
+ floatingIconBounds.right += offsetX;
+ floatingIconBounds.bottom += offsetY;
SurfaceParams[] params = new SurfaceParams[appTargets.length];
for (int i = appTargets.length - 1; i >= 0; i--) {
@@ -553,7 +579,7 @@
matrix.setScale(scale, scale);
matrix.postTranslate(windowTransX0, windowTransY0);
- floatingView.update(iconBounds, mIconAlpha.value, percent, 0f,
+ floatingView.update(floatingIconBounds, mIconAlpha.value, percent, 0f,
mWindowRadius.value * scale, true /* isOpening */);
builder.withMatrix(matrix)
.withWindowCrop(crop)
@@ -561,10 +587,11 @@
.withCornerRadius(mWindowRadius.value)
.withShadowRadius(mShadowRadius.value);
} else {
- tmpPos.set(target.position.x, target.position.y);
if (target.localBounds != null) {
final Rect localBounds = target.localBounds;
tmpPos.set(target.localBounds.left, target.localBounds.top);
+ } else {
+ tmpPos.set(target.position.x, target.position.y);
}
matrix.setTranslate(tmpPos.x, tmpPos.y);
@@ -636,6 +663,24 @@
}
/**
+ * Registers remote animations used when closing apps to home screen.
+ */
+ @Override
+ public void registerRemoteTransitions() {
+ if (SEPARATE_RECENTS_ACTIVITY.get()) {
+ return;
+ }
+ if (hasControlRemoteAppTransitionPermission()) {
+ mWallpaperOpenTransitionRunner = createWallpaperOpenRunner(false /* fromUnlock */);
+ mLauncherOpenTransition = RemoteAnimationAdapterCompat.buildRemoteTransition(
+ new WrappedLauncherAnimationRunner<>(mWallpaperOpenTransitionRunner,
+ false /* startAtFrontOfQueue */));
+ mLauncherOpenTransition.addHomeOpenCheck();
+ SystemUiProxy.INSTANCE.getNoCreate().registerRemoteTransition(mLauncherOpenTransition);
+ }
+ }
+
+ /**
* Unregisters all remote animations.
*/
@Override
@@ -654,6 +699,20 @@
}
}
+ @Override
+ public void unregisterRemoteTransitions() {
+ if (SEPARATE_RECENTS_ACTIVITY.get()) {
+ return;
+ }
+ if (hasControlRemoteAppTransitionPermission()) {
+ if (mLauncherOpenTransition == null) return;
+ SystemUiProxy.INSTANCE.getNoCreate().unregisterRemoteTransition(
+ mLauncherOpenTransition);
+ mLauncherOpenTransition = null;
+ mWallpaperOpenTransitionRunner = null;
+ }
+ }
+
private boolean launcherIsATargetWithMode(RemoteAnimationTargetCompat[] targets, int mode) {
return taskIsATargetWithMode(targets, mLauncher.getTaskId(), mode);
}
@@ -721,9 +780,10 @@
RemoteAnimationTargetCompat target = appTargets[i];
SurfaceParams.Builder builder = new SurfaceParams.Builder(target.leash);
- tmpPos.set(target.position.x, target.position.y);
if (target.localBounds != null) {
tmpPos.set(target.localBounds.left, target.localBounds.top);
+ } else {
+ tmpPos.set(target.position.x, target.position.y);
}
if (target.mode == MODE_CLOSING) {
@@ -932,8 +992,15 @@
*/
static class AnimOpenProperties {
- public final float startCrop;
- public final float endCrop;
+ public final int cropCenterXStart;
+ public final int cropCenterYStart;
+ public final int cropWidthStart;
+ public final int cropHeightStart;
+
+ public final int cropCenterXEnd;
+ public final int cropCenterYEnd;
+ public final int cropWidthEnd;
+ public final int cropHeightEnd;
public final float dX;
public final float dY;
@@ -948,7 +1015,7 @@
public final float iconAlphaStart;
AnimOpenProperties(Resources r, DeviceProfile dp, Rect windowTargetBounds,
- RectF launcherIconBounds, View view, int[] dragLayerBounds) {
+ RectF launcherIconBounds, View view, int dragLayerLeft, int dragLayerTop) {
// Scale the app icon to take up the entire screen. This simplifies the math when
// animating the app window position / scale.
float smallestSize = Math.min(windowTargetBounds.height(), windowTargetBounds.width());
@@ -966,8 +1033,8 @@
finalAppIconScale = Math.max(maxScaleX, maxScaleY);
// Animate the app icon to the center of the window bounds in screen coordinates.
- float centerX = windowTargetBounds.centerX() - dragLayerBounds[0];
- float centerY = windowTargetBounds.centerY() - dragLayerBounds[1];
+ float centerX = windowTargetBounds.centerX() - dragLayerLeft;
+ float centerY = windowTargetBounds.centerY() - dragLayerTop;
dX = centerX - launcherIconBounds.centerX();
dY = centerY - launcherIconBounds.centerY();
@@ -981,15 +1048,31 @@
alphaDuration = useUpwardAnimation ? APP_LAUNCH_ALPHA_DURATION
: APP_LAUNCH_ALPHA_DOWN_DURATION;
- if (dp.isVerticalBarLayout()) {
- startCrop = windowTargetBounds.height();
- endCrop = windowTargetBounds.width();
+ if (ENABLE_SHELL_STARTING_SURFACE) {
+ iconAlphaStart = 0;
+
+ // TOOD: Share value from shell when available.
+ final float windowIconSize = Utilities.pxFromSp(108, r.getDisplayMetrics());
+
+ cropCenterXStart = windowTargetBounds.centerX();
+ cropCenterYStart = windowTargetBounds.centerY();
+
+ cropWidthStart = (int) windowIconSize;
+ cropHeightStart = (int) windowIconSize;
} else {
- startCrop = windowTargetBounds.width();
- endCrop = windowTargetBounds.height();
+ iconAlphaStart = 1;
+
+ cropWidthStart = cropHeightStart =
+ Math.min(windowTargetBounds.width(), windowTargetBounds.height());
+ cropCenterXStart = cropCenterYStart =
+ Math.min(windowTargetBounds.centerX(), windowTargetBounds.centerY());
}
- iconAlphaStart = 1f;
+ cropWidthEnd = windowTargetBounds.width();
+ cropHeightEnd = windowTargetBounds.height();
+
+ cropCenterXEnd = windowTargetBounds.centerX();
+ cropCenterYEnd = windowTargetBounds.centerY();
}
}
}
diff --git a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
index 9ce196e..3343cf5 100644
--- a/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
+++ b/quickstep/src/com/android/launcher3/search/DeviceSearchAdapterProvider.java
@@ -23,6 +23,8 @@
import android.view.LayoutInflater;
import android.view.ViewGroup;
+import com.android.app.search.LayoutType;
+import com.android.app.search.ResultType;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView;
@@ -78,10 +80,10 @@
SearchTargetHandler
payloadResultView =
(SearchTargetHandler) holder.itemView;
- if (FeatureFlags.SEARCH_TARGET_LEGACY.get()) {
+ if (!FeatureFlags.USE_SEARCH_API.get()) {
payloadResultView.applySearchTarget(item.getSearchTargetLegacy());
} else {
- payloadResultView.applySearchTarget(item.getSearchTarget());
+ payloadResultView.applySearchTarget(item.getSearchTarget(), item.getInlineItems());
}
}
@@ -123,9 +125,24 @@
* Returns -1 if viewType is not found
*/
public int getViewTypeForSearchTarget(SearchTarget t) {
- //TODO: Replace with values from :SearchUi
- if (t.getResultType() == 1 && t.getLayoutType().equals("icon")) {
- return VIEW_TYPE_SEARCH_ICON;
+ if (t.getLayoutType().equals(LayoutType.TEXT_HEADER)) {
+ return VIEW_TYPE_SEARCH_CORPUS_TITLE;
+ }
+ switch (t.getResultType()) {
+ case ResultType.APPLICATION:
+ if (t.getLayoutType().equals(LayoutType.ICON_SINGLE_VERTICAL_TEXT)) {
+ return VIEW_TYPE_SEARCH_ICON;
+ }
+ break;
+ case ResultType.SETTING:
+ if (t.getLayoutType().equals(LayoutType.ICON_SLICE)) {
+ return VIEW_TYPE_SEARCH_SLICE;
+ }
+ return VIEW_TYPE_SEARCH_ROW;
+ case ResultType.SHORTCUT:
+ return VIEW_TYPE_SEARCH_ICON_ROW;
+ case ResultType.PLAY:
+ return VIEW_TYPE_SEARCH_ROW_WITH_BUTTON;
}
return -1;
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
index 258d977..65ac3f9 100644
--- a/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchAdapterItem.java
@@ -32,12 +32,16 @@
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.systemui.plugins.shared.SearchTargetLegacy;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Extension of AdapterItem that contains an extra payload specific to item
*/
public class SearchAdapterItem extends AllAppsGridAdapter.AdapterItem {
private SearchTargetLegacy mSearchTargetLegacy;
private SearchTarget mSearchTarget;
+ private List<SearchTarget> mInlineItems = new ArrayList<>();
private static final int AVAILABLE_FOR_ACCESSIBILITY = VIEW_TYPE_SEARCH_ROW_WITH_BUTTON
@@ -65,6 +69,9 @@
return mSearchTarget;
}
+ public List<SearchTarget> getInlineItems() {
+ return mInlineItems;
+ }
@Override
protected boolean isCountedForAccessibility() {
return (AVAILABLE_FOR_ACCESSIBILITY & viewType) == viewType;
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
index e4d737c..d5fe0e8 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIcon.java
@@ -31,10 +31,12 @@
import android.view.View;
import android.view.ViewGroup;
+import com.android.app.search.ResultType;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsStore;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.AppInfo;
@@ -46,6 +48,7 @@
import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
import com.android.systemui.plugins.shared.SearchTargetLegacy;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -128,10 +131,27 @@
}
}
+ /**
+ * Applies {@link SearchTarget} to view. registers a consumer after a corresponding
+ * {@link ItemInfoWithIcon} is created
+ */
+ public void applySearchTarget(SearchTarget searchTarget, List<SearchTarget> inlineItems,
+ Consumer<ItemInfoWithIcon> cb) {
+ mOnItemInfoChanged = cb;
+ applySearchTarget(searchTarget, inlineItems);
+ }
+
@Override
- public void applySearchTarget(SearchTarget searchTarget) {
- prepareUsingApp(new ComponentName(searchTarget.getPackageName(),
- searchTarget.getExtras().getString("class")), searchTarget.getUserHandle());
+ public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ switch (parentTarget.getResultType()) {
+ case ResultType.APPLICATION:
+ prepareUsingApp(new ComponentName(parentTarget.getPackageName(),
+ parentTarget.getExtras().getString("class")), parentTarget.getUserHandle());
+ break;
+ case ResultType.SHORTCUT:
+ prepareUsingShortcutInfo(parentTarget.getShortcutInfo());
+ break;
+ }
}
private void prepareUsingApp(ComponentName componentName, UserHandle userHandle) {
@@ -185,7 +205,9 @@
@Override
public void handleSelection(int eventType) {
mLauncher.getItemOnClickListener().onClick(this);
- reportEvent(eventType);
+ if (!FeatureFlags.USE_SEARCH_API.get()) {
+ reportEvent(eventType);
+ }
}
private void reportEvent(int eventType) {
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
index 8c491d2..80d543a 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultIconRow.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.app.search.SearchTarget;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ShortcutInfo;
@@ -32,6 +33,7 @@
import androidx.annotation.Nullable;
+import com.android.app.search.ResultType;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
@@ -112,6 +114,16 @@
}
@Override
+ public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ mResultIcon.applySearchTarget(parentTarget, children, this);
+ if (parentTarget.getResultType() == ResultType.SHORTCUT) {
+ ShortcutInfo shortcutInfo = parentTarget.getShortcutInfo();
+ setProviderDetails(new ComponentName(shortcutInfo.getPackage(), ""),
+ shortcutInfo.getUserHandle());
+ }
+ }
+
+ @Override
public void applySearchTarget(SearchTargetLegacy searchTarget) {
mSearchTarget = searchTarget;
mResultIcon.applySearchTarget(searchTarget, this);
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java b/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java
index 3bb821f..840bde9 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultPlayItem.java
@@ -17,6 +17,8 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.app.search.SearchAction;
+import android.app.search.SearchTarget;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -27,8 +29,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -43,12 +43,11 @@
import com.android.launcher3.R;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.util.Themes;
-import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
-import com.android.systemui.plugins.shared.SearchTargetLegacy;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
+import java.util.List;
/**
* A View representing a PlayStore item.
@@ -67,9 +66,8 @@
private TextView[] mDetailViews = new TextView[3];
private Button mPreviewButton;
private String mPackageName;
- private boolean mIsInstantGame;
-
- private SearchTargetLegacy mSearchTarget;
+ private Intent mIntent;
+ private Intent mSecondaryIntent;
public SearchResultPlayItem(Context context) {
@@ -93,7 +91,7 @@
mIconView = findViewById(R.id.icon);
mTitleView = findViewById(R.id.title_view);
mPreviewButton = findViewById(R.id.try_button);
- mPreviewButton.setOnClickListener(view -> launchInstantGame());
+ mPreviewButton.setOnClickListener(view -> launchIntent(mSecondaryIntent));
mDetailViews[0] = findViewById(R.id.detail_0);
mDetailViews[1] = findViewById(R.id.detail_1);
mDetailViews[2] = findViewById(R.id.detail_2);
@@ -101,9 +99,59 @@
ViewGroup.LayoutParams iconParams = mIconView.getLayoutParams();
iconParams.height = mDeviceProfile.allAppsIconSizePx;
iconParams.width = mDeviceProfile.allAppsIconSizePx;
- setOnClickListener(view -> handleSelection(SearchTargetEventLegacy.SELECT));
+ setOnClickListener(view -> launchIntent(mIntent));
}
+ private void showIfNecessary(TextView textView, @Nullable String string) {
+ if (string == null || string.isEmpty()) {
+ textView.setVisibility(GONE);
+ } else {
+ textView.setText(string);
+ textView.setVisibility(VISIBLE);
+ }
+ }
+
+ private void launchIntent(Intent intent) {
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+
+ @Override
+ public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ if (parentTarget.getPackageName().equals(mPackageName)) {
+ return;
+ }
+ mPackageName = parentTarget.getPackageName();
+ SearchAction action = parentTarget.getSearchAction();
+ mTitleView.setText(action.getTitle());
+ showIfNecessary(mDetailViews[0], action.getSubtitle().toString());
+ mIntent = action.getIntent();
+
+ mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
+ loadIcon(action.getIcon().getUri().toString());
+
+ mSecondaryIntent = children.size() == 1 ? children.get(0).getSearchAction().getIntent()
+ : null;
+ mPreviewButton.setVisibility(mSecondaryIntent == null ? GONE : VISIBLE);
+ }
+
+ private void loadIcon(String iconUrl) {
+ UI_HELPER_EXECUTOR.execute(() -> {
+ try {
+ URL url = new URL(iconUrl);
+ URLConnection con = url.openConnection();
+ 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)));
+ mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ });
+ }
private Bitmap getRoundedBitmap(Bitmap bitmap) {
final int iconSize = bitmap.getWidth();
@@ -124,80 +172,4 @@
});
return output;
}
-
-
- @Override
- public void applySearchTarget(SearchTargetLegacy searchTarget) {
- mSearchTarget = searchTarget;
- Bundle bundle = searchTarget.getExtras();
- SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
- if (bundle.getString("package", "").equals(mPackageName)) {
- return;
- }
- mIsInstantGame = bundle.getBoolean("instant_game", false);
- mPackageName = bundle.getString("package");
- mPreviewButton.setVisibility(mIsInstantGame ? VISIBLE : GONE);
- mTitleView.setText(bundle.getString("title"));
-// TODO: Should use a generic type to get values b/165320033
- showIfNecessary(mDetailViews[0], bundle.getString("price"));
- showIfNecessary(mDetailViews[1], bundle.getString("rating"));
-
- mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
- UI_HELPER_EXECUTOR.execute(() -> {
- try {
- URL url = new URL(bundle.getString("icon_url"));
- 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)));
- mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
- } catch (IOException e) {
- e.printStackTrace();
- }
- });
- }
-
- private void showIfNecessary(TextView textView, @Nullable String string) {
- if (string == null || string.isEmpty()) {
- textView.setVisibility(GONE);
- } else {
- textView.setText(string);
- textView.setVisibility(VISIBLE);
- }
- }
-
- @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="
- + mPackageName));
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(i);
- logSearchEvent(eventType);
- }
-
- private void launchInstantGame() {
- if (!mIsInstantGame) return;
- Intent intent = new Intent(Intent.ACTION_VIEW);
- String referrer = "Pixel_Launcher";
- String id = mPackageName;
- String deepLinkUrl = "market://details?id=" + id + "&launch=true&referrer=" + referrer;
- intent.setPackage("com.android.vending");
- intent.setData(Uri.parse(deepLinkUrl));
- intent.putExtra("overlay", true);
- intent.putExtra("callerId", getContext().getPackageName());
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- logSearchEvent(SearchTargetEventLegacy.CHILD_SELECT);
- }
-
- private void logSearchEvent(int eventType) {
- SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
- new SearchTargetEventLegacy.Builder(mSearchTarget, eventType).build());
- }
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java b/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java
index 80ad305..bf50b67 100644
--- a/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java
+++ b/quickstep/src/com/android/launcher3/search/SearchResultSettingsSlice.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.search;
+import android.app.search.SearchTarget;
import android.content.Context;
import android.net.Uri;
import android.util.AttributeSet;
@@ -35,6 +36,8 @@
import com.android.systemui.plugins.shared.SearchTargetEventLegacy;
import com.android.systemui.plugins.shared.SearchTargetLegacy;
+import java.util.List;
+
/**
* A slice view wrapper with settings app icon at start
*/
@@ -89,6 +92,18 @@
}
@Override
+ public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ reset();
+ try {
+ mSliceLiveData = mLauncher.getLiveSearchManager().getSliceForUri(
+ parentTarget.getSliceUri());
+ mSliceLiveData.observe(mLauncher, mSliceView);
+ } catch (Exception ex) {
+ Log.e(TAG, "unable to bind slice", ex);
+ }
+ }
+
+ @Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
mSliceView.setOnSliceActionListener(this);
diff --git a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
index eb40938..ccc38db 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSectionHeaderView.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.search;
+import android.app.search.SearchTarget;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.TextView;
@@ -23,6 +24,8 @@
import com.android.systemui.plugins.shared.SearchTargetLegacy;
+import java.util.List;
+
/**
* Header text view that shows a title for a given section in All apps search
*/
@@ -53,4 +56,10 @@
setVisibility(INVISIBLE);
}
}
+
+ @Override
+ public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ setText(parentTarget.getSearchAction().getTitle());
+ setVisibility(VISIBLE);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
new file mode 100644
index 0000000..6585213
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/search/SearchServicePipeline.java
@@ -0,0 +1,137 @@
+/*
+ * 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.search;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.app.search.Query;
+import android.app.search.SearchContext;
+import android.app.search.SearchSession;
+import android.app.search.SearchTarget;
+import android.app.search.SearchUiManager;
+import android.content.Context;
+import android.os.CancellationSignal;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.app.search.ResultType;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AllAppsSectionDecorator;
+import com.android.launcher3.allapps.search.SearchPipeline;
+import com.android.launcher3.allapps.search.SearchSectionInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * Search pipeline utilizing {@link android.app.search.SearchUiManager}
+ */
+public class SearchServicePipeline implements SearchPipeline {
+ private static final int SUPPORTED_RESULT_TYPES =
+ ResultType.APPLICATION | ResultType.SHORTCUT | ResultType.PLAY | ResultType.PEOPLE
+ | ResultType.SETTING;
+ private static final int REQUEST_TIMEOUT = 200;
+ private static final String TAG = "SearchServicePipeline";
+
+
+ private final Context mContext;
+ private final SearchSession mSession;
+ private final DeviceSearchAdapterProvider mAdapterProvider;
+
+ private boolean mCanceled = false;
+
+
+ public SearchServicePipeline(Context context, DeviceSearchAdapterProvider adapterProvider) {
+ mContext = context;
+ mAdapterProvider = adapterProvider;
+ SearchUiManager manager = context.getSystemService(SearchUiManager.class);
+ mSession = manager.createSearchSession(
+ new SearchContext(SUPPORTED_RESULT_TYPES, REQUEST_TIMEOUT, null));
+ }
+
+ @Override
+ public void query(String input, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
+ CancellationSignal cancellationSignal) {
+ mCanceled = false;
+ Query query = new Query(input, System.currentTimeMillis(), null);
+ mSession.query(query, UI_HELPER_EXECUTOR, items -> {
+ if (!mCanceled) {
+ callback.accept(this.onResult(items));
+ }
+ Log.w(TAG, "Ignoring results due to cancel signal");
+ });
+ }
+
+ /**
+ * Given A list of search Targets, pairs a group of search targets to a AdapterItem that can
+ * be inflated in AllAppsRecyclerView
+ */
+ private ArrayList<AllAppsGridAdapter.AdapterItem> onResult(List<SearchTarget> searchTargets) {
+ HashMap<String, SearchAdapterItem> adapterMap = new LinkedHashMap<>();
+ List<SearchTarget> unmappedChildren = new ArrayList<>();
+ SearchSectionInfo section = new SearchSectionInfo();
+ section.setDecorationHandler(
+ new AllAppsSectionDecorator.SectionDecorationHandler(mContext, true));
+ for (SearchTarget target : searchTargets) {
+ if (!TextUtils.isEmpty(target.getParentId())) {
+ if (!addChildToParent(target, adapterMap)) {
+ unmappedChildren.add(target);
+ }
+ continue;
+ }
+ int viewType = mAdapterProvider.getViewTypeForSearchTarget(target);
+ if (viewType != -1) {
+ SearchAdapterItem adapterItem = new SearchAdapterItem(target, viewType);
+ adapterItem.searchSectionInfo = section;
+ adapterMap.put(target.getId(), adapterItem);
+ }
+ }
+ for (SearchTarget s : unmappedChildren) {
+ if (!addChildToParent(s, adapterMap)) {
+ Log.w(TAG,
+ "Unable to pair child " + s.getId() + " to parent " + s.getParentId());
+ }
+ }
+ return new ArrayList<>(adapterMap.values());
+ }
+
+ /**
+ * Adds a child SearchTarget to a collection of searchTarget children with a shared parentId.
+ * Returns false if no parent searchTarget with id=$parentId does not exists.
+ */
+ private boolean addChildToParent(SearchTarget target, HashMap<String, SearchAdapterItem> map) {
+ if (!map.containsKey(target.getParentId())) return false;
+ map.get(target.getParentId()).getInlineItems().add(target);
+ return true;
+ }
+
+ /**
+ * Unregister callbacks and destroy search session
+ */
+ public void destroy() {
+ mSession.destroy();
+ }
+
+ /**
+ * Cancels current ongoing search request.
+ */
+ public void cancel() {
+ mCanceled = true;
+ }
+}
diff --git a/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java b/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java
index 8306e3b..6fc0046 100644
--- a/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java
+++ b/quickstep/src/com/android/launcher3/search/SearchSettingsRowView.java
@@ -19,6 +19,8 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.app.search.SearchAction;
+import android.app.search.SearchTarget;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -97,6 +99,14 @@
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
}
+ @Override
+ public void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
+ SearchAction action = parentTarget.getSearchAction();
+ mIconView.setContentDescription(action.getTitle());
+ showIfAvailable(mTitleView, action.getTitle().toString());
+ showIfAvailable(mBreadcrumbsView, action.getSubtitle().toString());
+ }
+
private void showIfAvailable(TextView view, @Nullable String string) {
if (TextUtils.isEmpty(string)) {
view.setVisibility(GONE);
diff --git a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
index 9ff057f..e72578d 100644
--- a/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
+++ b/quickstep/src/com/android/launcher3/search/SearchTargetHandler.java
@@ -20,6 +20,8 @@
import com.android.systemui.plugins.shared.SearchTargetLegacy;
+import java.util.List;
+
/**
* An interface for supporting dynamic search results
*/
@@ -28,13 +30,14 @@
/**
* Update view using values from {@link SearchTargetLegacy}
*/
- void applySearchTarget(SearchTargetLegacy searchTarget);
+ default void applySearchTarget(SearchTargetLegacy searchTarget) {
+ }
+
/**
* Update view using values from {@link SearchTargetLegacy}
*/
- default void applySearchTarget(SearchTarget searchTarget){
-
+ default void applySearchTarget(SearchTarget parentTarget, List<SearchTarget> children) {
}
/**
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
index 3be1ced..a00ce56 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java
@@ -64,7 +64,7 @@
import com.android.launcher3.uioverrides.touchcontrollers.StatusBarTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.TransposedQuickSwitchTouchController;
-import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarToOverviewTouchController;
+import com.android.launcher3.uioverrides.touchcontrollers.TwoButtonNavbarTouchController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
@@ -220,6 +220,7 @@
@Override
public void onDestroy() {
super.onDestroy();
+ getAppsView().getSearchUiManager().destroy();
if (mHotseatPredictionController != null) {
mHotseatPredictionController.destroy();
mHotseatPredictionController = null;
@@ -284,7 +285,7 @@
list.add(new NoButtonNavbarToOverviewTouchController(this));
break;
case TWO_BUTTONS:
- list.add(new TwoButtonNavbarToOverviewTouchController(this));
+ list.add(new TwoButtonNavbarTouchController(this));
list.add(getDeviceProfile().isVerticalBarLayout()
? new TransposedQuickSwitchTouchController(this)
: new QuickSwitchTouchController(this));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarToOverviewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarToOverviewTouchController.java
deleted file mode 100644
index ff4bfe6..0000000
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarToOverviewTouchController.java
+++ /dev/null
@@ -1,89 +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.uioverrides.touchcontrollers;
-
-import static com.android.launcher3.LauncherState.NORMAL;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
-
-import android.view.MotionEvent;
-
-import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherState;
-import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
-import com.android.launcher3.touch.AbstractStateChangeTouchController;
-import com.android.launcher3.touch.SingleAxisSwipeDetector;
-import com.android.quickstep.SystemUiProxy;
-
-/**
- * Touch controller for handling edge swipes in 2-button mode
- */
-public class TwoButtonNavbarToOverviewTouchController extends AbstractStateChangeTouchController {
-
- private static final String TAG = "2BtnNavbarTouchCtrl";
-
- public TwoButtonNavbarToOverviewTouchController(Launcher l) {
- super(l, l.getDeviceProfile().isVerticalBarLayout()
- ? SingleAxisSwipeDetector.HORIZONTAL : SingleAxisSwipeDetector.VERTICAL);
- }
-
- @Override
- protected boolean canInterceptTouch(MotionEvent ev) {
- if (mCurrentAnimation != null) {
- // If we are already animating from a previous state, we can intercept.
- return true;
- }
- if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
- return false;
- }
- return mLauncher.isInState(NORMAL) && (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
- }
-
- @Override
- protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
- boolean draggingFromNav =
- mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
- return draggingFromNav ? OVERVIEW : NORMAL;
- } else {
- return isDragTowardPositive ? OVERVIEW : NORMAL;
- }
- }
-
- @Override
- protected float getShiftRange() {
- return mLauncher.getDeviceProfile().isVerticalBarLayout()
- ? mLauncher.getDragLayer().getWidth() : super.getShiftRange();
- }
-
- @Override
- protected float initCurrentAnimation(@AnimationFlags int animComponent) {
- float range = getShiftRange();
- long maxAccuracy = (long) (2 * range);
- mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
- maxAccuracy, animComponent);
- return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
- }
-
- @Override
- protected void onSwipeInteractionCompleted(LauncherState targetState) {
- super.onSwipeInteractionCompleted(targetState);
- if (mStartState == NORMAL && targetState == OVERVIEW) {
- SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
- }
- }
-}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
new file mode 100644
index 0000000..6271a44
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/TwoButtonNavbarTouchController.java
@@ -0,0 +1,138 @@
+/*
+ * 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.uioverrides.touchcontrollers;
+
+import static com.android.launcher3.AbstractFloatingView.TYPE_ALL_APPS_EDU;
+import static com.android.launcher3.AbstractFloatingView.getOpenView;
+import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
+
+import android.animation.ValueAnimator;
+import android.view.MotionEvent;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.states.StateAnimationConfig.AnimationFlags;
+import com.android.launcher3.touch.AbstractStateChangeTouchController;
+import com.android.launcher3.touch.SingleAxisSwipeDetector;
+import com.android.quickstep.SystemUiProxy;
+import com.android.quickstep.views.AllAppsEduView;
+
+/**
+ * Touch controller for handling edge swipes in 2-button mode
+ */
+public class TwoButtonNavbarTouchController extends AbstractStateChangeTouchController {
+
+ private static final int MAX_NUM_SWIPES_TO_TRIGGER_EDU = 3;
+
+ private static final String TAG = "2BtnNavbarTouchCtrl";
+
+ private final boolean mIsTransposed;
+
+ // If true, we will finish the current animation instantly on second touch.
+ private boolean mFinishFastOnSecondTouch;
+
+ private int mContinuousTouchCount = 0;
+
+ public TwoButtonNavbarTouchController(Launcher l) {
+ super(l, l.getDeviceProfile().isVerticalBarLayout()
+ ? SingleAxisSwipeDetector.HORIZONTAL : SingleAxisSwipeDetector.VERTICAL);
+ mIsTransposed = l.getDeviceProfile().isVerticalBarLayout();
+ }
+
+ @Override
+ protected boolean canInterceptTouch(MotionEvent ev) {
+ boolean canIntercept = canInterceptTouchInternal(ev);
+ if (!canIntercept) {
+ mContinuousTouchCount = 0;
+ }
+ return canIntercept;
+ }
+
+ private boolean canInterceptTouchInternal(MotionEvent ev) {
+ if (mCurrentAnimation != null) {
+ if (mFinishFastOnSecondTouch) {
+ mCurrentAnimation.getAnimationPlayer().end();
+ }
+
+ // If we are already animating from a previous state, we can intercept.
+ return true;
+ }
+ if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
+ return false;
+ }
+ if ((ev.getEdgeFlags() & EDGE_NAV_BAR) == 0) {
+ return false;
+ }
+ if (!mIsTransposed && mLauncher.isInState(OVERVIEW)) {
+ return true;
+ }
+ return mLauncher.isInState(NORMAL);
+ }
+
+ @Override
+ protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
+ if (mIsTransposed) {
+ boolean draggingFromNav =
+ mLauncher.getDeviceProfile().isSeascape() == isDragTowardPositive;
+ return draggingFromNav ? OVERVIEW : NORMAL;
+ } else {
+ return isDragTowardPositive ^ (fromState == OVERVIEW) ? OVERVIEW : NORMAL;
+ }
+ }
+
+ @Override
+ protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
+ LauncherState targetState, float velocity, boolean isFling) {
+ super.updateSwipeCompleteAnimation(animator, expectedDuration, targetState,
+ velocity, isFling);
+ mFinishFastOnSecondTouch = !mIsTransposed && mFromState == NORMAL;
+ }
+
+ @Override
+ protected float getShiftRange() {
+ return mLauncher.getDeviceProfile().isVerticalBarLayout()
+ ? mLauncher.getDragLayer().getWidth() : super.getShiftRange();
+ }
+
+ @Override
+ protected float initCurrentAnimation(@AnimationFlags int animComponent) {
+ float range = getShiftRange();
+ long maxAccuracy = (long) (2 * range);
+ mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(mToState,
+ maxAccuracy, animComponent);
+ return (mLauncher.getDeviceProfile().isSeascape() ? 2 : -2) / range;
+ }
+
+ @Override
+ protected void onSwipeInteractionCompleted(LauncherState targetState) {
+ super.onSwipeInteractionCompleted(targetState);
+ if (!mIsTransposed) {
+ mContinuousTouchCount++;
+ }
+ if (mStartState == NORMAL && targetState == OVERVIEW) {
+ SystemUiProxy.INSTANCE.get(mLauncher).onOverviewShown(true, TAG);
+ } else if (targetState == NORMAL
+ && mContinuousTouchCount >= MAX_NUM_SWIPES_TO_TRIGGER_EDU) {
+ mContinuousTouchCount = 0;
+ if (getOpenView(mLauncher, TYPE_ALL_APPS_EDU) == null) {
+ AllAppsEduView.show(mLauncher);
+ }
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index b4f20d1..5bed929 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -396,4 +396,9 @@
pa.addFloat(recentsView, FULLSCREEN_PROGRESS, 1, 0, LINEAR);
}
}
+
+ /** Called when OverviewService is bound to this process */
+ void onOverviewServiceBound() {
+ // Do nothing
+ }
}
diff --git a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
index 901040d..a80c111 100644
--- a/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -22,12 +22,14 @@
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.Matrix;
@@ -126,7 +128,11 @@
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
Intent intent = new Intent(mGestureState.getHomeIntent());
mActiveAnimationFactory.addGestureContract(intent);
- mContext.startActivity(intent, options.toBundle());
+ try {
+ mContext.startActivity(intent, options.toBundle());
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ mContext.startActivity(createHomeIntent());
+ }
return mActiveAnimationFactory;
}
diff --git a/quickstep/src/com/android/quickstep/ImageActionsApi.java b/quickstep/src/com/android/quickstep/ImageActionsApi.java
index ba8ba33..b04905c 100644
--- a/quickstep/src/com/android/quickstep/ImageActionsApi.java
+++ b/quickstep/src/com/android/quickstep/ImageActionsApi.java
@@ -22,11 +22,14 @@
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.ImageActionUtils.persistBitmapAndStartActivity;
+import android.app.prediction.AppTarget;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -96,4 +99,12 @@
ImageActionUtils.saveScreenshot(mSystemUiProxy, screenshot, screenshotBounds, visibleInsets,
task);
}
+
+ /**
+ * Share the image when user taps on overview share targets.
+ */
+ @UiThread
+ public void shareImage(RectF rectF, ShortcutInfo shortcutInfo, AppTarget appTarget) {
+ ImageActionUtils.shareImage(mContext, mBitmapSupplier, rectF, shortcutInfo, appTarget, TAG);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
index 9f435f5..7630bc4 100644
--- a/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/LauncherActivityInterface.java
@@ -270,4 +270,11 @@
+ res.getDimensionPixelSize(R.dimen.overview_actions_height);
return actionsHeight;
}
-}
\ No newline at end of file
+
+ @Override
+ void onOverviewServiceBound() {
+ final BaseQuickstepLauncher activity = getCreatedActivity();
+ if (activity == null) return;
+ activity.getAppTransitionManager().registerRemoteTransitions();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index ec6e303..985389e 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -175,9 +175,12 @@
return;
}
- InteractionJankMonitorWrapper.begin(
- mActivityInterface.getCreatedActivity().getRootView(),
- InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ final T activity = mActivityInterface.getCreatedActivity();
+ if (activity != null) {
+ InteractionJankMonitorWrapper.begin(
+ activity.getRootView(),
+ InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
+ }
// Otherwise, start overview.
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 7bf7712..fb8f9fe 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -20,6 +20,7 @@
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
@@ -74,9 +75,7 @@
public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
mContext = context;
mDeviceState = deviceState;
- mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mCurrentHomeIntent = createHomeIntent();
mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
ComponentName myHomeComponent =
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index c37fd84..7beeae2 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -21,6 +21,7 @@
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_DURATION;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.STATUS_BAR_TRANSITION_PRE_DELAY;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.testing.TestProtocol.OVERVIEW_STATE_ORDINAL;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
@@ -309,9 +310,7 @@
}
public void startHome() {
- startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ startActivity(createHomeIntent());
}
@Override
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
index 74b56e9..4301377 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationDeviceState.java
@@ -24,6 +24,7 @@
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
+import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
@@ -383,6 +384,7 @@
*/
public boolean canStartSystemGesture() {
boolean canStartWithNavHidden = (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
+ || (mSystemUiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0
|| mRotationTouchHelper.isTaskListFrozen();
return canStartWithNavHidden
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index a214d81..ca55468 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -35,6 +35,7 @@
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteTransitionCompat;
/**
* Holds the reference to SystemUI.
@@ -408,4 +409,26 @@
}
}
}
+
+ @Override
+ public void registerRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.registerRemoteTransition(remoteTransition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ }
+
+ @Override
+ public void unregisterRemoteTransition(RemoteTransitionCompat remoteTransition) {
+ if (mSystemUiProxy != null) {
+ try {
+ mSystemUiProxy.unregisterRemoteTransition(remoteTransition);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed call registerRemoteTransition");
+ }
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index e59035c..1f7cec5 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -136,6 +136,12 @@
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy);
TouchInteractionService.this.initInputMonitor();
preloadOverview(true /* fromInit */);
+ mDeviceState.runOnUserUnlocked(() -> {
+ final BaseActivityInterface ai =
+ mOverviewComponentObserver.getActivityInterface();
+ if (ai == null) return;
+ ai.onOverviewServiceBound();
+ });
});
sIsInitialized = true;
}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index 5aaea00..85ecab1 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -19,6 +19,7 @@
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_UP;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.util.VelocityUtils.PX_PER_MS;
@@ -203,9 +204,7 @@
public void onAnimationEnd(Animator animation) {
if (dismissTask) {
// For now, just start the home intent so user is prompted to unlock the device.
- mContext.startActivity(new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+ mContext.startActivity(createHomeIntent());
mHomeLaunched = true;
}
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
index 924b32c..864e08d 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OverviewWithoutFocusInputConsumer.java
@@ -15,10 +15,12 @@
*/
package com.android.quickstep.inputconsumers;
+import static com.android.launcher3.Utilities.createHomeIntent;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND;
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOME_GESTURE;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.graphics.PointF;
import android.view.MotionEvent;
@@ -77,7 +79,11 @@
@Override
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
- mContext.startActivity(mGestureState.getHomeIntent());
+ try {
+ mContext.startActivity(mGestureState.getHomeIntent());
+ } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
+ mContext.startActivity(createHomeIntent());
+ }
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
BaseActivity activity = BaseDraggingActivity.fromContext(mContext);
int state = (mGestureState != null && mGestureState.getEndTarget() != null)
diff --git a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
index e998e9a..d022085 100644
--- a/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
+++ b/quickstep/src/com/android/quickstep/util/ImageActionUtils.java
@@ -22,15 +22,19 @@
import static com.android.launcher3.util.Executors.THREAD_POOL_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+import android.app.prediction.AppTarget;
import android.content.ClipData;
import android.content.ClipDescription;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.ShortcutInfo;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Picture;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.net.Uri;
import android.util.Log;
@@ -71,6 +75,34 @@
}
/**
+ * Launch the activity to share image for overview sharing. This is to share cropped bitmap
+ * with specific share targets (with shortcutInfo and appTarget) rendered in overview.
+ */
+ @UiThread
+ public static void shareImage(Context context, Supplier<Bitmap> bitmapSupplier, RectF rectF,
+ ShortcutInfo shortcutInfo, AppTarget appTarget, String tag) {
+ if (bitmapSupplier.get() == null) {
+ return;
+ }
+ Rect crop = new Rect();
+ rectF.round(crop);
+ Intent intent = new Intent();
+ Uri uri = getImageUri(bitmapSupplier.get(), crop, context, tag);
+ ClipData clipdata = new ClipData(new ClipDescription("content",
+ new String[]{"image/png"}),
+ new ClipData.Item(uri));
+ intent.setAction(Intent.ACTION_SEND)
+ .setComponent(new ComponentName(appTarget.getPackageName(), appTarget.getClassName()))
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(FLAG_GRANT_READ_URI_PERMISSION)
+ .setType("image/png")
+ .putExtra(Intent.EXTRA_STREAM, uri)
+ .putExtra(Intent.EXTRA_SHORTCUT_ID, shortcutInfo.getId())
+ .setClipData(clipdata);
+ context.startActivity(intent);
+ }
+
+ /**
* Launch the activity to share image.
*/
@UiThread
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f281296..2f2b566 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -72,7 +72,6 @@
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.Property;
import android.util.SparseBooleanArray;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -1515,7 +1514,9 @@
}
int scrollDiff = newScroll[i] - oldScroll[i] + offset;
if (scrollDiff != 0) {
- Property translationProperty = mOrientationHandler.getPrimaryViewTranslate();
+ FloatProperty translationProperty = child instanceof TaskView
+ ? ((TaskView) child).getPrimaryFillDismissGapTranslationProperty()
+ : mOrientationHandler.getPrimaryViewTranslate();
ResourceProvider rp = DynamicResource.provider(mActivity);
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
@@ -1927,7 +1928,11 @@
? modalLeftOffsetSize
: modalRightOffsetSize;
float totalTranslation = translation + modalTranslation;
- mOrientationHandler.getPrimaryViewTranslate().set(getChildAt(i),
+ View child = getChildAt(i);
+ FloatProperty translationProperty = child instanceof TaskView
+ ? ((TaskView) child).getPrimaryTaskOffsetTranslationProperty()
+ : mOrientationHandler.getPrimaryViewTranslate();
+ translationProperty.set(child,
totalTranslation * mOrientationHandler.getPrimaryTranslationDirectionFactor());
}
updateCurveProperties();
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index d94e623..b791d29 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -152,6 +152,58 @@
}
};
+ private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_X =
+ new FloatProperty<TaskView>("fillDismissGapTranslationX") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setFillDismissGapTranslationX(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mFillDismissGapTranslationX;
+ }
+ };
+
+ private static final FloatProperty<TaskView> FILL_DISMISS_GAP_TRANSLATION_Y =
+ new FloatProperty<TaskView>("fillDismissGapTranslationY") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setFillDismissGapTranslationY(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mFillDismissGapTranslationY;
+ }
+ };
+
+ private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_X =
+ new FloatProperty<TaskView>("taskOffsetTranslationX") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setTaskOffsetTranslationX(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mTaskOffsetTranslationX;
+ }
+ };
+
+ private static final FloatProperty<TaskView> TASK_OFFSET_TRANSLATION_Y =
+ new FloatProperty<TaskView>("taskOffsetTranslationY") {
+ @Override
+ public void setValue(TaskView taskView, float v) {
+ taskView.setTaskOffsetTranslationY(v);
+ }
+
+ @Override
+ public Float get(TaskView taskView) {
+ return taskView.mTaskOffsetTranslationY;
+ }
+ };
+
private final OnAttachStateChangeListener mTaskMenuStateListener =
new OnAttachStateChangeListener() {
@Override
@@ -179,6 +231,13 @@
private final FullscreenDrawParams mCurrentFullscreenParams;
private final StatefulActivity mActivity;
+ // Various causes of changing primary translation, which we aggregate to setTranslationX/Y().
+ // TODO: We should do this for secondary translation properties as well.
+ private float mFillDismissGapTranslationX;
+ private float mFillDismissGapTranslationY;
+ private float mTaskOffsetTranslationX;
+ private float mTaskOffsetTranslationY;
+
private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0;
private float mFocusTransitionProgress = 1;
@@ -601,6 +660,8 @@
protected void resetViewTransforms() {
setCurveScale(1);
+ mFillDismissGapTranslationX = mTaskOffsetTranslationX = 0f;
+ mFillDismissGapTranslationY = mTaskOffsetTranslationY = 0f;
setTranslationX(0f);
setTranslationY(0f);
setTranslationZ(0);
@@ -745,6 +806,44 @@
return mCurveScale;
}
+ private void setFillDismissGapTranslationX(float x) {
+ mFillDismissGapTranslationX = x;
+ applyTranslationX();
+ }
+
+ private void setFillDismissGapTranslationY(float y) {
+ mFillDismissGapTranslationY = y;
+ applyTranslationY();
+ }
+
+ private void setTaskOffsetTranslationX(float x) {
+ mTaskOffsetTranslationX = x;
+ applyTranslationX();
+ }
+
+ private void setTaskOffsetTranslationY(float y) {
+ mTaskOffsetTranslationY = y;
+ applyTranslationY();
+ }
+
+ private void applyTranslationX() {
+ setTranslationX(mFillDismissGapTranslationX + mTaskOffsetTranslationX);
+ }
+
+ private void applyTranslationY() {
+ setTranslationY(mFillDismissGapTranslationY + mTaskOffsetTranslationY);
+ }
+
+ public FloatProperty<TaskView> getPrimaryFillDismissGapTranslationProperty() {
+ return getPagedOrientationHandler().getPrimaryValue(
+ FILL_DISMISS_GAP_TRANSLATION_X, FILL_DISMISS_GAP_TRANSLATION_Y);
+ }
+
+ public FloatProperty<TaskView> getPrimaryTaskOffsetTranslationProperty() {
+ return getPagedOrientationHandler().getPrimaryValue(
+ TASK_OFFSET_TRANSLATION_X, TASK_OFFSET_TRANSLATION_Y);
+ }
+
@Override
public boolean hasOverlappingRendering() {
// TODO: Clip-out the icon region from the thumbnail, since they are overlapping.
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index b9e0f62..475953b 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -23,6 +23,7 @@
import static com.android.launcher3.tapl.TestHelpers.getHomeIntentInPackage;
import static com.android.launcher3.tapl.TestHelpers.getLauncherInMyProcess;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_ACTIVITY_TIMEOUT;
+import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_BROADCAST_TIMEOUT_SECS;
import static com.android.launcher3.ui.AbstractLauncherUiTest.DEFAULT_UI_TIMEOUT;
import static com.android.launcher3.ui.AbstractLauncherUiTest.resolveSystemApp;
import static com.android.launcher3.ui.AbstractLauncherUiTest.startAppFast;
@@ -66,6 +67,8 @@
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
@@ -112,11 +115,16 @@
@Override
public void evaluate() throws Throwable {
TestCommandReceiver.callCommand(TestCommandReceiver.ENABLE_TEST_LAUNCHER);
+ OverviewUpdateHandler updateHandler =
+ MAIN_EXECUTOR.submit(OverviewUpdateHandler::new).get();
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
getLauncherCommand(mOtherLauncherActivity));
+ updateHandler.mChangeCounter
+ .await(DEFAULT_BROADCAST_TIMEOUT_SECS, TimeUnit.SECONDS);
try {
base.evaluate();
} finally {
+ MAIN_EXECUTOR.submit(updateHandler::destroy).get();
TestCommandReceiver.callCommand(TestCommandReceiver.DISABLE_TEST_LAUNCHER);
UiDevice.getInstance(getInstrumentation()).executeShellCommand(
getLauncherCommand(getLauncherInMyProcess()));
@@ -241,4 +249,30 @@
private int getTaskCount(RecentsActivity recents) {
return recents.<RecentsView>getOverviewPanel().getTaskViewCount();
}
+
+ private class OverviewUpdateHandler {
+
+ final RecentsAnimationDeviceState mRads;
+ final OverviewComponentObserver mObserver;
+ final CountDownLatch mChangeCounter;
+
+ OverviewUpdateHandler() {
+ Context ctx = getInstrumentation().getTargetContext();
+ mRads = new RecentsAnimationDeviceState(ctx);
+ mObserver = new OverviewComponentObserver(ctx, mRads);
+ mChangeCounter = new CountDownLatch(1);
+ if (mObserver.getHomeIntent().getComponent()
+ .getPackageName().equals(mOtherLauncherActivity.packageName)) {
+ // Home already same
+ mChangeCounter.countDown();
+ } else {
+ mObserver.setOverviewChangeListener(b -> mChangeCounter.countDown());
+ }
+ }
+
+ void destroy() {
+ mObserver.onDestroy();
+ mRads.destroy();
+ }
+ }
}
diff --git a/res/layout/launcher_preview_layout.xml b/res/layout/launcher_preview_layout.xml
index 4a20c70..1691680 100644
--- a/res/layout/launcher_preview_layout.xml
+++ b/res/layout/launcher_preview_layout.xml
@@ -29,23 +29,8 @@
launcher:containerType="workspace"
launcher:pageIndicator="@+id/page_indicator"/>
- <com.android.launcher3.Hotseat
+ <include
android:id="@+id/hotseat"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:importantForAccessibility="no"
- android:theme="@style/HomeScreenElementTheme"
- launcher:containerType="hotseat" />
-
- <com.android.launcher3.InsettableFrameLayout
- android:id="@+id/apps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
-
- <include
- android:id="@id/search_container_all_apps"
- layout="@layout/search_container_all_apps"/>
-
- </com.android.launcher3.InsettableFrameLayout>
+ layout="@layout/hotseat" />
</com.android.launcher3.InsettableFrameLayout>
\ No newline at end of file
diff --git a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
index f019a20..fdddab4 100644
--- a/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
+++ b/robolectric_tests/src/com/android/launcher3/util/LauncherUIHelper.java
@@ -18,6 +18,8 @@
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.launcher3.Utilities.createHomeIntent;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -46,10 +48,7 @@
*/
public static String getLauncherClassName() {
Context context = RuntimeEnvironment.application;
- Intent homeIntent = new Intent(Intent.ACTION_MAIN)
- .addCategory(Intent.CATEGORY_HOME)
- .setPackage(context.getPackageName())
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Intent homeIntent = createHomeIntent().setPackage(context.getPackageName());
List<ResolveInfo> launchers = context.getPackageManager()
.queryIntentActivities(homeIntent, 0);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5b55c4b..0274775 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1596,6 +1596,7 @@
mOverlayManager.onActivityDestroyed(this);
mAppTransitionManager.unregisterRemoteAnimations();
+ mAppTransitionManager.unregisterRemoteTransitions();
mUserChangedCallbackCloseable.close();
mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
mLiveSearchManager.stop();
diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java
index 24e0d14..ac3ad9f 100644
--- a/src/com/android/launcher3/LauncherAppTransitionManager.java
+++ b/src/com/android/launcher3/LauncherAppTransitionManager.java
@@ -67,4 +67,18 @@
public void unregisterRemoteAnimations() {
// Do nothing
}
+
+ /**
+ * Registers remote transitions for certain system transitions.
+ */
+ public void registerRemoteTransitions() {
+ // Do nothing
+ }
+
+ /**
+ * Unregisters all remote transitions.
+ */
+ public void unregisterRemoteTransitions() {
+ // Do nothing
+ }
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index df5d234..8066aa6 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -461,6 +461,15 @@
}
/**
+ * Returns an intent for starting the default home activity
+ */
+ public static Intent createHomeIntent() {
+ return new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ /**
* Wraps a message with a TTS span, so that a different message is spoken than
* what is getting displayed.
* @param msg original message
diff --git a/src/com/android/launcher3/allapps/SearchUiManager.java b/src/com/android/launcher3/allapps/SearchUiManager.java
index aa056a0..f926086 100644
--- a/src/com/android/launcher3/allapps/SearchUiManager.java
+++ b/src/com/android/launcher3/allapps/SearchUiManager.java
@@ -59,6 +59,11 @@
Interpolator interpolator);
/**
+ * Called when activity is destroyed. Used to close search system services
+ */
+ default void destroy(){}
+
+ /**
* Returns true if the QSB should be visible for the given set of visible elements
*/
default boolean isQsbVisible(int visibleElements) {
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 2455706..81174d8 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -98,9 +98,8 @@
public static final BooleanFlag ENABLE_DEVICE_SEARCH = getDebugFlag(
"ENABLE_DEVICE_SEARCH", false, "Allows on device search in all apps");
- public static final BooleanFlag SEARCH_TARGET_LEGACY = getDebugFlag(
- "SEARCH_TARGET_LEGACY", true,
- "Use SearchTarget provided by plugin lib (only during migration)");
+ public static final BooleanFlag USE_SEARCH_API = getDebugFlag(
+ "USE_SEARCH_API", true, "Use SearchUIManager api for device search");
public static final BooleanFlag DISABLE_INITIAL_IME_IN_ALLAPPS = getDebugFlag(
"DISABLE_INITIAL_IME_IN_ALLAPPS", false, "Disable default IME state in all apps");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index efc1201..3a9986e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -64,7 +64,6 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceLayoutManager;
-import com.android.launcher3.allapps.SearchUiManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.icons.BaseIconFactory;
@@ -512,16 +511,6 @@
mWorkspace.addViewToCellLayout(qsb, 0, R.id.search_container_workspace, lp, true);
}
- // Setup search view
- SearchUiManager searchUiManager = mRootView.findViewById(R.id.search_container_all_apps);
- mRootView.findViewById(R.id.apps_view).setTranslationY(
- mDp.heightPx - searchUiManager.getScrollRangeDelta(mInsets));
- ViewGroup searchView = (ViewGroup) searchUiManager;
- searchView.setEnabled(false);
- for (int i = 0; i < searchView.getChildCount(); i++) {
- searchView.getChildAt(i).setEnabled(false);
- }
-
measureView(mRootView, mDp.widthPx, mDp.heightPx);
dispatchVisibilityAggregated(mRootView, true);
measureView(mRootView, mDp.widthPx, mDp.heightPx);
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index c57c3e4..d4fa278 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -26,7 +26,6 @@
import android.content.pm.LauncherActivityInfo;
import android.content.pm.LauncherApps;
import android.os.LocaleList;
-import android.os.Process;
import android.os.UserHandle;
import android.util.Log;
@@ -162,7 +161,7 @@
/** Updates the given PackageInstallInfo's associated AppInfo's installation info. */
public List<AppInfo> updatePromiseInstallInfo(PackageInstallInfo installInfo) {
List<AppInfo> updatedAppInfos = new ArrayList<>();
- UserHandle user = Process.myUserHandle();
+ UserHandle user = installInfo.user;
for (int i = data.size() - 1; i >= 0; i--) {
final AppInfo appInfo = data.get(i);
final ComponentName tgtComp = appInfo.getTargetComponent();
@@ -177,7 +176,8 @@
appInfo.setProgressLevel(installInfo);
updatedAppInfos.add(appInfo);
- } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED) {
+ } else if (installInfo.state == PackageInstallInfo.STATUS_FAILED
+ && !appInfo.isAppStartable()) {
removeApp(i);
}
}
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index ecf4f36..ba28e82 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -51,7 +51,7 @@
public static final int REQUEST_ROTATE = 1;
public static final int REQUEST_LOCK = 2;
- private final Activity mActivity;
+ private Activity mActivity;
private final SharedPreferences mSharedPrefs;
private boolean mIgnoreAutoRotateSettings;
@@ -95,6 +95,7 @@
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String s) {
+ if (mDestroyed) return;
boolean wasRotationEnabled = mHomeRotationEnabled;
mHomeRotationEnabled = mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY,
getAllowRotationDefaultValue());
@@ -141,6 +142,7 @@
public void destroy() {
if (!mDestroyed) {
mDestroyed = true;
+ mActivity = null;
if (mSharedPrefs != null) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
}
diff --git a/src/com/android/launcher3/util/OnboardingPrefs.java b/src/com/android/launcher3/util/OnboardingPrefs.java
index 539768e..14df0f3 100644
--- a/src/com/android/launcher3/util/OnboardingPrefs.java
+++ b/src/com/android/launcher3/util/OnboardingPrefs.java
@@ -34,7 +34,6 @@
public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
- public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
public static final String HOTSEAT_LONGPRESS_TIP_SEEN = "launcher.hotseat_longpress_tip_seen";