Merge "Moving some callbacks from LauncherCallbacks to OverviewCallbacks" into ub-launcher3-edmonton
diff --git a/quickstep/AndroidManifest.xml b/quickstep/AndroidManifest.xml
index bab2cd7..c76ad83 100644
--- a/quickstep/AndroidManifest.xml
+++ b/quickstep/AndroidManifest.xml
@@ -21,6 +21,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3" >
+ <uses-sdk android:targetSdkVersion="28" android:minSdkVersion="28"/>
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<application
android:backupAgent="com.android.launcher3.LauncherBackupAgent"
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index 70e545f..120d6f9 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/res/layout/overview_clear_all_button.xml b/quickstep/res/layout/overview_clear_all_button.xml
index 1ada914..9c4b618 100644
--- a/quickstep/res/layout/overview_clear_all_button.xml
+++ b/quickstep/res/layout/overview_clear_all_button.xml
@@ -1,14 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
-<TextView
+<com.android.quickstep.views.ClearAllButton
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/clear_all_button"
style="@android:style/Widget.DeviceDefault.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start|top"
- android:fontFamily="sans-serif-medium"
android:text="@string/recents_clear_all"
android:textColor="?attr/workspaceTextColor"
+ android:visibility="invisible"
android:textSize="14sp"
/>
\ No newline at end of file
diff --git a/quickstep/res/values/config.xml b/quickstep/res/values/config.xml
index 37929b6..7673f69 100644
--- a/quickstep/res/values/config.xml
+++ b/quickstep/res/values/config.xml
@@ -17,4 +17,6 @@
<string name="task_overlay_factory_class" translatable="false"></string>
<string name="overview_callbacks_class" translatable="false"></string>
+
+ <string name="user_event_dispatcher_class" translatable="false">com.google.quickstep.logging.UserEventDispatcherExtension</string>
</resources>
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 85ccf29..94aaf15 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -335,10 +335,14 @@
launcherAnimator.play(dragLayerAlpha);
launcherAnimator.play(dragLayerTransY);
mDragLayer.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ // Pause page indicator animations as they lead to layer trashing.
+ mLauncher.getWorkspace().getPageIndicator().pauseAnimations();
endListener = () -> {
mDragLayer.setLayerType(View.LAYER_TYPE_NONE, null);
mDragLayer.setAlpha(1);
mDragLayer.setTranslationY(0);
+ mLauncher.getWorkspace().getPageIndicator().skipAnimationsToEnd();
};
}
return new Pair<>(launcherAnimator, endListener);
@@ -694,6 +698,13 @@
workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY);
workspaceAnimator.setDuration(333);
workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ currentPage.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ workspaceAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ currentPage.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ });
// Animate the shelf in two parts: slide in, and overeshoot.
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
@@ -715,7 +726,6 @@
allAppsOvershoot.setDuration(153);
allAppsOvershoot.setInterpolator(Interpolators.OVERSHOOT_0);
-
anim.play(workspaceAnimator);
anim.playSequentially(allAppsSlideIn, allAppsOvershoot);
anim.addListener(mReapplyStateListener);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
index 7da50c8..5a216f6 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import android.view.View;
@@ -47,10 +46,6 @@
@Override
public void onStateEnabled(Launcher launcher) {
- if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
- launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
- }
-
AbstractFloatingView.closeAllOpenViews(launcher);
dispatchWindowStateChanged(launcher);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index 61422e0..f87f006 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -16,7 +16,6 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
import static com.android.launcher3.states.RotationHelper.REQUEST_ROTATE;
@@ -59,10 +58,6 @@
@Override
public void onStateEnabled(Launcher launcher) {
- if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
- launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
- }
-
RecentsView rv = launcher.getOverviewPanel();
rv.setOverviewStateEnabled(true);
AbstractFloatingView.closeAllOpenViews(launcher);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 2e95c04..2a603d7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -187,10 +187,7 @@
builder = new AnimatorSetBuilder();
}
- if (mPendingAnimation != null) {
- mPendingAnimation.finish(false, Touch.SWIPE);
- mPendingAnimation = null;
- }
+ cancelPendingAnim();
RecentsView recentsView = mLauncher.getOverviewPanel();
TaskView taskView = (TaskView) recentsView.getChildAt(recentsView.getNextPage());
@@ -199,10 +196,16 @@
mPendingAnimation = recentsView.createTaskLauncherAnimation(taskView, maxAccuracy);
mPendingAnimation.anim.setInterpolator(Interpolators.ZOOM_IN);
- mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy);
+ Runnable onCancelRunnable = () -> {
+ cancelPendingAnim();
+ clearState();
+ };
+ mCurrentAnimation = AnimatorPlaybackController.wrap(mPendingAnimation.anim, maxAccuracy,
+ onCancelRunnable);
+ mLauncher.getStateManager().setCurrentUserControlledAnimation(mCurrentAnimation);
} else {
mCurrentAnimation = mLauncher.getStateManager()
- .createAnimationToNewWorkspace(mToState, builder, maxAccuracy);
+ .createAnimationToNewWorkspace(mToState, builder, maxAccuracy, this::clearState);
}
if (totalShift == 0) {
@@ -212,6 +215,13 @@
return 1 / totalShift;
}
+ private void cancelPendingAnim() {
+ if (mPendingAnimation != null) {
+ mPendingAnimation.finish(false, Touch.SWIPE);
+ mPendingAnimation = null;
+ }
+ }
+
@Override
protected void updateSwipeCompleteAnimation(ValueAnimator animator, long expectedDuration,
LauncherState targetState, float velocity, boolean isFling) {
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 49d4931..2579bc2 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -63,7 +63,7 @@
@Override
public void setStateWithAnimation(final LauncherState toState,
AnimatorSetBuilder builder, AnimationConfig config) {
- PropertySetter setter = config.getProperSetter(builder);
+ PropertySetter setter = config.getPropertySetter(builder);
float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0],
builder.getInterpolator(ANIM_OVERVIEW_TRANSLATION, LINEAR));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
index 63a7984..dc3f79c 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TaskViewTouchController.java
@@ -87,12 +87,14 @@
protected abstract boolean isRecentsInteractive();
+ protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+ }
+
@Override
public void onAnimationCancel(Animator animation) {
if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
- mDetector.finishedScrolling();
- mCurrentAnimation = null;
+ clearState();
}
}
@@ -194,8 +196,12 @@
mEndDisplacement = dl.getHeight() - mTempCords[1];
}
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setOnCancelRunnable(null);
+ }
mCurrentAnimation = AnimatorPlaybackController
- .wrap(mPendingAnimation.anim, maxDuration);
+ .wrap(mPendingAnimation.anim, maxDuration, this::clearState);
+ onUserControlledAnimationCreated(mCurrentAnimation);
mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
mProgressMultiplier = 1 / mEndDisplacement;
@@ -271,8 +277,17 @@
mPendingAnimation.finish(wasSuccess, logAction);
mPendingAnimation = null;
}
+ clearState();
+ }
+
+ private void clearState() {
mDetector.finishedScrolling();
+ mDetector.setDetectableScrollConditions(0, false);
mTaskBeingDragged = null;
mCurrentAnimation = null;
+ if (mPendingAnimation != null) {
+ mPendingAnimation.finish(false, Touch.SWIPE);
+ mPendingAnimation = null;
+ }
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 01e2bf3..06099b9 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -21,6 +21,8 @@
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
+import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import android.content.Context;
@@ -28,7 +30,9 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
@@ -44,19 +48,19 @@
return new TouchController[] {
launcher.getDragController(),
new OverviewToAllAppsTouchController(launcher),
- new LauncherTaskViewcontroller(launcher)};
+ new LauncherTaskViewController(launcher)};
}
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return new TouchController[] {
launcher.getDragController(),
new OverviewToAllAppsTouchController(launcher),
new LandscapeEdgeSwipeController(launcher),
- new LauncherTaskViewcontroller(launcher)};
+ new LauncherTaskViewController(launcher)};
} else {
return new TouchController[] {
launcher.getDragController(),
new PortraitStatesTouchController(launcher),
- new LauncherTaskViewcontroller(launcher)};
+ new LauncherTaskViewController(launcher)};
}
}
@@ -88,6 +92,55 @@
recents.reset();
}
+ public static void onCreate(Launcher launcher) {
+ if (!launcher.getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
+ launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateSetImmediately(LauncherState state) {
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ boolean swipeUpEnabled = OverviewInteractionState.getInstance(launcher)
+ .isSwipeUpGestureEnabled();
+ LauncherState prevState = launcher.getStateManager().getLastState();
+
+ if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
+ && finalState == ALL_APPS && prevState == NORMAL))) {
+ launcher.getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
+ launcher.getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+
+ if (!launcher.getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
+ launcher.getStateManager().addStateListener(new LauncherStateManager.StateListener() {
+ @Override
+ public void onStateSetImmediately(LauncherState state) {
+ }
+
+ @Override
+ public void onStateTransitionStart(LauncherState toState) {
+ }
+
+ @Override
+ public void onStateTransitionComplete(LauncherState finalState) {
+ LauncherState prevState = launcher.getStateManager().getLastState();
+
+ if (finalState == ALL_APPS && prevState == OVERVIEW) {
+ launcher.getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
+ launcher.getStateManager().removeStateListener(this);
+ }
+ }
+ });
+ }
+ }
+
public static void onStart(Context context) {
RecentsModel model = RecentsModel.getInstance(context);
if (model != null) {
@@ -114,9 +167,9 @@
}
}
- private static class LauncherTaskViewcontroller extends TaskViewTouchController<Launcher> {
+ private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
- public LauncherTaskViewcontroller(Launcher activity) {
+ public LauncherTaskViewController(Launcher activity) {
super(activity);
}
@@ -124,5 +177,10 @@
protected boolean isRecentsInteractive() {
return mActivity.isInState(OVERVIEW);
}
+
+ @Override
+ protected void onUserControlledAnimationCreated(AnimatorPlaybackController animController) {
+ mActivity.getStateManager().setCurrentUserControlledAnimation(animController);
+ }
}
}
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 1026b0e..529a765 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -253,7 +253,7 @@
@Override
public RecentsView getVisibleRecentsView() {
Launcher launcher = getVisibleLaucher();
- return launcher != null && launcher.isInState(OVERVIEW)
+ return launcher != null && launcher.getStateManager().getState().overviewUi
? launcher.getOverviewPanel() : null;
}
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index ea9d009..adc245b 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -16,6 +16,7 @@
package com.android.quickstep;
+import android.util.Log;
import android.view.HapticFeedbackConstants;
import com.android.launcher3.Alarm;
@@ -42,9 +43,10 @@
* Snap to a new page when crossing these thresholds. The first and last auto-advance.
*/
private static final float[] QUICK_SCRUB_THRESHOLDS = new float[] {
- 0.05f, 0.35f, 0.65f, 0.95f
+ 0.04f, 0.27f, 0.50f, 0.73f, 0.96f
};
+ private static final String TAG = "QuickScrubController";
private static final boolean ENABLE_AUTO_ADVANCE = true;
private static final long AUTO_ADVANCE_DELAY = 500;
private static final int QUICKSCRUB_SNAP_DURATION_PER_PAGE = 325;
@@ -58,6 +60,8 @@
private int mQuickScrubSection;
private boolean mStartedFromHome;
private boolean mFinishedTransitionToQuickScrub;
+ private Runnable mOnFinishedTransitionToQuickScrubRunnable;
+ private ActivityControlHelper mActivityControlHelper;
public QuickScrubController(BaseActivity activity, RecentsView recentsView) {
mActivity = activity;
@@ -68,11 +72,13 @@
}
}
- public void onQuickScrubStart(boolean startingFromHome) {
+ public void onQuickScrubStart(boolean startingFromHome, ActivityControlHelper controlHelper) {
mInQuickScrub = true;
mStartedFromHome = startingFromHome;
mQuickScrubSection = 0;
mFinishedTransitionToQuickScrub = false;
+ mOnFinishedTransitionToQuickScrubRunnable = null;
+ mActivityControlHelper = controlHelper;
snapToNextTaskIfAvailable();
mActivity.getUserEventDispatcher().resetActionDurationMillis();
@@ -85,13 +91,18 @@
}
int page = mRecentsView.getNextPage();
Runnable launchTaskRunnable = () -> {
- TaskView taskView = ((TaskView) mRecentsView.getPageAt(page));
+ TaskView taskView = mRecentsView.getPageAt(page);
if (taskView != null) {
- taskView.launchTask(true);
+ taskView.launchTask(true, (result) -> {
+ if (!result) {
+ taskView.notifyTaskLaunchFailed(TAG);
+ breakOutOfQuickScrub();
+ }
+ }, taskView.getHandler());
} else {
- // Break out of quick scrub so user can interact with launcher.
- mActivity.onBackPressed();
+ breakOutOfQuickScrub();
}
+ mActivityControlHelper = null;
};
int snapDuration = Math.abs(page - mRecentsView.getPageNearestToCenterOfScreen())
* QUICKSCRUB_END_SNAP_DURATION_PER_PAGE;
@@ -100,13 +111,27 @@
mRecentsView.setNextPageSwitchRunnable(launchTaskRunnable);
} else {
// No page move needed, just launch it
- launchTaskRunnable.run();
+ if (mFinishedTransitionToQuickScrub) {
+ launchTaskRunnable.run();
+ } else {
+ mOnFinishedTransitionToQuickScrubRunnable = launchTaskRunnable;
+ }
}
mActivity.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP,
ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
ContainerType.WORKSPACE : ContainerType.APP);
}
+ /**
+ * Attempts to go to normal overview or back to home, so UI doesn't prevent user interaction.
+ */
+ private void breakOutOfQuickScrub() {
+ if (mRecentsView.getChildCount() == 0 || mActivityControlHelper == null
+ || !mActivityControlHelper.switchToRecentsIfVisible()) {
+ mActivity.onBackPressed();
+ }
+ }
+
public void onQuickScrubProgress(float progress) {
int quickScrubSection = 0;
for (float threshold : QUICK_SCRUB_THRESHOLDS) {
@@ -135,6 +160,10 @@
public void onFinishedTransitionToQuickScrub() {
mFinishedTransitionToQuickScrub = true;
+ if (mOnFinishedTransitionToQuickScrubRunnable != null) {
+ mOnFinishedTransitionToQuickScrubRunnable.run();
+ mOnFinishedTransitionToQuickScrubRunnable = null;
+ }
}
public void snapToNextTaskIfAvailable() {
@@ -166,6 +195,12 @@
@Override
public void onAlarm(Alarm alarm) {
int currPage = mRecentsView.getNextPage();
+ boolean recentsVisible = mActivityControlHelper != null
+ && mActivityControlHelper.getVisibleRecentsView() != null;
+ if (!recentsVisible) {
+ Log.w(TAG, "Failed to auto advance; recents not visible");
+ return;
+ }
if (mQuickScrubSection == QUICK_SCRUB_THRESHOLDS.length
&& currPage < mRecentsView.getPageCount() - 1) {
goToPageWithHaptic(currPage + 1);
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
index 6fece31..228af8e 100644
--- a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -27,7 +27,6 @@
import android.os.UserHandle;
import android.util.Log;
import android.view.View;
-import android.view.ViewTreeObserver.OnPreDrawListener;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
@@ -239,7 +238,7 @@
Log.w(TAG, "Failed to start screen pinning: ", e);
}
} else {
- Log.w(TAG, taskView.getLaunchTaskFailedMsg());
+ taskView.notifyTaskLaunchFailed(TAG);
}
};
taskView.launchTask(true, resultCallback, mHandler);
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 60e6d2c..3babd1f 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -21,9 +21,7 @@
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_POINTER_UP;
import static android.view.MotionEvent.ACTION_UP;
-
-import static com.android.systemui.shared.system.ActivityManagerWrapper
- .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_NONE;
import android.annotation.TargetApi;
@@ -352,8 +350,8 @@
mStartPending = true;
Runnable action = () -> {
- mQuickScrubController.onQuickScrubStart(
- mActivityHelper.onQuickInteractionStart(mActivity, true));
+ mQuickScrubController.onQuickScrubStart(mActivityHelper.onQuickInteractionStart(
+ mActivity, true), mActivityHelper);
mQuickScrubController.onQuickScrubProgress(mLastProgress);
mStartPending = false;
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 5fbd05f..4e17b4b 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -760,7 +760,7 @@
}
mActivityControlHelper.onQuickInteractionStart(mActivity, false);
- mQuickScrubController.onQuickScrubStart(false);
+ mQuickScrubController.onQuickScrubStart(false, mActivityControlHelper);
// Inform the last progress in case we skipped before.
mQuickScrubController.onQuickScrubProgress(mCurrentQuickScrubProgress);
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
new file mode 100644
index 0000000..d4cdd35
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.logging;
+
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.systemui.shared.system.MetricsLoggerCompat;
+
+/**
+ * This class handles AOSP MetricsLogger function calls.
+ */
+public class UserEventDispatcherExtension extends UserEventDispatcher {
+
+ public void logStateChangeAction(int action, int dir, int srcChildTargetType,
+ int srcParentContainerType, int dstContainerType,
+ int pageIndex) {
+ new MetricsLoggerCompat().visibility(MetricsLoggerCompat.OVERVIEW_ACTIVITY,
+ dstContainerType == LauncherLogProto.ContainerType.TASKSWITCHER);
+ super.logStateChangeAction(action, dir, srcChildTargetType, srcParentContainerType,
+ dstContainerType, pageIndex);
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
new file mode 100644
index 0000000..f3a0e4f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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.views;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.Button;
+
+public class ClearAllButton extends Button {
+ RecentsView mRecentsView;
+
+ public ClearAllButton(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setRecentsView(RecentsView recentsView) {
+ mRecentsView = recentsView;
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ // Should be visible to accessibility even when completely covered by the task.
+ // Otherwise, we won't be able to scroll to it.
+ info.setVisibleToUser(true);
+ }
+
+ @Override
+ public boolean performAccessibilityAction(int action, Bundle arguments) {
+ final boolean res = super.performAccessibilityAction(action, arguments);
+ if (action == ACTION_ACCESSIBILITY_FOCUS) {
+ mRecentsView.revealClearAllButton();
+ }
+ return res;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f521b25..7a04dcd 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -41,7 +41,6 @@
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.FloatProperty;
-import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -144,6 +143,14 @@
// TODO: Re-enable layout transitions for addition of the unpinned task
reloadIfNeeded();
}
+
+ @Override
+ public void onTaskRemoved(int taskId) {
+ TaskView taskView = getTaskView(taskId);
+ if (taskView != null) {
+ dismissTask(taskView, true /* animate */, false /* removeTask */);
+ }
+ }
};
private int mLoadPlanId = -1;
@@ -264,6 +271,7 @@
loader.unloadTaskData(task);
loader.getHighResThumbnailLoader().onTaskInvisible(task);
}
+ onChildViewsChanged();
}
public boolean isTaskViewVisible(TaskView tv) {
@@ -306,59 +314,31 @@
final int childCount = getChildCount();
if (mShowEmptyMessage || childCount == 0) return 0;
- // Current visible coordinate of the end of the oldest task.
final View lastChild = getChildAt(childCount - 1);
+
+ // Current visible coordinate of the end of the oldest task.
final int carouselCurrentEnd =
(mIsRtl ? lastChild.getLeft() : lastChild.getRight()) - getScrollX();
- // As the end (let's call it E aka carouselCurrentEnd) of the carousel moves over Clear
- // all button, the button changes trasparency.
- // fullAlphaX and zeroAlphaX are the points of the 100% and 0% alpha correspondingly.
- // Alpha changes linearly between 100% and 0% as E moves through this range. It doesn't
- // change outside of the range.
+ // Visible button-facing end of a centered task.
+ final int centeredTaskEnd = mIsRtl ?
+ getPaddingLeft() + mInsets.left :
+ getWidth() - getPaddingRight() - mInsets.right;
- // Once E hits the border of the Clear-All button that looks towards the most recent
- // task, the whole button is uncovered, and it should have alpha 100%.
- final float fullAlphaX = mIsRtl ?
- mClearAllButton.getX() + mClearAllButton.getWidth() :
- mClearAllButton.getX();
-
- // X coordinate of the carousel scrolled as far as possible in the direction towards the
- // button. Logically, the button is "behind" the least recent task. This is the
- // coordinate of the end of the least recent task in the carousel just after opening,
- // with the most recent task in the center, and the rest of tasks go from that point
- // towards and potentially behind the button.
- final int carouselMotionLimit = getScrollForPage(childCount - 1) - getScrollForPage(0) +
- (mIsRtl ?
- getPaddingLeft() + mInsets.left :
- getWidth() - getPaddingRight() - mInsets.right);
-
- // The carousel might not be able to ever cover a part of the Clear-all button. Then
- // always show the button as 100%. Technically, this check also prevents dividing by zero
- // or getting a negative transparency ratio.
- if (mIsRtl ? carouselMotionLimit >= fullAlphaX : carouselMotionLimit <= fullAlphaX) {
- return 1;
- }
-
- // If the carousel is able to cover the button completely, we make the button completely
- // transparent when E hits the border of the button farthest from the most recent task.
- // Or, the carousel may not be able to move that far towards the button so it completely
- // covers the it. Then we set the motion limit position of the carousel as the point
- // where the button reaches 0 alpha.
- final float zeroAlphaX = mIsRtl ?
- Math.max(mClearAllButton.getX(), carouselMotionLimit) :
- Math.min(mClearAllButton.getX() + mClearAllButton.getWidth(), carouselMotionLimit);
+ // The distance of the carousel travel during which the alpha changes from 0 to 1. This
+ // is the motion between the oldest task in its centered position and the oldest task
+ // scrolled to the end.
+ final int alphaChangeRange = (mIsRtl ? 0 : mMaxScrollX) - getScrollForPage(childCount - 1);
return Utilities.boundToRange(
- (zeroAlphaX - carouselCurrentEnd) /
- (zeroAlphaX - fullAlphaX), 0, 1);
+ ((float) (centeredTaskEnd - carouselCurrentEnd)) /
+ alphaChangeRange, 0, 1);
}
private void updateClearAllButtonAlpha() {
if (mClearAllButton != null) {
final float alpha = calculateClearAllButtonAlpha();
mClearAllButton.setAlpha(alpha * mContentAlpha);
- mClearAllButton.setVisibility(alpha == 0 ? INVISIBLE : VISIBLE);
}
}
@@ -371,7 +351,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN && mTouchState == TOUCH_STATE_REST
- && mScroller.isFinished() && mClearAllButton.getVisibility() == View.VISIBLE) {
+ && mScroller.isFinished() && mClearAllButton.getAlpha() > 0) {
mClearAllButton.getHitRect(mTempRect);
mTempRect.offset(-getLeft(), -getTop());
if (mTempRect.contains((int) ev.getX(), (int) ev.getY())) {
@@ -1011,6 +991,7 @@
super.onViewAdded(child);
child.setAlpha(mContentAlpha);
setAdjacentScale(mAdjacentScale);
+ onChildViewsChanged();
}
@Override
@@ -1177,7 +1158,7 @@
tv.setVisibility(VISIBLE);
getOverlay().remove(drawable);
if (!result) {
- Log.w(TAG, tv.getLaunchTaskFailedMsg());
+ tv.notifyTaskLaunchFailed(TAG);
}
};
@@ -1243,4 +1224,15 @@
mClearAllButton = clearAllButton;
updateClearAllButtonAlpha();
}
+
+ private void onChildViewsChanged() {
+ final int childCount = getChildCount();
+ mClearAllButton.setAccessibilityTraversalAfter(
+ childCount == 0 ? NO_ID : getChildAt(childCount - 1).getId());
+ mClearAllButton.setVisibility(childCount == 0 ? INVISIBLE : VISIBLE);
+ }
+
+ public void revealClearAllButton() {
+ scrollTo(mIsRtl ? 0 : computeMaxScrollX(), 0);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
index 15925b5..a951de9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 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.views;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
@@ -9,7 +25,6 @@
import android.util.FloatProperty;
import android.view.Gravity;
import android.view.MotionEvent;
-import android.view.View;
import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R;
@@ -31,7 +46,7 @@
private final Rect mTempRect = new Rect();
private RecentsView mRecentsView;
- private View mClearAllButton;
+ private ClearAllButton mClearAllButton;
public RecentsViewContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -48,13 +63,15 @@
mRecentsView.dismissAllTasks();
});
- mRecentsView = (RecentsView) findViewById(R.id.overview_panel);
+ mRecentsView = findViewById(R.id.overview_panel);
final InsettableFrameLayout.LayoutParams params =
(InsettableFrameLayout.LayoutParams) mClearAllButton.getLayoutParams();
params.gravity = Gravity.TOP | (RecentsView.FLIP_RECENTS ? Gravity.START : Gravity.END);
mClearAllButton.setLayoutParams(params);
mClearAllButton.forceHasOverlappingRendering(false);
+
mRecentsView.setClearAllButton(mClearAllButton);
+ mClearAllButton.setRecentsView(mRecentsView);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 93bdab2..a7527a6 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -16,6 +16,7 @@
package com.android.quickstep.views;
+import static android.widget.Toast.LENGTH_SHORT;
import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
import android.animation.Animator;
@@ -36,6 +37,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.Toast;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
@@ -138,7 +140,7 @@
public void launchTask(boolean animate) {
launchTask(animate, (result) -> {
if (!result) {
- Log.w(TAG, getLaunchTaskFailedMsg());
+ notifyTaskLaunchFailed(TAG);
}
}, getHandler());
}
@@ -312,11 +314,12 @@
return super.performAccessibilityAction(action, arguments);
}
- public String getLaunchTaskFailedMsg() {
+ public void notifyTaskLaunchFailed(String tag) {
String msg = "Failed to launch task";
if (mTask != null) {
msg += " (task=" + mTask.key.baseIntent + " userId=" + mTask.key.userId + ")";
}
- return msg;
+ Log.w(tag, msg);
+ Toast.makeText(getContext(), R.string.activity_not_available, LENGTH_SHORT).show();
}
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index ab73074..168bd08 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -780,7 +780,7 @@
}
private static final class IconDB extends SQLiteCacheHelper {
- private final static int RELEASE_VERSION = 21;
+ private final static int RELEASE_VERSION = 22;
private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid";
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7e551b1..9a9e001 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -283,6 +283,7 @@
mDragController = new DragController(this);
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new LauncherStateManager(this);
+ UiFactory.onCreate(this);
mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
@@ -318,7 +319,7 @@
if (!internalStateHandled) {
// If we are not binding synchronously, show a fade in animation when
// the first page bind completes.
- mDragLayer.setAlpha(0);
+ mLauncherView.setAlpha(0);
}
} else {
// Pages bound synchronously.
@@ -2125,8 +2126,8 @@
@Override
public void finishFirstPageBind(final ViewOnDrawExecutor executor) {
- if (mDragLayer.getAlpha() < 1) {
- mDragLayer.animate().alpha(1).withEndAction(
+ if (mLauncherView.getAlpha() < 1) {
+ mLauncherView.animate().alpha(1).withEndAction(
executor == null ? null : executor::onLoadAnimationCompleted).start();
} else if (executor != null) {
executor.onLoadAnimationCompleted();
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index d196c37..7f25301 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -238,16 +238,18 @@
*/
public AnimatorPlaybackController createAnimationToNewWorkspace(
LauncherState state, long duration) {
- return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration);
+ return createAnimationToNewWorkspace(state, new AnimatorSetBuilder(), duration, null);
}
- public AnimatorPlaybackController createAnimationToNewWorkspace(
- LauncherState state, AnimatorSetBuilder builder, long duration) {
+ public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
+ AnimatorSetBuilder builder, long duration, Runnable onCancelRunnable) {
mConfig.reset();
mConfig.userControlled = true;
mConfig.duration = duration;
- return AnimatorPlaybackController.wrap(
- createAnimationToNewWorkspaceInternal(state, builder, null), duration);
+ mConfig.playbackController = AnimatorPlaybackController.wrap(
+ createAnimationToNewWorkspaceInternal(state, builder, null), duration,
+ onCancelRunnable);
+ return mConfig.playbackController;
}
protected AnimatorSet createAnimationToNewWorkspaceInternal(final LauncherState state,
@@ -358,6 +360,12 @@
mConfig.reset();
}
+ public void setCurrentUserControlledAnimation(AnimatorPlaybackController controller) {
+ setCurrentAnimation(controller.getTarget());
+ mConfig.userControlled = true;
+ mConfig.playbackController = controller;
+ }
+
/**
* Sets the animation as the current state animation, i.e., canceled when
* starting another animation and may block some launcher interactions while running.
@@ -405,30 +413,39 @@
public static class AnimationConfig extends AnimatorListenerAdapter {
public long duration;
public boolean userControlled;
- private PropertySetter mProperSetter;
+ public AnimatorPlaybackController playbackController;
+ private PropertySetter mPropertySetter;
private AnimatorSet mCurrentAnimation;
private LauncherState mTargetState;
+ /**
+ * Cancels the current animation and resets config variables.
+ */
public void reset() {
duration = 0;
userControlled = false;
- mProperSetter = null;
+ mPropertySetter = null;
mTargetState = null;
- if (mCurrentAnimation != null) {
+ if (playbackController != null) {
+ playbackController.getAnimationPlayer().cancel();
+ playbackController.dispatchOnCancel();
+ } else if (mCurrentAnimation != null) {
mCurrentAnimation.setDuration(0);
mCurrentAnimation.cancel();
- mCurrentAnimation = null;
}
+
+ mCurrentAnimation = null;
+ playbackController = null;
}
- public PropertySetter getProperSetter(AnimatorSetBuilder builder) {
- if (mProperSetter == null) {
- mProperSetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
+ public PropertySetter getPropertySetter(AnimatorSetBuilder builder) {
+ if (mPropertySetter == null) {
+ mPropertySetter = duration == 0 ? NO_ANIM_PROPERTY_SETTER
: new AnimatedPropertySetter(duration, builder);
}
- return mProperSetter;
+ return mPropertySetter;
}
@Override
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 8311ab9..87ee076 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1171,11 +1171,19 @@
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()));
+ 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();
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 420a7c4..77a45bf 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -53,7 +53,7 @@
public void setStateWithAnimation(LauncherState toState, AnimatorSetBuilder builder,
AnimationConfig config) {
- setWorkspaceProperty(toState, config.getProperSetter(builder));
+ setWorkspaceProperty(toState, config.getPropertySetter(builder));
}
public float getFinalScale() {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 37405db..211d98f 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -45,6 +45,7 @@
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.AllAppsScrim;
import com.android.launcher3.keyboard.FocusedItemDecorator;
@@ -122,6 +123,13 @@
}
@Override
+ protected void setDampedScrollShift(float shift) {
+ // Bound the shift amount to avoid content from drawing on top (Y-val) of the QSB.
+ float maxShift = getSearchView().getHeight() / 2f;
+ super.setDampedScrollShift(Utilities.boundToRange(shift, -maxShift, maxShift));
+ }
+
+ @Override
public void onDeviceProfileChanged(DeviceProfile dp) {
for (AdapterHolder holder : mAH) {
if (holder.recyclerView != null) {
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index 6a0e1cc..53d3da6 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -163,7 +163,7 @@
AnimatorSetBuilder builder, AnimationConfig config) {
float targetProgress = toState.getVerticalProgress(mLauncher);
if (Float.compare(mProgress, targetProgress) == 0) {
- setAlphas(toState, config.getProperSetter(builder));
+ setAlphas(toState, config.getPropertySetter(builder));
// Fail fast
onProgressAnimationEnd();
return;
@@ -178,7 +178,7 @@
builder.play(anim);
- setAlphas(toState, config.getProperSetter(builder));
+ setAlphas(toState, config.getPropertySetter(builder));
}
private void setAlphas(LauncherState toState, PropertySetter setter) {
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index f73916c..8f1c8df 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -42,7 +42,7 @@
*/
public class DiscoveryBounce extends AbstractFloatingView {
- private static final long DELAY_MS = 200;
+ private static final long DELAY_MS = 450;
public static final String HOME_BOUNCE_SEEN = "launcher.apps_view_shown";
public static final String SHELF_BOUNCE_SEEN = "launcher.shelf_bounce_seen";
diff --git a/src/com/android/launcher3/anim/AnimatorPlaybackController.java b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
index 1dba7d6..8e729e8 100644
--- a/src/com/android/launcher3/anim/AnimatorPlaybackController.java
+++ b/src/com/android/launcher3/anim/AnimatorPlaybackController.java
@@ -35,18 +35,23 @@
*/
public abstract class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateListener {
+ public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
+ return wrap(anim, duration, null);
+ }
+
/**
* Creates an animation controller for the provided animation.
* The actual duration does not matter as the animation is manually controlled. It just
* needs to be larger than the total number of pixels so that we don't have jittering due
* to float (animation-fraction * total duration) to int conversion.
*/
- public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration) {
+ public static AnimatorPlaybackController wrap(AnimatorSet anim, long duration,
+ Runnable onCancelRunnable) {
/**
* TODO: use {@link AnimatorSet#setCurrentPlayTime(long)} once b/68382377 is fixed.
*/
- return new AnimatorPlaybackControllerVL(anim, duration);
+ return new AnimatorPlaybackControllerVL(anim, duration, onCancelRunnable);
}
private final ValueAnimator mAnimationPlayer;
@@ -58,10 +63,13 @@
private Runnable mEndAction;
protected boolean mTargetCancelled = false;
+ protected Runnable mOnCancelRunnable;
- protected AnimatorPlaybackController(AnimatorSet anim, long duration) {
+ protected AnimatorPlaybackController(AnimatorSet anim, long duration,
+ Runnable onCancelRunnable) {
mAnim = anim;
mDuration = duration;
+ mOnCancelRunnable = onCancelRunnable;
mAnimationPlayer = ValueAnimator.ofFloat(0, 1);
mAnimationPlayer.setInterpolator(Interpolators.LINEAR);
@@ -72,6 +80,21 @@
@Override
public void onAnimationCancel(Animator animation) {
mTargetCancelled = true;
+ if (mOnCancelRunnable != null) {
+ mOnCancelRunnable.run();
+ mOnCancelRunnable = null;
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mTargetCancelled = false;
+ mOnCancelRunnable = null;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mTargetCancelled = false;
}
});
}
@@ -163,12 +186,33 @@
}
}
+ public void dispatchOnCancel() {
+ dispatchOnCancelRecursively(mAnim);
+ }
+
+ private void dispatchOnCancelRecursively(Animator animator) {
+ for (AnimatorListener l : nonNullList(animator.getListeners())) {
+ l.onAnimationCancel(animator);
+ }
+
+ if (animator instanceof AnimatorSet) {
+ for (Animator anim : nonNullList(((AnimatorSet) animator).getChildAnimations())) {
+ dispatchOnCancelRecursively(anim);
+ }
+ }
+ }
+
+ public void setOnCancelRunnable(Runnable runnable) {
+ mOnCancelRunnable = runnable;
+ }
+
public static class AnimatorPlaybackControllerVL extends AnimatorPlaybackController {
private final ValueAnimator[] mChildAnimations;
- private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration) {
- super(anim, duration);
+ private AnimatorPlaybackControllerVL(AnimatorSet anim, long duration,
+ Runnable onCancelRunnable) {
+ super(anim, duration, onCancelRunnable);
// Build animation list
ArrayList<ValueAnimator> childAnims = new ArrayList<>();
diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java
index 81f3f90..a2a0801 100644
--- a/src/com/android/launcher3/graphics/IconNormalizer.java
+++ b/src/com/android/launcher3/graphics/IconNormalizer.java
@@ -24,6 +24,7 @@
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
@@ -66,12 +67,10 @@
private final int mMaxSize;
private final Bitmap mBitmap;
- private final Bitmap mBitmapARGB;
private final Canvas mCanvas;
private final Paint mPaintMaskShape;
private final Paint mPaintMaskShapeOutline;
private final byte[] mPixels;
- private final int[] mPixelsARGB;
private final Rect mAdaptiveIconBounds;
private float mAdaptiveIconScale;
@@ -83,9 +82,6 @@
private final Path mShapePath;
private final Matrix mMatrix;
- private final Paint mPaintIcon;
- private final Canvas mCanvasARGB;
-
/** package private **/
IconNormalizer(Context context) {
// Use twice the icon size as maximum size to avoid scaling down twice.
@@ -93,19 +89,11 @@
mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
mCanvas = new Canvas(mBitmap);
mPixels = new byte[mMaxSize * mMaxSize];
- mPixelsARGB = new int[mMaxSize * mMaxSize];
mLeftBorder = new float[mMaxSize];
mRightBorder = new float[mMaxSize];
mBounds = new Rect();
mAdaptiveIconBounds = new Rect();
- // Needed for isShape() method
- mBitmapARGB = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ARGB_8888);
- mCanvasARGB = new Canvas(mBitmapARGB);
-
- mPaintIcon = new Paint();
- mPaintIcon.setColor(Color.WHITE);
-
mPaintMaskShape = new Paint();
mPaintMaskShape.setColor(Color.RED);
mPaintMaskShape.setStyle(Paint.Style.FILL);
@@ -115,7 +103,7 @@
mPaintMaskShapeOutline.setStrokeWidth(2 * context.getResources().getDisplayMetrics().density);
mPaintMaskShapeOutline.setStyle(Paint.Style.STROKE);
mPaintMaskShapeOutline.setColor(Color.BLACK);
- mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+ mPaintMaskShapeOutline.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mShapePath = new Path();
mMatrix = new Matrix();
@@ -141,8 +129,6 @@
// Condition 2:
// Actual icon (white) and the fitted shape (e.g., circle)(red) XOR operation
// should generate transparent image, if the actual icon is equivalent to the shape.
- mBitmapARGB.eraseColor(Color.TRANSPARENT);
- mCanvasARGB.drawBitmap(mBitmap, 0, 0, mPaintIcon);
// Fit the shape within the icon's bounding box
mMatrix.reset();
@@ -151,31 +137,41 @@
maskPath.transform(mMatrix, mShapePath);
// XOR operation
- mCanvasARGB.drawPath(mShapePath, mPaintMaskShape);
+ mCanvas.drawPath(mShapePath, mPaintMaskShape);
// DST_OUT operation around the mask path outline
- mCanvasARGB.drawPath(mShapePath, mPaintMaskShapeOutline);
+ mCanvas.drawPath(mShapePath, mPaintMaskShapeOutline);
// Check if the result is almost transparent
- return isTransparentBitmap(mBitmapARGB);
+ return isTransparentBitmap();
}
/**
* Used to determine if certain the bitmap is transparent.
*/
- private boolean isTransparentBitmap(Bitmap bitmap) {
- int w = mBounds.width();
- int h = mBounds.height();
- bitmap.getPixels(mPixelsARGB, 0 /* the first index to write into the array */,
- w /* stride */,
- mBounds.left, mBounds.top,
- w, h);
+ private boolean isTransparentBitmap() {
+ ByteBuffer buffer = ByteBuffer.wrap(mPixels);
+ buffer.rewind();
+ mBitmap.copyPixelsToBuffer(buffer);
+
+ int y = mBounds.top;
+ // buffer position
+ int index = y * mMaxSize;
+ // buffer shift after every row, width of buffer = mMaxSize
+ int rowSizeDiff = mMaxSize - mBounds.right;
+
int sum = 0;
- for (int i = w * h - 1; i >= 0; i--) {
- if(Color.alpha(mPixelsARGB[i]) > MIN_VISIBLE_ALPHA) {
+ for (; y < mBounds.bottom; y++) {
+ index += mBounds.left;
+ for (int x = mBounds.left; x < mBounds.right; x++) {
+ if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
sum++;
+ }
+ index++;
}
+ index += rowSizeDiff;
}
+
float percentageDiffPixels = ((float) sum) / (mBounds.width() * mBounds.height());
return percentageDiffPixels < PIXEL_DIFF_PERCENTAGE_THRESHOLD;
}
@@ -306,7 +302,7 @@
mBounds.bottom = bottomY;
if (outBounds != null) {
- outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top),
+ outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height,
1 - ((float) mBounds.right) / width,
1 - ((float) mBounds.bottom) / height);
}
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index bf870cc..2c1eb32 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -285,7 +285,7 @@
* Used primarily for swipe up and down when state changes when swipe up happens from the
* navbar bezel, the {@param srcChildContainerType} is NAVBAR and
* {@param srcParentContainerType} is either one of the two
- * (1) WORKSPACE: if the launcher the foreground activity
+ * (1) WORKSPACE: if the launcher is the foreground activity
* (2) APP: if another app was the foreground activity
*/
public void logStateChangeAction(int action, int dir, int srcChildTargetType,
diff --git a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
index 3c16cde..1383f53 100644
--- a/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
+++ b/src/com/android/launcher3/pageindicators/WorkspacePageIndicator.java
@@ -41,8 +41,9 @@
private static final int LINE_ALPHA_ANIMATOR_INDEX = 0;
private static final int NUM_PAGES_ANIMATOR_INDEX = 1;
private static final int TOTAL_SCROLL_ANIMATOR_INDEX = 2;
+ private static final int ANIMATOR_COUNT = 3;
- private ValueAnimator[] mAnimators = new ValueAnimator[3];
+ private ValueAnimator[] mAnimators = new ValueAnimator[ANIMATOR_COUNT];
private final Handler mDelayedLineFadeHandler = new Handler(Looper.getMainLooper());
private final Launcher mLauncher;
@@ -232,6 +233,28 @@
mAnimators[animatorIndex].start();
}
+ /**
+ * Pauses all currently running animations.
+ */
+ public void pauseAnimations() {
+ for (int i = 0; i < ANIMATOR_COUNT; i++) {
+ if (mAnimators[i] != null) {
+ mAnimators[i].pause();
+ }
+ }
+ }
+
+ /**
+ * Force-ends all currently running or paused animations.
+ */
+ public void skipAnimationsToEnd() {
+ for (int i = 0; i < ANIMATOR_COUNT; i++) {
+ if (mAnimators[i] != null) {
+ mAnimators[i].end();
+ }
+ }
+ }
+
@Override
public void setInsets(Rect insets) {
DeviceProfile grid = mLauncher.getDeviceProfile();
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c0ad110..d5c0788 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -18,10 +18,7 @@
import static com.android.launcher3.Utilities.SINGLE_FRAME_MS;
import static com.android.launcher3.anim.Interpolators.scrollInterpolatorForVelocity;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
-import android.util.Log;
import android.view.MotionEvent;
import com.android.launcher3.Launcher;
@@ -30,13 +27,13 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.PendingAnimation;
+import com.android.launcher3.util.TouchController;
/**
* TouchController for handling state changes
*/
-public abstract class AbstractStateChangeTouchController extends AnimatorListenerAdapter
+public abstract class AbstractStateChangeTouchController
implements TouchController, SwipeDetector.Listener {
private static final String TAG = "ASCTouchController";
@@ -146,8 +143,10 @@
mToState = newToState;
mStartProgress = 0;
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setOnCancelRunnable(null);
+ }
mProgressMultiplier = initCurrentAnimation();
- mCurrentAnimation.getTarget().addListener(this);
mCurrentAnimation.dispatchOnStart();
return true;
}
@@ -203,7 +202,6 @@
targetState = (progress > SUCCESS_TRANSITION_PROGRESS) ? mToState : mFromState;
}
-
final float endProgress;
final float startProgress;
final long duration;
@@ -220,6 +218,8 @@
endProgress - Math.max(progress, 0));
}
} else {
+ mCurrentAnimation.setOnCancelRunnable(null);
+ mCurrentAnimation.dispatchOnCancel();
endProgress = 0;
if (progress <= 0) {
duration = 0;
@@ -236,6 +236,7 @@
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
anim.setFloatValues(startProgress, endProgress);
updateSwipeCompleteAnimation(anim, duration, targetState, velocity, fling);
+ mCurrentAnimation.dispatchOnStart();
anim.start();
}
@@ -275,13 +276,6 @@
protected void clearState() {
mCurrentAnimation = null;
mDetector.finishedScrolling();
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- if (mCurrentAnimation != null && animation == mCurrentAnimation.getTarget()) {
- Log.e(TAG, "Who dare cancel the animation when I am in control", new Exception());
- clearState();
- }
+ mDetector.setDetectableScrollConditions(0, false);
}
}
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index a508191..598738b 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -97,7 +97,7 @@
mActiveEdge = edge;
}
- private void setDampedScrollShift(float shift) {
+ protected void setDampedScrollShift(float shift) {
if (shift != mDampedScrollShift) {
mDampedScrollShift = shift;
invalidate();
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index bd1a96e..b8cd035 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -40,6 +40,8 @@
public static void onLauncherStateOrFocusChanged(Launcher launcher) { }
+ public static void onCreate(Launcher launcher) { }
+
public static void onStart(Launcher launcher) { }
public static void onLauncherStateOrResumeChanged(Launcher launcher) { }