Merge "UNREVIEWED: TAPL prototype for in-lab flake test" into ub-launcher3-edmonton-polish
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f7f496f..92852d2 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -259,8 +259,7 @@
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
- setEnableFreeScroll(true);
- setEnableOverscroll(true);
+ enableFreeScroll(true);
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index c4ccd96..508e5bb 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -121,7 +121,6 @@
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
});
setOutlineProvider(new TaskOutlineProvider(getResources()));
- setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
}
@Override
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
index af87550..a9b4955 100644
--- a/src/com/android/launcher3/LauncherScroller.java
+++ b/src/com/android/launcher3/LauncherScroller.java
@@ -459,13 +459,13 @@
return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
}
- public int getSplineFlingDuration(float velocity) {
+ private int getSplineFlingDuration(float velocity) {
final double l = getSplineDeceleration(velocity);
final double decelMinusOne = DECELERATION_RATE - 1.0;
return (int) (1000.0 * Math.exp(l / decelMinusOne));
}
- public double getSplineFlingDistance(float velocity) {
+ private double getSplineFlingDistance(float velocity) {
final double l = getSplineDeceleration(velocity);
final double decelMinusOne = DECELERATION_RATE - 1.0;
return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 8b415d6..3c7c1aa 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -382,7 +382,9 @@
}
private void onStateTransitionStart(LauncherState state) {
- mState.onStateDisabled(mLauncher);
+ if (mState != state) {
+ mState.onStateDisabled(mLauncher);
+ }
mState = state;
mState.onStateEnabled(mLauncher);
mLauncher.getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 0cb6539..db5dc66 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -63,6 +63,7 @@
protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+ public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
// OverScroll constants
private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -82,6 +83,7 @@
public static final int INVALID_RESTORE_PAGE = -1001;
private boolean mFreeScroll = false;
+ private boolean mSettleOnPageInFreeScroll = false;
protected int mFlingThresholdVelocity;
protected int mMinFlingVelocity;
@@ -354,6 +356,17 @@
@Override
public void scrollTo(int x, int y) {
+ // In free scroll mode, we clamp the scrollX
+ if (mFreeScroll) {
+ // If the scroller is trying to move to a location beyond the maximum allowed
+ // in the free scroll mode, we make sure to end the scroll operation.
+ if (!mScroller.isFinished() && (x > mMaxScrollX || x < 0)) {
+ forceFinishScroller(false);
+ }
+
+ x = Utilities.boundToRange(x, 0, mMaxScrollX);
+ }
+
mUnboundedScrollX = x;
boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
@@ -1013,7 +1026,13 @@
dampedOverScroll(amount);
}
- protected void setEnableFreeScroll(boolean freeScroll) {
+
+ protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
+ setEnableFreeScroll(true);
+ mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
+ }
+
+ private void setEnableFreeScroll(boolean freeScroll) {
boolean wasFreeScroll = mFreeScroll;
mFreeScroll = freeScroll;
@@ -1022,6 +1041,8 @@
} else if (wasFreeScroll) {
snapToPage(getNextPage());
}
+
+ setEnableOverscroll(!freeScroll);
}
protected void setEnableOverscroll(boolean enable) {
@@ -1134,15 +1155,42 @@
snapToDestination();
}
} else {
- int unscaledScrollX = getScrollX() - (int) Math.round(
- mScroller.getSplineFlingDistance(velocityX) * Math.signum(velocityX));
- int duration = mScroller.getSplineFlingDuration(velocityX);
- int finalPage = getPageNearestToCenterOfScreen(unscaledScrollX);
- if ((isFling || isSignificantMove) && (finalPage != mCurrentPage)) {
- snapToPage(finalPage, duration);
- } else {
- snapToDestination();
+ if (!mScroller.isFinished()) {
+ abortScrollerAnimation(true);
}
+
+ float scaleX = getScaleX();
+ int vX = (int) (-velocityX * scaleX);
+ int initialScrollX = (int) (getScrollX() * scaleX);
+
+ mScroller.setInterpolator(mDefaultInterpolator);
+ mScroller.fling(initialScrollX,
+ getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
+ int unscaledScrollX = (int) (mScroller.getFinalX() / scaleX);
+ mNextPage = getPageNearestToCenterOfScreen(unscaledScrollX);
+ int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
+ int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
+ if (mSettleOnPageInFreeScroll && unscaledScrollX > 0
+ && unscaledScrollX < mMaxScrollX) {
+ // If scrolling ends in the half of the added space that is closer to the
+ // end, settle to the end. Otherwise snap to the nearest page.
+ // If flinging past one of the ends, don't change the velocity as it will
+ // get stopped at the end anyway.
+ final int finalX = unscaledScrollX < firstPageScroll / 2 ?
+ 0 :
+ unscaledScrollX > (lastPageScroll + mMaxScrollX) / 2 ?
+ mMaxScrollX :
+ getScrollForPage(mNextPage);
+
+ mScroller.setFinalX((int) (finalX * getScaleX()));
+ // Ensure the scroll/snap doesn't happen too fast;
+ int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
+ - mScroller.getDuration();
+ if (extraScrollDuration > 0) {
+ mScroller.extendDuration(extraScrollDuration);
+ }
+ }
+ invalidate();
}
onScrollInteractionEnd();
} else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 8993978..fdf32af 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -312,6 +312,12 @@
}
}
+ @Override
+ public int getCanvasClipTopForOverscroll() {
+ // Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped.
+ return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop();
+ }
+
private void rebindAdapters(boolean showTabs) {
rebindAdapters(showTabs, false /* force */);
}
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 89ba72a..1fa233a 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -182,7 +182,12 @@
* The bitmap is also visually normalized with other icons.
*/
public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) {
- return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
+ return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false, null);
+ }
+
+ public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
+ boolean isInstantApp) {
+ return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null);
}
/**
@@ -191,8 +196,10 @@
* The bitmap is also visually normalized with other icons.
*/
public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
- boolean isInstantApp) {
- float[] scale = new float[1];
+ boolean isInstantApp, float [] scale) {
+ if (scale == null) {
+ scale = new float[1];
+ }
icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale);
Bitmap bitmap = createIconBitmap(icon, scale[0]);
if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index b26d39f..8e29df1 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -16,6 +16,10 @@
package com.android.launcher3.qsb;
+import static android.appwidget.AppWidgetManager.ACTION_APPWIDGET_BIND;
+import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
+import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER;
+
import android.app.Activity;
import android.app.Fragment;
import android.app.SearchManager;
@@ -74,11 +78,12 @@
/**
* A fragment to display the QSB.
*/
- public static class QsbFragment extends Fragment implements View.OnClickListener {
+ public static class QsbFragment extends Fragment {
+ public static final int QSB_WIDGET_HOST_ID = 1026;
private static final int REQUEST_BIND_QSB = 1;
- private static final String QSB_WIDGET_ID = "qsb_widget_id";
+ protected String mKeyWidgetId = "qsb_widget_id";
private QsbWidgetHost mQsbWidgetHost;
private AppWidgetProviderInfo mWidgetInfo;
private QsbWidgetHostView mQsb;
@@ -90,10 +95,15 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mQsbWidgetHost = new QsbWidgetHost(getActivity());
+ mQsbWidgetHost = createHost();
mOrientation = getContext().getResources().getConfiguration().orientation;
}
+ protected QsbWidgetHost createHost() {
+ return new QsbWidgetHost(getActivity(), QSB_WIDGET_HOST_ID,
+ (c) -> new QsbWidgetHostView(c));
+ }
+
private FrameLayout mWrapper;
@Override
@@ -110,24 +120,16 @@
}
private View createQsb(ViewGroup container) {
- Activity activity = getActivity();
- mWidgetInfo = getSearchWidgetProvider(activity);
+ mWidgetInfo = getSearchWidgetProvider();
if (mWidgetInfo == null) {
// There is no search provider, just show the default widget.
- return QsbWidgetHostView.getDefaultView(container);
+ return getDefaultView(container, false /* show setup icon */);
}
-
+ Bundle opts = createBindOptions();
+ Activity activity = getActivity();
AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity);
- InvariantDeviceProfile idp = LauncherAppState.getIDP(activity);
- Bundle opts = new Bundle();
- Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(activity, idp.numColumns, 1, null);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
- opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
-
- int widgetId = Utilities.getPrefs(activity).getInt(QSB_WIDGET_ID, -1);
+ int widgetId = Utilities.getPrefs(activity).getInt(mKeyWidgetId, -1);
AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
boolean isWidgetBound = (widgetInfo != null) &&
widgetInfo.provider.equals(mWidgetInfo.provider);
@@ -166,32 +168,18 @@
}
// Return a default widget with setup icon.
- View v = QsbWidgetHostView.getDefaultView(container);
- View setupButton = v.findViewById(R.id.btn_qsb_setup);
- setupButton.setVisibility(View.VISIBLE);
- setupButton.setOnClickListener(this);
- return v;
+ return getDefaultView(container, true /* show setup icon */);
}
private void saveWidgetId(int widgetId) {
- Utilities.getPrefs(getActivity()).edit().putInt(QSB_WIDGET_ID, widgetId).apply();
- }
-
- @Override
- public void onClick(View view) {
- // Start intent for bind the widget
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
- // Allocate a new widget id for QSB
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mQsbWidgetHost.allocateAppWidgetId());
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider);
- startActivityForResult(intent, REQUEST_BIND_QSB);
+ Utilities.getPrefs(getActivity()).edit().putInt(mKeyWidgetId, widgetId).apply();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_BIND_QSB) {
if (resultCode == Activity.RESULT_OK) {
- saveWidgetId(data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1));
+ saveWidgetId(data.getIntExtra(EXTRA_APPWIDGET_ID, -1));
rebindFragment();
} else {
mQsbWidgetHost.deleteHost();
@@ -228,48 +216,83 @@
public boolean isQsbEnabled() {
return FeatureFlags.QSB_ON_FIRST_SCREEN;
}
- }
- /**
- * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
- * provided by the same package which is set to be global search activity.
- * If widgetCategory is not supported, or no such widget is found, returns the first widget
- * provided by the package.
- */
- public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
- SearchManager searchManager =
- (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
- ComponentName searchComponent = searchManager.getGlobalSearchActivity();
- if (searchComponent == null) return null;
- String providerPkg = searchComponent.getPackageName();
+ protected Bundle createBindOptions() {
+ InvariantDeviceProfile idp = LauncherAppState.getIDP(getActivity());
- AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+ Bundle opts = new Bundle();
+ Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(getActivity(),
+ idp.numColumns, 1, null);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+ return opts;
+ }
- AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
- if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
- if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
- return info;
- } else if (defaultWidgetForSearchPackage == null) {
- defaultWidgetForSearchPackage = info;
+ protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {
+ // Return a default widget with setup icon.
+ View v = QsbWidgetHostView.getDefaultView(container);
+ if (showSetupIcon) {
+ View setupButton = v.findViewById(R.id.btn_qsb_setup);
+ setupButton.setVisibility(View.VISIBLE);
+ setupButton.setOnClickListener((v2) -> startActivityForResult(
+ new Intent(ACTION_APPWIDGET_BIND)
+ .putExtra(EXTRA_APPWIDGET_ID, mQsbWidgetHost.allocateAppWidgetId())
+ .putExtra(EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider),
+ REQUEST_BIND_QSB));
+ }
+ return v;
+ }
+
+ /**
+ * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
+ * provided by the same package which is set to be global search activity.
+ * If widgetCategory is not supported, or no such widget is found, returns the first widget
+ * provided by the package.
+ */
+ protected AppWidgetProviderInfo getSearchWidgetProvider() {
+ SearchManager searchManager =
+ (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
+ ComponentName searchComponent = searchManager.getGlobalSearchActivity();
+ if (searchComponent == null) return null;
+ String providerPkg = searchComponent.getPackageName();
+
+ AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+
+ AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getActivity());
+ for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
+ if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
+ if ((info.widgetCategory
+ & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
+ return info;
+ } else if (defaultWidgetForSearchPackage == null) {
+ defaultWidgetForSearchPackage = info;
+ }
}
}
+ return defaultWidgetForSearchPackage;
}
- return defaultWidgetForSearchPackage;
}
- private static class QsbWidgetHost extends AppWidgetHost {
+ public static class QsbWidgetHost extends AppWidgetHost {
- private static final int QSB_WIDGET_HOST_ID = 1026;
+ private final WidgetViewFactory mViewFactory;
- public QsbWidgetHost(Context context) {
- super(context, QSB_WIDGET_HOST_ID);
+ public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory) {
+ super(context, hostId);
+ mViewFactory = viewFactory;
}
@Override
protected AppWidgetHostView onCreateView(
Context context, int appWidgetId, AppWidgetProviderInfo appWidget) {
- return new QsbWidgetHostView(context);
+ return mViewFactory.newView(context);
}
}
+
+ public interface WidgetViewFactory {
+
+ QsbWidgetHostView newView(Context context);
+ }
}
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index 7d8a4db..407812d 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -58,13 +58,9 @@
try {
super.onLayout(changed, left, top, right, bottom);
} catch (final RuntimeException e) {
- post(new Runnable() {
- @Override
- public void run() {
- // Update the widget with 0 Layout id, to reset the view to error view.
- updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
- }
- });
+ // Update the widget with 0 Layout id, to reset the view to error view.
+ post(() -> updateAppWidget(
+ new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0)));
}
}
@@ -76,24 +72,16 @@
@Override
protected View getDefaultView() {
View v = super.getDefaultView();
- v.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- Launcher.getLauncher(getContext()).startSearch("", false, null, true);
- }
- });
+ v.setOnClickListener((v2) ->
+ Launcher.getLauncher(getContext()).startSearch("", false, null, true));
return v;
}
public static View getDefaultView(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.qsb_default_view, parent, false);
- v.findViewById(R.id.btn_qsb_search).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- Launcher.getLauncher(view.getContext()).startSearch("", false, null, true);
- }
- });
+ v.findViewById(R.id.btn_qsb_search).setOnClickListener((v2) ->
+ Launcher.getLauncher(v2.getContext()).startSearch("", false, null, true));
return v;
}
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 5022d65..b0313ce 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -54,7 +54,7 @@
}
};
- private final SparseBooleanArray mSpringViews = new SparseBooleanArray();
+ protected final SparseBooleanArray mSpringViews = new SparseBooleanArray();
private final SpringAnimation mSpring;
private float mDampedScrollShift = 0;
@@ -85,12 +85,24 @@
invalidate();
}
+ /**
+ * Used to clip the canvas when drawing child views during overscroll.
+ */
+ public int getCanvasClipTopForOverscroll() {
+ return 0;
+ }
+
@Override
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
+ int saveCount = canvas.save();
+
+ canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight());
canvas.translate(0, mDampedScrollShift);
boolean result = super.drawChild(canvas, child, drawingTime);
- canvas.translate(0, -mDampedScrollShift);
+
+ canvas.restoreToCount(saveCount);
+
return result;
}
return super.drawChild(canvas, child, drawingTime);