Merge "Fix bug where long press menu text is white on gray for dark backgrounds." into ub-launcher3-master
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index ef50ac4..9069698 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/quickstep/MotionEventQueue.java b/quickstep/src/com/android/quickstep/MotionEventQueue.java
new file mode 100644
index 0000000..e3c3a1b
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/MotionEventQueue.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.quickstep;
+
+import static android.view.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_MOVE;
+
+import android.view.Choreographer;
+import android.view.MotionEvent;
+
+import com.android.systemui.shared.system.ChoreographerCompat;
+
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Helper class for batching input events
+ */
+public class MotionEventQueue implements Runnable {
+
+ // We use two arrays and swap the current index when one array is being consumed
+ private final EventArray[] mArrays = new EventArray[] {new EventArray(), new EventArray()};
+ private int mCurrentIndex = 0;
+
+ private final Choreographer mChoreographer;
+ private final Consumer<MotionEvent> mConsumer;
+
+ public MotionEventQueue(Choreographer choreographer, Consumer<MotionEvent> consumer) {
+ mChoreographer = choreographer;
+ mConsumer = consumer;
+ }
+
+ public void queue(MotionEvent event) {
+ synchronized (mArrays) {
+ EventArray array = mArrays[mCurrentIndex];
+ if (array.isEmpty()) {
+ ChoreographerCompat.postInputFrame(mChoreographer, this);
+ }
+
+ int eventAction = event.getAction();
+ if (eventAction == ACTION_MOVE && array.lastEventAction == ACTION_MOVE) {
+ // Replace and recycle the last event
+ array.set(array.size() - 1, event).recycle();
+ } else {
+ array.add(event);
+ array.lastEventAction = eventAction;
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ EventArray array = swapAndGetCurrentArray();
+ int size = array.size();
+ for (int i = 0; i < size; i++) {
+ MotionEvent event = array.get(i);
+ mConsumer.accept(event);
+ event.recycle();
+ }
+ array.clear();
+ array.lastEventAction = ACTION_CANCEL;
+ }
+
+ private EventArray swapAndGetCurrentArray() {
+ synchronized (mArrays) {
+ EventArray current = mArrays[mCurrentIndex];
+ mCurrentIndex = mCurrentIndex ^ 1;
+ return current;
+ }
+ }
+
+ private static class EventArray extends ArrayList<MotionEvent> {
+
+ public int lastEventAction = ACTION_CANCEL;
+
+ public EventArray() {
+ super(4);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index dc7d648..af82fe9 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -28,11 +28,7 @@
import android.os.Build;
import android.os.Handler;
import android.os.UserHandle;
-import android.support.annotation.BinderThread;
import android.support.annotation.UiThread;
-import android.util.DisplayMetrics;
-import android.view.Choreographer;
-import android.view.Choreographer.FrameCallback;
import android.view.View;
import android.view.ViewTreeObserver.OnPreDrawListener;
@@ -46,16 +42,15 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.states.InternalStateHandler;
+import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.WindowManagerWrapper;
-import java.util.concurrent.atomic.AtomicBoolean;
-
@TargetApi(Build.VERSION_CODES.O)
-public class NavBarSwipeInteractionHandler extends InternalStateHandler implements FrameCallback {
+public class NavBarSwipeInteractionHandler extends InternalStateHandler {
private static final int STATE_LAUNCHER_READY = 1 << 0;
private static final int STATE_RECENTS_DELAY_COMPLETE = 1 << 1;
@@ -90,9 +85,6 @@
// animated to 1, so allow for a smooth transition.
private final AnimatedFloat mActivityMultiplier = new AnimatedFloat(this::updateFinalShift);
- private final Choreographer mChoreographer;
- private final AtomicBoolean mFrameScheduled = new AtomicBoolean(false);
-
private final int mRunningTaskId;
private final Context mContext;
@@ -106,18 +98,12 @@
private boolean mLauncherReady;
private boolean mTouchEndHandled;
+ private float mCurrentDisplacement;
private Bitmap mTaskSnapshot;
- // These are updated on the binder thread, and eventually picked up on doFrame
- private volatile float mCurrentDisplacement;
- private volatile float mEndVelocity;
- private volatile boolean mTouchEnded = false;
-
- NavBarSwipeInteractionHandler(
- RunningTaskInfo runningTaskInfo, Choreographer choreographer, Context context) {
+ NavBarSwipeInteractionHandler(RunningTaskInfo runningTaskInfo, Context context) {
mRunningTaskId = runningTaskInfo.id;
- mChoreographer = choreographer;
mContext = context;
WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
@@ -168,11 +154,13 @@
@Override
public void onLauncherResume() {
+ TraceHelper.partitionSection("TouchInt", "Launcher On resume");
mDragView.getViewTreeObserver().addOnPreDrawListener(new OnPreDrawListener() {
@Override
public boolean onPreDraw() {
mDragView.getViewTreeObserver().removeOnPreDrawListener(this);
mStateCallback.setState(STATE_LAUNCHER_READY);
+ TraceHelper.partitionSection("TouchInt", "Launcher drawn");
return true;
}
});
@@ -194,39 +182,12 @@
// Optimization
mLauncher.getAppsView().setVisibility(View.GONE);
mRecentsView.setVisibility(View.GONE);
+ TraceHelper.partitionSection("TouchInt", "Launcher on new intent");
}
- /**
- * This is updated on the binder thread and is picked up on the UI thread during the next
- * scheduled frame.
- * TODO: Instead of continuously scheduling frames, post the motion events to UI thread
- * (can ignore all continuous move events until the last move).
- */
- @BinderThread
+ @UiThread
public void updateDisplacement(float displacement) {
mCurrentDisplacement = displacement;
- scheduleFrameIfNeeded();
- }
-
- @BinderThread
- public void endTouch(float endVelocity) {
- mEndVelocity = endVelocity;
- mTouchEnded = true;
- scheduleFrameIfNeeded();
- }
-
- private void scheduleFrameIfNeeded() {
- boolean alreadyScheduled = mFrameScheduled.getAndSet(true);
- if (!alreadyScheduled) {
- // TODO: Here we might end up scheduling one additional frame in some race conditions.
- // This can be avoided by synchronising postFrameCallback as well
- mChoreographer.postFrameCallback(this);
- }
- }
-
- @Override
- public void doFrame(long l) {
- mFrameScheduled.set(false);
executeFrameUpdate();
}
@@ -238,14 +199,6 @@
float shift = hotseatHeight == 0 ? 0 : translation / hotseatHeight;
mCurrentShift.updateValue(shift);
}
-
- if (mTouchEnded) {
- if (mTouchEndHandled) {
- return;
- }
- mTouchEndHandled = true;
- animateToFinalShift();
- }
}
@UiThread
@@ -301,25 +254,30 @@
}
@UiThread
- private void animateToFinalShift() {
+ public void endTouch(float endVelocity) {
+ if (mTouchEndHandled) {
+ return;
+ }
+ mTouchEndHandled = true;
+
Resources res = mContext.getResources();
float flingThreshold = res.getDimension(R.dimen.quickstep_fling_threshold_velocity);
- boolean isFling = Math.abs(mEndVelocity) > flingThreshold;
+ boolean isFling = Math.abs(endVelocity) > flingThreshold;
long duration = DEFAULT_SWIPE_DURATION;
final float endShift;
if (!isFling) {
endShift = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW ? 1 : 0;
} else {
- endShift = mEndVelocity < 0 ? 1 : 0;
+ endShift = endVelocity < 0 ? 1 : 0;
float minFlingVelocity = res.getDimension(R.dimen.quickstep_fling_min_velocity);
- if (Math.abs(mEndVelocity) > minFlingVelocity && mLauncherReady) {
+ if (Math.abs(endVelocity) > minFlingVelocity && mLauncherReady) {
float distanceToTravel = (endShift - mCurrentShift.value) * mHotseat.getHeight();
// we want the page's snap velocity to approximately match the velocity at
// which the user flings, so we scale the duration by a value near to the
// derivative of the scroll interpolator at zero, ie. 5.
- duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / mEndVelocity));
+ duration = 5 * Math.round(1000 * Math.abs(distanceToTravel / endVelocity));
}
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index a0340b6..f92d773 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -22,6 +22,7 @@
import android.widget.ArrayAdapter;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.recents.model.Task;
@@ -37,7 +38,8 @@
super.onCreate(savedInstanceState);
RecentsTaskLoadPlan plan = new RecentsTaskLoadPlan(this);
- plan.preloadPlan(new RecentsTaskLoader(this, 1, 1, 0), -1, UserHandle.myUserId());
+ plan.preloadPlan(new PreloadOptions(), new RecentsTaskLoader(this, 1, 1, 0), -1,
+ UserHandle.myUserId());
mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1);
mAdapter.addAll(plan.getTaskStack().getTasks());
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 675f456..ba88f99 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -16,10 +16,12 @@
package com.android.quickstep;
+import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -41,6 +43,12 @@
*/
public class RecentsView extends PagedView {
+ /** Designates how "curvy" the carousel is from 0 to 1, where 0 is a straight line. */
+ private static final float CURVE_FACTOR = 0.25f;
+ /** A circular curve of x from 0 to 1, where 0 is the center of the screen and 1 is the edge. */
+ private static final TimeInterpolator CURVE_INTERPOLATOR
+ = x -> (float) (1 - Math.sqrt(1 - Math.pow(x, 2)));
+
private boolean mOverviewStateEnabled;
private boolean mTaskStackListenerRegistered;
@@ -69,6 +77,7 @@
super(context, attrs, defStyleAttr);
setWillNotDraw(false);
setPageSpacing((int) getResources().getDimension(R.dimen.recents_page_spacing));
+ enableFreeScroll(true);
}
@Override
@@ -170,4 +179,39 @@
padding.left = padding.right = (int) ((profile.availableWidthPx - overviewWidth) / 2);
return padding;
}
+
+ @Override
+ public void scrollTo(int x, int y) {
+ super.scrollTo(x, y);
+ updateCurveProperties();
+ }
+
+ /**
+ * Scales and adjusts translation of adjacent pages as if on a curved carousel.
+ */
+ private void updateCurveProperties() {
+ if (getPageCount() == 0 || getPageAt(0).getMeasuredWidth() == 0) {
+ return;
+ }
+ final int halfScreenWidth = getMeasuredWidth() / 2;
+ final int screenCenter = halfScreenWidth + getScrollX();
+ final int pageSpacing = getResources().getDimensionPixelSize(R.dimen.recents_page_spacing);
+ final int pageCount = getPageCount();
+ for (int i = 0; i < pageCount; i++) {
+ View page = getPageAt(i);
+ int pageWidth = page.getMeasuredWidth();
+ int halfPageWidth = pageWidth / 2;
+ int pageCenter = page.getLeft() + halfPageWidth;
+ float distanceFromScreenCenter = Math.abs(pageCenter - screenCenter);
+ float distanceToReachEdge = halfScreenWidth + halfPageWidth + pageSpacing;
+ float linearInterpolation = Math.min(1, distanceFromScreenCenter / distanceToReachEdge);
+ float curveInterpolation = CURVE_INTERPOLATOR.getInterpolation(linearInterpolation);
+ float scale = 1 - curveInterpolation * CURVE_FACTOR;
+ page.setScaleX(scale);
+ page.setScaleY(scale);
+ // Make sure the biggest card (i.e. the one in front) shows on top of the adjacent ones.
+ page.setTranslationZ(scale);
+ page.setTranslationX((screenCenter - pageCenter) * curveInterpolation * CURVE_FACTOR);
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 50f5528..55fd448 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -42,9 +42,11 @@
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.R;
+import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
@@ -62,7 +64,7 @@
@Override
public void onMotionEvent(MotionEvent ev) {
- handleMotionEvent(ev);
+ mEventQueue.queue(ev);
}
@Override
@@ -75,7 +77,7 @@
private RunningTaskInfo mRunningTask;
private Intent mHomeIntent;
private ComponentName mLauncher;
- private Choreographer mChoreographer;
+ private MotionEventQueue mEventQueue;
private MainThreadExecutor mMainThreadExecutor;
private int mDisplayRotation;
@@ -110,8 +112,8 @@
sRecentsTaskLoader.startLoader(this);
}
- mChoreographer = Choreographer.getInstance();
mMainThreadExecutor = new MainThreadExecutor();
+ mEventQueue = new MotionEventQueue(Choreographer.getInstance(), this::handleMotionEvent);
}
@Override
@@ -130,6 +132,7 @@
}
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
+ TraceHelper.beginSection("TouchInt");
mActivePointerId = ev.getPointerId(0);
mDownPos.set(ev.getX(), ev.getY());
mLastPos.set(mDownPos);
@@ -192,6 +195,7 @@
case MotionEvent.ACTION_CANCEL:
// TODO: Should be different than ACTION_UP
case MotionEvent.ACTION_UP: {
+ TraceHelper.endSection("TouchInt");
endInteraction();
break;
@@ -202,12 +206,13 @@
private void startTouchTracking() {
// Create the shared handler
final NavBarSwipeInteractionHandler handler =
- new NavBarSwipeInteractionHandler(mRunningTask, mChoreographer, this);
+ new NavBarSwipeInteractionHandler(mRunningTask, this);
// Preload and start the recents activity on a background thread
final Context context = this;
final RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(context);
final int taskId = mRunningTask.id;
+ TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
BackgroundExecutor.get().submit(() -> {
// Get the snap shot before
@@ -216,6 +221,8 @@
// Start the launcher activity with our custom handler
Intent homeIntent = handler.addToIntent(new Intent(mHomeIntent));
startActivity(homeIntent, ActivityOptions.makeCustomAnimation(this, 0, 0).toBundle());
+ TraceHelper.partitionSection("TouchInt", "Home started");
+
/*
ActivityManagerWrapper.getInstance().startRecentsActivity(null, options,
ActivityOptions.makeCustomAnimation(this, 0, 0), UserHandle.myUserId(),
@@ -224,7 +231,9 @@
// Preload the plan
RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
- loadPlan.preloadPlan(loader, taskId, UserHandle.myUserId());
+ PreloadOptions opts = new PreloadOptions();
+ opts.loadTitles = false;
+ loadPlan.preloadPlan(opts, loader, taskId, UserHandle.myUserId());
// Set the load plan on UI thread
mMainThreadExecutor.execute(() -> handler.setRecentsTaskLoadPlan(loadPlan));
});
@@ -249,13 +258,16 @@
return null;
}
+ TraceHelper.beginSection("TaskSnapshot");
// TODO: We are using some hardcoded layers for now, to best approximate the activity layers
try {
return mISystemUiProxy.screenshot(new Rect(), mDisplaySize.x, mDisplaySize.y, 0, 100000,
- false, mDisplayRotation);
+ false, mDisplayRotation).toBitmap();
} catch (RemoteException e) {
Log.e(TAG, "Error capturing snapshot", e);
return null;
+ } finally {
+ TraceHelper.endSection("TaskSnapshot");
}
}
}
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 832aaef..c42c15c 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -35,21 +35,27 @@
android:id="@+id/all_apps_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clickable="true"
- android:paddingTop="30dp"
+ android:paddingTop="@dimen/all_apps_header_top_padding"
+ android:clipToPadding="false"
android:layout_below="@id/search_container_all_apps" >
<com.android.launcher3.allapps.PredictionRowView
android:id="@+id/header_content"
android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
+ android:layout_height="wrap_content" />
+
+ <include layout="@layout/all_apps_divider"
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/tabs" />
<com.android.launcher3.views.SlidingTabStrip
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="@dimen/all_apps_header_tab_height"
android:layout_below="@id/header_content"
- android:orientation="horizontal">
+ android:orientation="horizontal" >
<Button
android:id="@+id/tab_personal"
android:layout_width="0dp"
@@ -67,7 +73,6 @@
android:textColor="@color/all_apps_tab_text"
android:background="?android:attr/selectableItemBackground"/>
</com.android.launcher3.views.SlidingTabStrip>
-
</RelativeLayout>
<!-- Note: we are reusing/repurposing a system attribute for search layout, because of a
diff --git a/res/layout/all_apps_tabs.xml b/res/layout/all_apps_tabs.xml
index fa1d591..54a9b88 100644
--- a/res/layout/all_apps_tabs.xml
+++ b/res/layout/all_apps_tabs.xml
@@ -25,7 +25,7 @@
android:clipChildren="false"
android:clipToPadding="false"
android:descendantFocusability="afterDescendants"
- android:paddingTop="30dp">
+ android:paddingTop="@dimen/all_apps_header_top_padding">
<include layout="@layout/all_apps_rv_layout" />
diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml
index 9734781..40a566e 100644
--- a/res/values-gu/strings.xml
+++ b/res/values-gu/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> માટે <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> શૉર્ટકટ અને <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> સૂચનાઓ"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"છોડી દો"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"સૂચના છોડી દીધી"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"મનગમતી ઍપ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"કાર્યાલયની ઍપ"</string>
</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 2387404..cfd1904 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> के लिए <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट और <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचनाएं हैं"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"खारिज करें"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना को खारिज किया गया"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"निजी ऐप"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"काम से जुड़े ऐप"</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index d7905b4..5ed22f8 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> קיצורי דרך ו-<xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> הודעות של <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"סגור"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ההודעה נסגרה"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"אישיות"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"עבודה"</string>
</resources>
diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml
index 4156a21..e19b42e 100644
--- a/res/values-kn/strings.xml
+++ b/res/values-kn/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ಗಾಗಿ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ಶಾರ್ಟ್ಕಟ್ಗಳು ಮತ್ತು <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ಅಧಿಸೂಚನೆಗಳು"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ವಜಾಗೊಳಿಸಿ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ಅಧಿಸೂಚನೆಯನ್ನು ವಜಾಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ವೈಯಕ್ತಿಕ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ಕೆಲಸ"</string>
</resources>
diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml
index 299fc45..102176c 100644
--- a/res/values-ml/strings.xml
+++ b/res/values-ml/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ആപ്പിനായുള്ള <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> കുറുക്കുവഴികളും <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> അറിയിപ്പുകളും"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"നിരസിക്കുക"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"അറിയിപ്പ് നിരസിച്ചു"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"വ്യക്തിപരം"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ജോലി"</string>
</resources>
diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml
index ca9a402..7b98bce 100644
--- a/res/values-mr/strings.xml
+++ b/res/values-mr/strings.xml
@@ -98,7 +98,7 @@
<string name="abandoned_promises_title" msgid="7096178467971716750">"हा अॅप इंस्टॉल केलेला नाही"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप इंस्टॉल केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे इंस्टॉल करू शकता."</string>
<string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> डाउनलोड होत आहे , <xliff:g id="PROGRESS">%2$s</xliff:g> पूर्ण झाले"</string>
- <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करीत आहे"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> इंस्टॉल करण्याची प्रतिक्षा करत आहे"</string>
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> विजेट"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"होम स्क्रीनवर जोडा"</string>
<string name="action_move_here" msgid="2170188780612570250">"आयटम येथे हलवा"</string>
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g>साठी <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> शॉर्टकट आणि <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचना"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"डिसमिस करा"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना डिसमिस केली"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"वैयक्तिक"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्यालय"</string>
</resources>
diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml
index 41e43a7..930fb00 100644
--- a/res/values-ne/strings.xml
+++ b/res/values-ne/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> का <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> सर्टकट र <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> सूचनाहरू"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"खारेज गर्नुहोस्"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"सूचना खारेज गरियो"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"व्यक्तिगत"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"कार्यसम्बन्धी"</string>
</resources>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 525f2f6..4156009 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> ਲਈ <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> ਸ਼ਾਰਟਕੱਟ ਅਤੇ <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> ਸੂਚਨਾਵਾਂ"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"ਖਾਰਜ ਕਰੋ"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"ਸੂਚਨਾ ਖਾਰਜ ਕੀਤੀ ਗਈ"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ਨਿੱਜੀ"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"ਕਾਰਜ-ਸਥਾਨ"</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 402f3a6..5209438 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -66,8 +66,8 @@
<skip />
<string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
<string name="folder_opened" msgid="94695026776264709">"Folda imefunguliwa, <xliff:g id="WIDTH">%1$d</xliff:g> kwa <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
- <string name="folder_tap_to_close" msgid="4625795376335528256">"Gonga ili ufunge folda"</string>
- <string name="folder_tap_to_rename" msgid="4017685068016979677">"Gonga ili ubadilishe jina"</string>
+ <string name="folder_tap_to_close" msgid="4625795376335528256">"Gusa ili ufunge folda"</string>
+ <string name="folder_tap_to_rename" msgid="4017685068016979677">"Gusa ili ubadilishe jina"</string>
<string name="folder_closed" msgid="4100806530910930934">"Folda imefungwa"</string>
<string name="folder_renamed" msgid="1794088362165669656">"Folda imebadilishwa jina kuwa <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format" msgid="6629239338071103179">"Folda: <xliff:g id="NAME">%1$s</xliff:g>"</string>
@@ -131,4 +131,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"Njia <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> za mkato na arifa <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> za <xliff:g id="APP_NAME">%3$s</xliff:g>"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"Ondoa"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"Arifa imeondolewa"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"Binafsi"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"Kazini"</string>
</resources>
diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml
index 5d72030..f8101bb 100644
--- a/res/values-ta/strings.xml
+++ b/res/values-ta/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> பயன்பாட்டிற்கான <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> குறுக்குவழிகளும் <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> அறிவிப்புகளும் உள்ளன"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"நிராகரி"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"அறிவிப்பு நிராகரிக்கப்பட்டது"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"தனிப்பட்டவை"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"பணி"</string>
</resources>
diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml
index 99b8c80..860b132 100644
--- a/res/values-ur/strings.xml
+++ b/res/values-ur/strings.xml
@@ -129,4 +129,6 @@
<string name="shortcuts_menu_with_notifications_description" msgid="8985659504915468840">"<xliff:g id="APP_NAME">%3$s</xliff:g> کے <xliff:g id="NUMBER_OF_SHORTCUTS">%1$d</xliff:g> شارٹ کٹس اور <xliff:g id="NUMBER_OF_NOTIFICATIONS">%2$d</xliff:g> اطلاعات"</string>
<string name="action_dismiss_notification" msgid="5909461085055959187">"برخاست کریں"</string>
<string name="notification_dismissed" msgid="6002233469409822874">"اطلاع مسترد ہو گئی"</string>
+ <string name="all_apps_personal_tab" msgid="4190252696685155002">"ذاتی"</string>
+ <string name="all_apps_work_tab" msgid="4884822796154055118">"دفتری"</string>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index efe5043..266e0b0 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -92,6 +92,8 @@
<dimen name="all_apps_caret_workspace_offset">18dp</dimen>
<dimen name="all_apps_header_tab_height">50dp</dimen>
<dimen name="all_apps_tabs_indicator_height">2dp</dimen>
+ <dimen name="all_apps_header_top_padding">36dp</dimen>
+ <dimen name="all_apps_prediction_row_divider_height">17dp</dimen>
<!-- Search bar in All Apps -->
<dimen name="all_apps_header_max_elevation">3dp</dimen>
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 6c22474..9f6efb3 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -30,7 +30,6 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -87,6 +86,7 @@
public static final int INVALID_RESTORE_PAGE = -1001;
private boolean mFreeScroll = false;
+ private boolean mSettleOnPageInFreeScroll = false;
protected int mFlingThresholdVelocity;
protected int mMinFlingVelocity;
@@ -1170,7 +1170,12 @@
* return true if freescroll has been enabled, false otherwise
*/
protected void enableFreeScroll() {
+ enableFreeScroll(false);
+ }
+
+ protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
setEnableFreeScroll(true);
+ mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
}
protected void disableFreeScroll() {
@@ -1414,7 +1419,22 @@
mScroller.setInterpolator(mDefaultInterpolator);
mScroller.fling(initialScrollX,
getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
- mNextPage = getPageNearestToCenterOfScreen((int) (mScroller.getFinalX() / scaleX));
+ 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 > firstPageScroll
+ && unscaledScrollX < lastPageScroll) {
+ // Make sure we land directly on a page. If flinging past one of the ends,
+ // don't change the velocity as it will get stopped at the end anyway.
+ mScroller.setFinalX((int) (getScrollForPage(mNextPage) * 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();
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index efd7b97..2bb95cb 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -281,6 +281,9 @@
mAH[i].recyclerView.scrollToTop();
}
}
+ if (mFloatingHeaderHandler != null) {
+ mFloatingHeaderHandler.reset();
+ }
// Reset the search bar and base recycler view after transitioning home
mSearchUiManager.reset();
}
@@ -301,6 +304,7 @@
});
mHeader = findViewById(R.id.all_apps_header);
+ mFloatingHeaderHandler = new FloatingHeaderHandler(mHeader);
rebindAdapters(mUsingTabs);
mSearchContainer = findViewById(R.id.search_container_all_apps);
@@ -431,10 +435,14 @@
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
setupWorkProfileTabs();
setupHeader();
- mHeader.setVisibility(View.VISIBLE);
} else {
- mHeader.setVisibility(View.GONE);
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
+ if (FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
+ setupHeader();
+ } else {
+ mFloatingHeaderHandler = null;
+ mHeader.setVisibility(View.GONE);
+ }
}
applyTouchDelegate();
@@ -512,7 +520,7 @@
}
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
- if (mUsingTabs) {
+ if (mFloatingHeaderHandler != null) {
mFloatingHeaderHandler.getContentView().setPredictedApps(apps);
}
mAH[AdapterHolder.MAIN].appsList.setPredictedApps(apps);
@@ -535,15 +543,24 @@
}
private void setupHeader() {
+ mHeader.setVisibility(View.VISIBLE);
int contentHeight = mLauncher.getDeviceProfile().allAppsCellHeightPx;
+ if (!mUsingTabs) {
+ contentHeight += getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_prediction_row_divider_height);
+ }
RecyclerView mainRV = mAH[AdapterHolder.MAIN].recyclerView;
- RecyclerView workRV = mAH[AdapterHolder.WORK] != null
- ? mAH[AdapterHolder.WORK].recyclerView : null;
- mFloatingHeaderHandler = new FloatingHeaderHandler(mHeader, mainRV, workRV, contentHeight);
- mFloatingHeaderHandler.getContentView().setNumAppsPerRow(mNumPredictedAppsPerRow);
- mFloatingHeaderHandler.getContentView().setComponentToAppMap(mComponentToAppMap);
+ RecyclerView workRV = mAH[AdapterHolder.WORK].recyclerView;
+ mFloatingHeaderHandler.setup(mainRV, workRV, contentHeight);
+ mFloatingHeaderHandler.getContentView().setup(mAH[AdapterHolder.MAIN].adapter,
+ mComponentToAppMap, mNumPredictedAppsPerRow);
+
+ int padding = contentHeight;
+ if (!mUsingTabs) {
+ padding += mHeader.getPaddingTop() + mHeader.getPaddingBottom();
+ }
for (int i = 0; i < mAH.length; i++) {
- mAH[i].paddingTopForTabs = contentHeight;
+ mAH[i].paddingTopForTabs = padding;
mAH[i].applyPadding();
}
}
@@ -556,7 +573,9 @@
public void onSearchResultsChanged() {
for (int i = 0; i < mAH.length; i++) {
- mAH[i].recyclerView.onSearchResultsChanged();
+ if (mAH[i].recyclerView != null) {
+ mAH[i].recyclerView.onSearchResultsChanged();
+ }
}
}
@@ -640,9 +659,14 @@
void applyPadding() {
if (recyclerView != null) {
- int paddingTop = mUsingTabs ? paddingTopForTabs : padding.top;
+ int paddingTop = mUsingTabs || FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW
+ ? paddingTopForTabs : padding.top;
recyclerView.setPadding(padding.left, paddingTop, padding.right, padding.bottom);
}
+ if (mFloatingHeaderHandler != null) {
+ mFloatingHeaderHandler.getContentView()
+ .setPadding(padding.left, 0 , padding.right, 0);
+ }
}
void applyNumsPerRow() {
@@ -652,7 +676,7 @@
}
adapter.setNumAppsPerRow(mNumAppsPerRow);
appsList.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow);
- if (mUsingTabs && mFloatingHeaderHandler != null) {
+ if (mFloatingHeaderHandler != null) {
mFloatingHeaderHandler.getContentView()
.setNumAppsPerRow(mNumPredictedAppsPerRow);
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index f9dde2f..7cf2d95 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -460,7 +460,7 @@
mFastScrollerSections.clear();
mAdapterItems.clear();
- if (!FeatureFlags.ALL_APPS_TABS_ENABLED) {
+ if (!FeatureFlags.ALL_APPS_PREDICTION_ROW_VIEW) {
if (DEBUG_PREDICTIONS) {
if (mPredictedAppComponents.isEmpty() && !mApps.isEmpty()) {
mPredictedAppComponents.add(new ComponentKeyMapper<AppInfo>(new ComponentKey(mApps.get(0).componentName,
diff --git a/src/com/android/launcher3/allapps/FloatingHeaderHandler.java b/src/com/android/launcher3/allapps/FloatingHeaderHandler.java
index 984966b..0b39b7d 100644
--- a/src/com/android/launcher3/allapps/FloatingHeaderHandler.java
+++ b/src/com/android/launcher3/allapps/FloatingHeaderHandler.java
@@ -15,36 +15,52 @@
*/
package com.android.launcher3.allapps;
+import android.animation.ValueAnimator;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
import com.android.launcher3.R;
-public class FloatingHeaderHandler extends RecyclerView.OnScrollListener {
+public class FloatingHeaderHandler extends RecyclerView.OnScrollListener
+ implements ValueAnimator.AnimatorUpdateListener {
- private final int mMaxTranslation;
private final View mHeaderView;
- private final PredictionRowView mContentView;
- private final RecyclerView mMainRV;
- private final RecyclerView mWorkRV;
+ private final PredictionRowView mPredictionRow;
+ private final ViewGroup mTabLayout;
+ private final View mDivider;
private final Rect mClip = new Rect(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ private final ValueAnimator mAnimator = ValueAnimator.ofInt(0, 0);
+ private RecyclerView mMainRV;
+ private RecyclerView mWorkRV;
+ private boolean mTopOnlyMode;
private boolean mHeaderHidden;
+ private int mMaxTranslation;
private int mSnappedScrolledY;
private int mTranslationY;
private int mMainScrolledY;
private int mWorkScrolledY;
private boolean mMainRVActive;
- public FloatingHeaderHandler(@NonNull View header, @NonNull RecyclerView personalRV,
- @Nullable RecyclerView workRV, int contentHeight) {
+ public FloatingHeaderHandler(@NonNull ViewGroup header) {
mHeaderView = header;
- mContentView = mHeaderView.findViewById(R.id.header_content);
- mContentView.getLayoutParams().height = contentHeight;
- mMaxTranslation = contentHeight;
+ mTabLayout = header.findViewById(R.id.tabs);
+ mDivider = header.findViewById(R.id.divider);
+ mPredictionRow = header.findViewById(R.id.header_content);
+ }
+
+ public void setup(@NonNull RecyclerView personalRV, @Nullable RecyclerView workRV,
+ int predictionRowHeight) {
+ mTopOnlyMode = workRV == null;
+ mTabLayout.setVisibility(mTopOnlyMode ? View.GONE : View.VISIBLE);
+ mPredictionRow.getLayoutParams().height = predictionRowHeight;
+ mMaxTranslation = predictionRowHeight;
mMainRV = personalRV;
mMainRV.addOnScrollListener(this);
mWorkRV = workRV;
@@ -52,6 +68,18 @@
workRV.addOnScrollListener(this);
}
setMainActive(true);
+ setupDivider();
+ }
+
+ private void setupDivider() {
+ Resources res = mHeaderView.getResources();
+ int verticalGap = res.getDimensionPixelSize(R.dimen.all_apps_divider_margin_vertical);
+ int sideGap = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin);
+ mDivider.setPadding(sideGap, verticalGap,sideGap, mTopOnlyMode ? verticalGap : 0);
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) mDivider.getLayoutParams();
+ lp.removeRule(RelativeLayout.ALIGN_BOTTOM);
+ lp.addRule(RelativeLayout.ALIGN_BOTTOM, mTopOnlyMode ? R.id.header_content : R.id.tabs);
+ mDivider.setLayoutParams(lp);
}
public void setMainActive(boolean active) {
@@ -65,7 +93,15 @@
}
public PredictionRowView getContentView() {
- return mContentView;
+ return mPredictionRow;
+ }
+
+ public ViewGroup getTabLayout() {
+ return mTabLayout;
+ }
+
+ public View getDivider() {
+ return mDivider;
}
@Override
@@ -75,27 +111,39 @@
return;
}
+ if (mAnimator.isStarted()) {
+ mAnimator.cancel();
+ }
+
int current = isMainRV
? (mMainScrolledY -= dy)
: (mWorkScrolledY -= dy);
- if (dy == 0) {
- setExpanded(true);
- } else {
- moved(current);
- apply();
- }
+ moved(current);
+ apply();
+ }
+
+ public void reset() {
+ mMainScrolledY = 0;
+ mWorkScrolledY = 0;
+ setExpanded(true);
+ }
+
+ private boolean canSnapAt(int currentScrollY) {
+ return !mTopOnlyMode || Math.abs(currentScrollY) <= mPredictionRow.getHeight();
}
private void moved(final int currentScrollY) {
if (mHeaderHidden) {
if (currentScrollY <= mSnappedScrolledY) {
- mSnappedScrolledY = currentScrollY;
+ if (canSnapAt(currentScrollY)) {
+ mSnappedScrolledY = currentScrollY;
+ }
} else {
mHeaderHidden = false;
}
mTranslationY = currentScrollY;
- } else {
+ } else if (!mHeaderHidden) {
mTranslationY = currentScrollY - mSnappedScrolledY - mMaxTranslation;
// update state vars
@@ -110,20 +158,36 @@
}
private void apply() {
+ int uncappedTranslationY = mTranslationY;
mTranslationY = Math.max(mTranslationY, -mMaxTranslation);
- mHeaderView.setTranslationY(mTranslationY);
+ mPredictionRow.setTranslationY(uncappedTranslationY);
+ mTabLayout.setTranslationY(mTranslationY);
+ mDivider.setTranslationY(mTopOnlyMode ? uncappedTranslationY : mTranslationY);
mClip.top = mMaxTranslation + mTranslationY;
+ // clipping on a draw might cause additional redraw
mMainRV.setClipBounds(mClip);
if (mWorkRV != null) {
mWorkRV.setClipBounds(mClip);
}
}
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ if (!mTopOnlyMode && newState == RecyclerView.SCROLL_STATE_IDLE
+ && mTranslationY != -mMaxTranslation && mTranslationY != 0) {
+ float scroll = Math.abs(getCurrentScroll());
+ boolean expand = scroll > mMaxTranslation
+ ? Math.abs(mTranslationY) < mMaxTranslation / 2 : true;
+ setExpanded(expand);
+ }
+ }
+
private void setExpanded(boolean expand) {
int translateTo = expand ? 0 : -mMaxTranslation;
- mTranslationY = translateTo;
- apply();
-
+ mAnimator.setIntValues(mTranslationY, translateTo);
+ mAnimator.addUpdateListener(this);
+ mAnimator.setDuration(150);
+ mAnimator.start();
mHeaderHidden = !expand;
mSnappedScrolledY = expand ? getCurrentScroll() - mMaxTranslation : getCurrentScroll();
}
@@ -136,4 +200,10 @@
return mMainRVActive ? mMainScrolledY : mWorkScrolledY;
}
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mTranslationY = (Integer) animation.getAnimatedValue();
+ apply();
+ }
+
}
diff --git a/src/com/android/launcher3/allapps/PredictionRowView.java b/src/com/android/launcher3/allapps/PredictionRowView.java
index 5551f07..45ef6c1 100644
--- a/src/com/android/launcher3/allapps/PredictionRowView.java
+++ b/src/com/android/launcher3/allapps/PredictionRowView.java
@@ -21,9 +21,11 @@
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
import android.widget.LinearLayout;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ComponentKeyMapper;
@@ -43,6 +45,8 @@
private final List<ComponentKeyMapper<AppInfo>> mPredictedAppComponents = new ArrayList<>();
// The set of predicted apps resolved from the component names and the current set of apps
private final List<AppInfo> mPredictedApps = new ArrayList<>();
+ // This adapter is only used to create an identical item w/ same behavior as in the all apps RV
+ private AllAppsGridAdapter mAdapter;
public PredictionRowView(@NonNull Context context) {
this(context, null);
@@ -53,8 +57,11 @@
setOrientation(LinearLayout.HORIZONTAL);
}
- public void setComponentToAppMap(HashMap<ComponentKey, AppInfo> componentToAppMap) {
- this.mComponentToAppMap = componentToAppMap;
+ public void setup(AllAppsGridAdapter adapter,
+ HashMap<ComponentKey, AppInfo> componentToAppMap, int numPredictedAppsPerRow) {
+ mAdapter = adapter;
+ mComponentToAppMap = componentToAppMap;
+ mNumPredictedAppsPerRow = numPredictedAppsPerRow;
}
/**
@@ -64,10 +71,6 @@
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
}
- public void onAppsUpdated() {
- // TODO
- }
-
/**
* Returns the predicted apps.
*/
@@ -87,15 +90,35 @@
public void setPredictedApps(List<ComponentKeyMapper<AppInfo>> apps) {
mPredictedAppComponents.clear();
mPredictedAppComponents.addAll(apps);
+ mPredictedApps.addAll(processPredictedAppComponents(mPredictedAppComponents));
+ onAppsUpdated();
+ }
- List<AppInfo> newPredictedApps = processPredictedAppComponents(apps);
- // We only need to do work if any of the visible predicted apps have changed.
- if (!newPredictedApps.equals(mPredictedApps)) {
- if (newPredictedApps.size() == mPredictedApps.size()) {
- swapInNewPredictedApps(newPredictedApps);
+ private void onAppsUpdated() {
+ if (getChildCount() != mNumPredictedAppsPerRow) {
+ while (getChildCount() > mNumPredictedAppsPerRow) {
+ removeViewAt(0);
+ }
+ while (getChildCount() < mNumPredictedAppsPerRow) {
+ AllAppsGridAdapter.ViewHolder holder = mAdapter
+ .onCreateViewHolder(this, AllAppsGridAdapter.VIEW_TYPE_ICON);
+ BubbleTextView icon = (BubbleTextView) holder.itemView;
+ LinearLayout.LayoutParams params =
+ new LayoutParams(0, icon.getLayoutParams().height);
+ params.weight = 1;
+ icon.setLayoutParams(params);
+ addView(icon);
+ }
+ }
+
+ for (int i = 0; i < getChildCount(); i++) {
+ BubbleTextView icon = (BubbleTextView) getChildAt(i);
+ icon.reset();
+ if (mPredictedApps.size() > i) {
+ icon.setVisibility(View.VISIBLE);
+ icon.applyFromApplicationInfo(mPredictedApps.get(i));
} else {
- // We need to update the appIndex of all the items.
- onAppsUpdated();
+ icon.setVisibility(View.INVISIBLE);
}
}
}
@@ -124,16 +147,4 @@
}
return predictedApps;
}
-
- /**
- * Swaps out the old predicted apps with the new predicted apps, in place. This optimization
- * allows us to skip an entire relayout that would otherwise be called by notifyDataSetChanged.
- *
- * Note: This should only be called if the # of predicted apps is the same.
- * This method assumes that predicted apps are the first items in the adapter.
- */
- private void swapInNewPredictedApps(List<AppInfo> apps) {
- // TODO
- }
-
}
diff --git a/src/com/android/launcher3/config/BaseFlags.java b/src/com/android/launcher3/config/BaseFlags.java
index 1924710..7cf3da0 100644
--- a/src/com/android/launcher3/config/BaseFlags.java
+++ b/src/com/android/launcher3/config/BaseFlags.java
@@ -61,4 +61,6 @@
// When enabled shows a work profile tab in all apps
public static final boolean ALL_APPS_TABS_ENABLED = false;
+ // When enabled prediction row is rendered as it's own custom view
+ public static final boolean ALL_APPS_PREDICTION_ROW_VIEW = false;
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 816c1d4..8640401 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -118,9 +118,9 @@
deepShortcutMap.clear();
}
- public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
- String[] args) {
- if (args.length > 0 && TextUtils.equals(args[0], "--proto")) {
+ public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
+ String[] args) {
+ if (Arrays.asList(args).contains("--proto")) {
dumpProto(prefix, fd, writer, args);
return;
}
@@ -219,7 +219,7 @@
targetList.addAll(workspaces.valueAt(i).getFlattenedList());
}
- if (args.length > 1 && TextUtils.equals(args[1], "--debug")) {
+ if (Arrays.asList(args).contains("--debug")) {
for (int i = 0; i < targetList.size(); i++) {
writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
}
diff --git a/src/com/android/launcher3/util/TraceHelper.java b/src/com/android/launcher3/util/TraceHelper.java
index 5b66fcd..0f3ac57 100644
--- a/src/com/android/launcher3/util/TraceHelper.java
+++ b/src/com/android/launcher3/util/TraceHelper.java
@@ -33,9 +33,8 @@
private static final boolean ENABLED = FeatureFlags.IS_DOGFOOD_BUILD;
- private static final boolean SYSTEM_TRACE = true;
- private static final ArrayMap<String, MutableLong> sUpTimes =
- ENABLED ? new ArrayMap<String, MutableLong>() : null;
+ private static final boolean SYSTEM_TRACE = false;
+ private static final ArrayMap<String, MutableLong> sUpTimes = ENABLED ? new ArrayMap<>() : null;
public static void beginSection(String sectionName) {
if (ENABLED) {