Merge "Adding 'delightful pagination' to folders, removed old animation and now have regular scrolling for navigating pages." into tm-qpr-dev
diff --git a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
index 351a3bc..c54d119 100644
--- a/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
+++ b/quickstep/src/com/android/launcher3/appprediction/PredictionRowView.java
@@ -33,6 +33,7 @@
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.anim.AlphaUpdateListener;
@@ -117,9 +118,14 @@
@Override
public int getExpectedHeight() {
- return getVisibility() == GONE ? 0
- : mActivityContext.getDeviceProfile().allAppsCellHeightPx + getPaddingTop()
- + getPaddingBottom();
+ DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
+ int iconHeight = deviceProfile.allAppsIconSizePx;
+ int iconPadding = deviceProfile.allAppsIconDrawablePaddingPx;
+ int textHeight = Utilities.calculateTextHeight(deviceProfile.allAppsIconTextSizePx);
+ int verticalPadding = getResources().getDimensionPixelSize(
+ R.dimen.all_apps_predicted_icon_vertical_padding);
+ int totalHeight = iconHeight + iconPadding + textHeight + verticalPadding * 2;
+ return getVisibility() == GONE ? 0 : totalHeight + getPaddingTop() + getPaddingBottom();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 4e635a7..24bb393 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1140,6 +1140,13 @@
boolean isCancel) {
long duration = MAX_SWIPE_DURATION;
float currentShift = mCurrentShift.value;
+ boolean recentsVisible = mRecentsView != null
+ && (mRecentsView.getWindowVisibility() == View.VISIBLE);
+ if (!recentsVisible) {
+ // We've hit a case where Launcher is been stopped mid-gesture, in this case, force
+ // a LAST_TASK end target
+ isCancel = true;
+ }
final GestureEndTarget endTarget = calculateEndTarget(velocity, endVelocity,
isFling, isCancel);
// Set the state, but don't notify until the animation completes
@@ -1219,7 +1226,7 @@
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
// or resumeLastTask().
- if (mRecentsView != null) {
+ if (recentsVisible) {
ActiveGestureLog.INSTANCE.trackEvent(ActiveGestureErrorDetector.GestureEvent
.SET_ON_PAGE_TRANSITION_END_CALLBACK);
mRecentsView.setOnPageTransitionEndCallback(
diff --git a/quickstep/src/com/android/quickstep/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java
index 6071bc8..cfcfce0 100644
--- a/quickstep/src/com/android/quickstep/util/ViewCapture.java
+++ b/quickstep/src/com/android/quickstep/util/ViewCapture.java
@@ -67,6 +67,10 @@
private static final String TAG = "ViewCapture";
+ // These flags are copies of two private flags in the View class.
+ private static final int PFLAG_INVALIDATED = 0x80000000;
+ private static final int PFLAG_DIRTY_MASK = 0x00200000;
+
// Number of frames to keep in memory
private static final int MEMORY_SIZE = 2000;
// Initial size of the reference pool. This is at least be 5 * total number of views in
@@ -187,6 +191,7 @@
private final ViewRef mViewRef = new ViewRef();
private int mFrameIndexBg = -1;
+ private boolean mIsFirstFrame = true;
private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
@@ -205,6 +210,7 @@
Message m = Message.obtain(mHandler);
m.obj = mViewRef.next;
mHandler.sendMessage(m);
+ mIsFirstFrame = false;
Trace.endSection();
}
@@ -214,9 +220,9 @@
*/
@WorkerThread
private boolean captureViewPropertiesBg(Message msg) {
- ViewRef start = (ViewRef) msg.obj;
+ ViewRef viewRefStart = (ViewRef) msg.obj;
long time = msg.getWhen();
- if (start == null) {
+ if (viewRefStart == null) {
return false;
}
mFrameIndexBg++;
@@ -227,12 +233,11 @@
ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
- ViewPropertyRef result = null;
+ ViewPropertyRef resultStart = null;
ViewPropertyRef resultEnd = null;
- ViewRef current = start;
- ViewRef last = start;
- while (current != null) {
+ ViewRef viewRefEnd = viewRefStart;
+ while (viewRefEnd != null) {
ViewPropertyRef propertyRef = recycle;
if (propertyRef == null) {
propertyRef = new ViewPropertyRef();
@@ -241,24 +246,64 @@
propertyRef.next = null;
}
- propertyRef.transfer(current);
- last = current;
- current = current.next;
+ ViewPropertyRef copy = null;
+ if (viewRefEnd.childCount < 0) {
+ copy = findInLastFrame(viewRefEnd.view.hashCode());
+ viewRefEnd.childCount = (copy != null) ? copy.childCount : 0;
+ }
+ viewRefEnd.transferTo(propertyRef);
- if (result == null) {
- result = propertyRef;
- resultEnd = result;
+ if (resultStart == null) {
+ resultStart = propertyRef;
+ resultEnd = resultStart;
} else {
resultEnd.next = propertyRef;
- resultEnd = propertyRef;
+ resultEnd = resultEnd.next;
}
+
+ if (copy != null) {
+ int pending = copy.childCount;
+ while (pending > 0) {
+ copy = copy.next;
+ pending = pending - 1 + copy.childCount;
+
+ propertyRef = recycle;
+ if (propertyRef == null) {
+ propertyRef = new ViewPropertyRef();
+ } else {
+ recycle = recycle.next;
+ propertyRef.next = null;
+ }
+
+ copy.transferTo(propertyRef);
+
+ resultEnd.next = propertyRef;
+ resultEnd = resultEnd.next;
+ }
+ }
+
+ if (viewRefEnd.next == null) {
+ // The compiler will complain about using a non-final variable from
+ // an outer class in a lambda if we pass in viewRefEnd directly.
+ final ViewRef finalViewRefEnd = viewRefEnd;
+ MAIN_EXECUTOR.execute(() -> addToPool(viewRefStart, finalViewRefEnd));
+ break;
+ }
+ viewRefEnd = viewRefEnd.next;
}
- mNodesBg[mFrameIndexBg] = result;
- ViewRef end = last;
- MAIN_EXECUTOR.execute(() -> addToPool(start, end));
+ mNodesBg[mFrameIndexBg] = resultStart;
return true;
}
+ private ViewPropertyRef findInLastFrame(int hashCode) {
+ int lastFrameIndex = (mFrameIndexBg == 0) ? MEMORY_SIZE - 1 : mFrameIndexBg - 1;
+ ViewPropertyRef viewPropertyRef = mNodesBg[lastFrameIndex];
+ while (viewPropertyRef != null && viewPropertyRef.hashCode != hashCode) {
+ viewPropertyRef = viewPropertyRef.next;
+ }
+ return viewPropertyRef;
+ }
+
void attachToRoot() {
if (mRoot.isAttachedToWindow()) {
mRoot.getViewTreeObserver().addOnDrawListener(this);
@@ -314,8 +359,16 @@
ref.view = view;
start.next = ref;
if (view instanceof ViewGroup) {
- ViewRef result = ref;
ViewGroup parent = (ViewGroup) view;
+ // If a view has not changed since the last frame, we will copy
+ // its children from the last processed frame's data.
+ if ((view.mPrivateFlags & (PFLAG_INVALIDATED | PFLAG_DIRTY_MASK)) == 0
+ && !mIsFirstFrame) {
+ // A negative child count is the signal to copy this view from the last frame.
+ ref.childCount = -parent.getChildCount();
+ return ref;
+ }
+ ViewRef result = ref;
int childCount = ref.childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
result = captureViewTree(parent.getChildAt(i), result);
@@ -349,31 +402,27 @@
public ViewPropertyRef next;
- public void transfer(ViewRef viewRef) {
- childCount = viewRef.childCount;
-
- View view = viewRef.view;
- viewRef.view = null;
-
- clazz = view.getClass();
- hashCode = view.hashCode();
- id = view.getId();
- left = view.getLeft();
- top = view.getTop();
- right = view.getRight();
- bottom = view.getBottom();
- scrollX = view.getScrollX();
- scrollY = view.getScrollY();
-
- translateX = view.getTranslationX();
- translateY = view.getTranslationY();
- scaleX = view.getScaleX();
- scaleY = view.getScaleY();
- alpha = view.getAlpha();
-
- visibility = view.getVisibility();
- willNotDraw = view.willNotDraw();
- elevation = view.getElevation();
+ public void transferTo(ViewPropertyRef out) {
+ out.clazz = this.clazz;
+ out.hashCode = this.hashCode;
+ out.childCount = this.childCount;
+ out.id = this.id;
+ out.left = this.left;
+ out.top = this.top;
+ out.right = this.right;
+ out.bottom = this.bottom;
+ out.scrollX = this.scrollX;
+ out.scrollY = this.scrollY;
+ out.scaleX = this.scaleX;
+ out.scaleY = this.scaleY;
+ out.translateX = this.translateX;
+ out.translateY = this.translateY;
+ out.alpha = this.alpha;
+ out.visibility = this.visibility;
+ out.willNotDraw = this.willNotDraw;
+ out.clipChildren = this.clipChildren;
+ out.next = this.next;
+ out.elevation = this.elevation;
}
/**
@@ -420,6 +469,33 @@
public View view;
public int childCount = 0;
public ViewRef next;
+
+ public void transferTo(ViewPropertyRef out) {
+ out.childCount = this.childCount;
+
+ View view = this.view;
+ this.view = null;
+
+ out.clazz = view.getClass();
+ out.hashCode = view.hashCode();
+ out.id = view.getId();
+ out.left = view.getLeft();
+ out.top = view.getTop();
+ out.right = view.getRight();
+ out.bottom = view.getBottom();
+ out.scrollX = view.getScrollX();
+ out.scrollY = view.getScrollY();
+
+ out.translateX = view.getTranslationX();
+ out.translateY = view.getTranslationY();
+ out.scaleX = view.getScaleX();
+ out.scaleY = view.getScaleY();
+ out.alpha = view.getAlpha();
+ out.elevation = view.getElevation();
+
+ out.visibility = view.getVisibility();
+ out.willNotDraw = view.willNotDraw();
+ }
}
private static final class ViewIdProvider {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 808b900..b00794f 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1244,7 +1244,7 @@
protected void onPageEndTransition() {
super.onPageEndTransition();
ActiveGestureLog.INSTANCE.addLog(
- "onPageEndTransition: current page index updated", mCurrentPage);
+ "onPageEndTransition: current page index updated", getNextPage());
if (isClearAllHidden() && !mActivity.getDeviceProfile().isTablet) {
mActionsView.updateDisabledFlags(OverviewActionsView.DISABLED_SCROLLING, false);
}
diff --git a/res/layout/all_apps_personal_work_tabs.xml b/res/layout/all_apps_personal_work_tabs.xml
index d15b906..4459c87 100644
--- a/res/layout/all_apps_personal_work_tabs.xml
+++ b/res/layout/all_apps_personal_work_tabs.xml
@@ -22,6 +22,7 @@
android:layout_gravity="center_horizontal"
android:paddingTop="@dimen/all_apps_tabs_vertical_padding"
android:paddingBottom="@dimen/all_apps_tabs_vertical_padding"
+ android:layout_marginTop="@dimen/all_apps_tabs_margin_top"
android:orientation="horizontal"
style="@style/TextHeadline">
diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml
index 709160d..3a58b85 100644
--- a/res/values-ky/strings.xml
+++ b/res/values-ky/strings.xml
@@ -85,7 +85,7 @@
<string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> телефон чалууларды аткарууга уруксаты жок"</string>
<string name="gadget_error_text" msgid="740356548025791839">"Виджет жүктөлбөй жатат"</string>
<string name="gadget_setup_text" msgid="8348374825537681407">"Виджеттин жөндөөлөрү"</string>
- <string name="gadget_complete_setup_text" msgid="309040266978007925">"Жөндөп бүтүү үчүн таптап коюңуз"</string>
+ <string name="gadget_complete_setup_text" msgid="309040266978007925">"Аягына чейин тууралоо үчүн басып коюңуз"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
<string name="folder_hint_text" msgid="5174843001373488816">"Аталышын түзөтүү"</string>
<string name="disabled_app_label" msgid="6673129024321402780">"<xliff:g id="APP_NAME">%1$s</xliff:g> өчүрүлгөн"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e244941..5dae9f2 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -121,6 +121,7 @@
<dimen name="all_apps_work_profile_tab_footer_bottom_padding">20dp</dimen>
<dimen name="all_apps_tabs_button_horizontal_padding">4dp</dimen>
<dimen name="all_apps_tabs_vertical_padding">6dp</dimen>
+ <dimen name="all_apps_tabs_margin_top">8dp</dimen>
<dimen name="all_apps_divider_height">2dp</dimen>
<dimen name="all_apps_divider_width">128dp</dimen>
<dimen name="all_apps_content_fade_in_offset">150dp</dimen>
@@ -128,9 +129,9 @@
<dimen name="all_apps_height_extra">6dp</dimen>
<dimen name="all_apps_bottom_sheet_horizontal_padding">0dp</dimen>
<dimen name="all_apps_paged_view_top_padding">40dp</dimen>
- <dimen name="all_apps_personal_work_tabs_vertical_margin">16dp</dimen>
<dimen name="all_apps_icon_drawable_padding">8dp</dimen>
+ <dimen name="all_apps_predicted_icon_vertical_padding">8dp</dimen>
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
<dimen name="arrow_toast_elevation">2dp</dimen>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index d34f535..83ff084 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -25,12 +25,16 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Bundle;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.IntDef;
import com.android.launcher3.DeviceProfile.DeviceProfileListenable;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.StatsLogManager;
+import com.android.launcher3.testing.TestLogging;
+import com.android.launcher3.testing.shared.TestProtocol;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.ViewCache;
import com.android.launcher3.views.AppLauncher;
@@ -172,6 +176,19 @@
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (Utilities.ATLEAST_T) {
+ getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ () -> {
+ onBackPressed();
+ TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
+ });
+ }
+ }
+
+ @Override
protected void onStart() {
addActivityFlags(ACTIVITY_STATE_STARTED);
super.onStart();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 17fb7eb..5e2187e 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -114,7 +114,6 @@
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import android.widget.Toast;
-import android.window.OnBackInvokedDispatcher;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
@@ -544,8 +543,6 @@
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
}
setTitle(R.string.home_screen);
-
- registerOnBackInvokedCallback();
}
protected LauncherOverlayManager getDefaultOverlay() {
@@ -2065,17 +2062,6 @@
mStateManager.getState().onBackPressed(this);
}
- private void registerOnBackInvokedCallback() {
- if (Utilities.ATLEAST_T) {
- getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT,
- () -> {
- onBackPressed();
- TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onBackInvoked");
- });
- }
- }
-
protected void onScreenOff() {
// Reset AllApps to its initial state only if we are not in the middle of
// processing a multi-step drop
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderView.java b/src/com/android/launcher3/allapps/FloatingHeaderView.java
index 73efd3f..1ef5498 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderView.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderView.java
@@ -272,7 +272,8 @@
}
mMaxTranslation += mFloatingRowsHeight;
if (!mTabsHidden) {
- mMaxTranslation += mTabsAdditionalPaddingBottom;
+ mMaxTranslation += mTabsAdditionalPaddingBottom
+ + getResources().getDimensionPixelSize(R.dimen.all_apps_tabs_margin_top);
}
}
diff --git a/src/com/android/launcher3/allapps/SearchTransitionController.java b/src/com/android/launcher3/allapps/SearchTransitionController.java
index 4be715b..da8902d 100644
--- a/src/com/android/launcher3/allapps/SearchTransitionController.java
+++ b/src/com/android/launcher3/allapps/SearchTransitionController.java
@@ -35,6 +35,7 @@
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
/** Coordinates the transition between Search and A-Z in All Apps. */
public class SearchTransitionController {
@@ -144,8 +145,11 @@
headerView.setAlpha(clampToProgress(searchToAzProgress, 0.8f, 1f));
// Account for the additional padding added for the tabs.
- appsTranslationY -=
- headerView.getPaddingTop() - headerView.getTabsAdditionalPaddingBottom();
+ appsTranslationY +=
+ headerView.getTabsAdditionalPaddingBottom()
+ + mAllAppsContainerView.getResources().getDimensionPixelOffset(
+ R.dimen.all_apps_tabs_margin_top)
+ - headerView.getPaddingTop();
}
View appsContainer = mAllAppsContainerView.getAppsRecyclerViewContainer();
diff --git a/src/com/android/launcher3/views/AllAppsButton.java b/src/com/android/launcher3/views/AllAppsButton.java
index b1e69c7..ab8e5db 100644
--- a/src/com/android/launcher3/views/AllAppsButton.java
+++ b/src/com/android/launcher3/views/AllAppsButton.java
@@ -45,5 +45,6 @@
Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
.createScaledBitmapWithShadow(theme.getDrawable(R.drawable.ic_all_apps_button));
setIcon(new FastBitmapDrawable(bitmap));
+ setContentDescription(context.getString(R.string.all_apps_button_label));
}
}
diff --git a/src/com/android/launcher3/views/FloatingIconView.java b/src/com/android/launcher3/views/FloatingIconView.java
index efc83eb..55af622 100644
--- a/src/com/android/launcher3/views/FloatingIconView.java
+++ b/src/com/android/launcher3/views/FloatingIconView.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
+import static android.view.Gravity.LEFT;
+
import static com.android.launcher3.Utilities.getBadge;
import static com.android.launcher3.Utilities.getFullDrawable;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -181,8 +183,10 @@
updatePosition(positionOut, lp);
setLayoutParams(lp);
- mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
- mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height));
+ // For code simplicity, we always layout the child views using Gravity.LEFT
+ // and manually handle RTL for FloatingIconView when positioning it on the screen.
+ mClipIconView.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height, LEFT));
+ mBtvDrawable.setLayoutParams(new FrameLayout.LayoutParams(lp.width, lp.height, LEFT));
}
private void updatePosition(RectF pos, InsettableFrameLayout.LayoutParams lp) {
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index e0df3f0..3986df6 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -239,7 +239,7 @@
// Launcher package. As during inproc tests the tested launcher may not be selected as the
// current launcher, choosing target package for inproc. For out-of-proc, use the installed
// launcher package.
- mLauncherPackage = testPackage.equals(targetPackage)
+ mLauncherPackage = testPackage.equals(targetPackage) || isGradleInstrumentation()
? getLauncherPackageName()
: targetPackage;
@@ -286,6 +286,20 @@
}
}
+ /**
+ * Gradle only supports out of process instrumentation. The test package is automatically
+ * generated by appending `.test` to the target package.
+ */
+ private boolean isGradleInstrumentation() {
+ final String testPackage = getContext().getPackageName();
+ final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
+ final String testSuffix = ".test";
+
+ return testPackage.endsWith(testSuffix) && testPackage.length() > testSuffix.length()
+ && testPackage.substring(0, testPackage.length() - testSuffix.length())
+ .equals(targetPackage);
+ }
+
public void enableCheckEventsForSuccessfulGestures() {
mCheckEventsForSuccessfulGestures = true;
}