Merge "Fixing task location calculation in seascape" into ub-launcher3-edmonton
diff --git a/protos/launcher_log.proto b/protos/launcher_log.proto
index 179f21d..cd404d6 100644
--- a/protos/launcher_log.proto
+++ b/protos/launcher_log.proto
@@ -55,6 +55,7 @@
optional int32 span_y = 14 [default = 1];// Used for ItemType.WIDGET
optional int32 predictedRank = 15;
optional TargetExtension extension = 16;
+ optional TipType tip_type = 17;
}
// Used to define what type of item a Target would represent.
@@ -88,6 +89,7 @@
NAVBAR = 11;
TASKSWITCHER = 12; // Recents UI Container (QuickStep)
APP = 13; // Foreground activity is another app (QuickStep)
+ TIP = 14; // Onboarding texts (QuickStep)
}
// Used to define what type of control a Target would represent.
@@ -109,14 +111,24 @@
CANCEL_TARGET = 14;
}
+enum TipType {
+ DEFAULT_NONE = 0;
+ BOUNCE = 1;
+ SWIPE_UP_TEXT = 2;
+ QUICK_SCRUB_TEXT = 3;
+ PREDICTION_TEXT = 4;
+}
+
// Used to define the action component of the LauncherEvent.
message Action {
enum Type {
TOUCH = 0;
AUTOMATED = 1;
COMMAND = 2;
+ TIP = 3;
// SOFT_KEYBOARD, HARD_KEYBOARD, ASSIST
}
+
enum Touch {
TAP = 0;
LONGPRESS = 1;
@@ -125,7 +137,8 @@
FLING = 4;
PINCH = 5;
}
- enum Direction {
+
+ enum Direction {
NONE = 0;
UP = 1;
DOWN = 2;
diff --git a/quickstep/libs/sysui_shared.jar b/quickstep/libs/sysui_shared.jar
index b343cc2..5e25fd8 100644
--- a/quickstep/libs/sysui_shared.jar
+++ b/quickstep/libs/sysui_shared.jar
Binary files differ
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index 29399142..e346310 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -118,6 +118,7 @@
finish();
} else if (mFinished) {
// Animation callback was already finished, skip the animation.
+ mAnimator.start();
mAnimator.end();
} else {
// Start the animation
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index cf62e2e..80ee577 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -21,7 +21,9 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
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.Utilities.postAsyncCallback;
+import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_7;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@@ -58,6 +60,7 @@
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.InsettableFrameLayout.LayoutParams;
+import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.dragndrop.DragLayer;
@@ -329,6 +332,30 @@
appsView.setTranslationY(startY);
appsView.setLayerType(View.LAYER_TYPE_NONE, null);
};
+ } else if (mLauncher.isInState(OVERVIEW)) {
+ AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
+ launcherAnimator.play(ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
+ allAppsController.getProgress(), ALL_APPS_PROGRESS_OFF_SCREEN));
+
+ View overview = mLauncher.getOverviewPanelContainer();
+ ObjectAnimator alpha = ObjectAnimator.ofFloat(overview, View.ALPHA, alphas);
+ alpha.setDuration(217);
+ alpha.setInterpolator(LINEAR);
+ launcherAnimator.play(alpha);
+
+ ObjectAnimator transY = ObjectAnimator.ofFloat(overview, View.TRANSLATION_Y, trans);
+ transY.setInterpolator(AGGRESSIVE_EASE);
+ transY.setDuration(350);
+ launcherAnimator.play(transY);
+
+ overview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ endListener = () -> {
+ overview.setLayerType(View.LAYER_TYPE_NONE, null);
+ overview.setAlpha(1f);
+ overview.setTranslationY(0f);
+ mLauncher.getStateManager().reapplyState();
+ };
} else {
mDragLayerAlpha.setValue(alphas[0]);
ObjectAnimator alpha =
diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
index 0451653..a11625a 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java
@@ -28,7 +28,7 @@
| FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY;
public FastOverviewState(int id) {
- super(id, QuickScrubController.QUICK_SCRUB_START_DURATION, STATE_FLAGS);
+ super(id, QuickScrubController.QUICK_SCRUB_FROM_HOME_START_DURATION, STATE_FLAGS);
}
@Override
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index febe360..79e3c42 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -17,6 +17,8 @@
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
import static com.android.quickstep.views.LauncherRecentsView.TRANSLATION_Y_FACTOR;
import static com.android.quickstep.views.RecentsView.ADJACENT_SCALE;
import static com.android.quickstep.views.RecentsViewContainer.CONTENT_ALPHA;
@@ -24,12 +26,14 @@
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.os.Build;
+import android.view.animation.Interpolator;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PropertySetter;
import com.android.quickstep.views.LauncherRecentsView;
import com.android.quickstep.views.RecentsViewContainer;
@@ -69,7 +73,13 @@
PropertySetter setter = config.getPropertySetter(builder);
float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher);
setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0], LINEAR);
- setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1], LINEAR);
+ Interpolator transYInterpolator = LINEAR;
+ if (toState == LauncherState.FAST_OVERVIEW) {
+ transYInterpolator = Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0,
+ QUICK_SCRUB_TRANSLATION_Y_FACTOR);
+ }
+ setter.setFloat(mRecentsView, TRANSLATION_Y_FACTOR, scaleTranslationYFactor[1],
+ transYInterpolator);
setter.setFloat(mRecentsViewContainer, CONTENT_ALPHA, toState.overviewUi ? 1 : 0,
AGGRESSIVE_EASE_IN_OUT);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
index 06099b9..e6030d1 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java
@@ -24,7 +24,9 @@
import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
+import android.app.Activity;
import android.content.Context;
+import android.util.Base64;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
@@ -32,13 +34,19 @@
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateHandler;
+import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
+import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
+import java.io.ByteArrayOutputStream;
+import java.io.PrintWriter;
+import java.util.zip.Deflater;
+
public class UiFactory {
public static TouchController[] createTouchControllers(Launcher launcher) {
@@ -167,6 +175,32 @@
}
}
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ if (!Utilities.IS_DEBUG_DEVICE) {
+ return false;
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ if (!(new ActivityCompat(activity).encodeViewHierarchy(out))) {
+ return false;
+ }
+
+ Deflater deflater = new Deflater();
+ deflater.setInput(out.toByteArray());
+ deflater.finish();
+
+ out.reset();
+ byte[] buffer = new byte[1024];
+ while (!deflater.finished()) {
+ int count = deflater.deflate(buffer); // returns the generated code... index
+ out.write(buffer, 0, count);
+ }
+
+ writer.println("--encoded-view-dump-v0--");
+ writer.println(Base64.encodeToString(
+ out.toByteArray(), Base64.NO_WRAP | Base64.NO_PADDING));
+ return true;
+ }
+
private static class LauncherTaskViewController extends TaskViewTouchController<Launcher> {
public LauncherTaskViewController(Launcher activity) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 2028501..81a73fc 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -48,8 +48,10 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.logging.UserEventDispatcher;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.AnimationFactory;
import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
@@ -189,6 +191,17 @@
mMainThreadExecutor.execute(new ShowRecentsCommand());
}
+ public void onTip(int actionType, int viewType) {
+ mMainThreadExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ UserEventDispatcher.newInstance(mContext,
+ new InvariantDeviceProfile(mContext).getDeviceProfile(mContext))
+ .logActionTip(actionType, viewType);
+ }
+ });
+ }
+
public ActivityControlHelper getActivityControlHelper() {
return mActivityControlHelper;
}
@@ -275,8 +288,10 @@
}
private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
- LatencyTrackerCompat.logToggleRecents(
- (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+ if (LatencyTrackerCompat.isEnabled(mContext)) {
+ LatencyTrackerCompat.logToggleRecents(
+ (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+ }
if (mListener != null) {
mListener.unregister();
diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java
index ae7de87..5ddd904 100644
--- a/quickstep/src/com/android/quickstep/QuickScrubController.java
+++ b/quickstep/src/com/android/quickstep/QuickScrubController.java
@@ -16,16 +16,18 @@
package com.android.quickstep;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+
import android.util.Log;
import android.view.HapticFeedbackConstants;
+import android.view.animation.Interpolator;
import com.android.launcher3.Alarm;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.Utilities;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
-import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -37,7 +39,11 @@
*/
public class QuickScrubController implements OnAlarmListener {
- public static final int QUICK_SCRUB_START_DURATION = 210;
+ public static final int QUICK_SCRUB_FROM_APP_START_DURATION = 240;
+ public static final int QUICK_SCRUB_FROM_HOME_START_DURATION = 150;
+ // We want the translation y to finish faster than the rest of the animation.
+ public static final float QUICK_SCRUB_TRANSLATION_Y_FACTOR = 5f / 6;
+ public static final Interpolator QUICK_SCRUB_START_INTERPOLATOR = FAST_OUT_SLOW_IN;
/**
* Snap to a new page when crossing these thresholds. The first and last auto-advance.
@@ -117,9 +123,9 @@
mOnFinishedTransitionToQuickScrubRunnable = launchTaskRunnable;
}
}
- mActivity.getUserEventDispatcher().logActionOnControl(Touch.DRAGDROP,
- ControlType.QUICK_SCRUB_BUTTON, null, mStartedFromHome ?
- ContainerType.WORKSPACE : ContainerType.APP);
+ mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(Touch.DRAGDROP,
+ LauncherLogProto.Action.Direction.NONE, page,
+ TaskUtils.getComponentKeyForTask(mRecentsView.getPageAt(page).getTask().key));
}
/**
@@ -168,23 +174,27 @@
public void snapToNextTaskIfAvailable() {
if (mInQuickScrub && mRecentsView.getChildCount() > 0) {
+ int duration = mStartedFromHome ? QUICK_SCRUB_FROM_HOME_START_DURATION
+ : QUICK_SCRUB_FROM_APP_START_DURATION;
int pageToGoTo = mStartedFromHome ? 0 : mRecentsView.getNextPage() + 1;
- goToPageWithHaptic(pageToGoTo, QUICK_SCRUB_START_DURATION, true /* forceHaptic */);
+ goToPageWithHaptic(pageToGoTo, duration, true /* forceHaptic */,
+ QUICK_SCRUB_START_INTERPOLATOR);
}
}
private void goToPageWithHaptic(int pageToGoTo) {
- goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */);
+ goToPageWithHaptic(pageToGoTo, -1 /* overrideDuration */, false /* forceHaptic */, null);
}
- private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic) {
+ private void goToPageWithHaptic(int pageToGoTo, int overrideDuration, boolean forceHaptic,
+ Interpolator interpolator) {
pageToGoTo = Utilities.boundToRange(pageToGoTo, 0, mRecentsView.getPageCount() - 1);
boolean snappingToPage = pageToGoTo != mRecentsView.getNextPage();
if (snappingToPage) {
int duration = overrideDuration > -1 ? overrideDuration
: Math.abs(pageToGoTo - mRecentsView.getNextPage())
* QUICKSCRUB_SNAP_DURATION_PER_PAGE;
- mRecentsView.snapToPage(pageToGoTo, duration);
+ mRecentsView.snapToPage(pageToGoTo, duration, interpolator);
}
if (snappingToPage || forceHaptic) {
mRecentsView.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 20ecde9..b472d61 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -59,6 +59,9 @@
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
/**
* A simple activity to show the recently launched tasks
*/
@@ -271,4 +274,11 @@
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
+
+ @Override
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ super.dump(prefix, fd, writer, args);
+ writer.println(prefix + "Misc:");
+ dumpMisc(writer);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index 7496b47..bd05b6d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -147,6 +147,11 @@
TraceHelper.endSection("SysUiBinder", "onQuickStep");
}
+
+ @Override
+ public void onTip(int actionType, int viewType) {
+ mOverviewCommandHelper.onTip(actionType, viewType);
+ }
};
private final TouchConsumer mNoOpTouchConsumer = (ev) -> {};
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index f29cc22..807dae8 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -17,11 +17,9 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.Utilities.postAsyncCallback;
-import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_FROM_APP_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
@@ -435,9 +433,8 @@
mLayoutListener.setHandler(this);
buildAnimationController();
- final long transitionDelay = mLauncherFrameDrawnTime - mTouchTimeMs;
if (LatencyTrackerCompat.isEnabled(mContext)) {
- LatencyTrackerCompat.logToggleRecents((int) transitionDelay);
+ LatencyTrackerCompat.logToggleRecents((int) (mLauncherFrameDrawnTime - mTouchTimeMs));
}
}
@@ -455,7 +452,7 @@
setStateOnUiThread(STATE_QUICK_SCRUB_START | STATE_GESTURE_COMPLETED);
// Start the window animation without waiting for launcher.
- animateToProgress(1f, QUICK_SCRUB_START_DURATION, TOUCH_RESPONSE_INTERPOLATOR);
+ animateToProgress(1f, QUICK_SCRUB_FROM_APP_START_DURATION, LINEAR);
}
@WorkerThread
@@ -502,10 +499,7 @@
RecentsAnimationControllerCompat controller = mRecentsAnimationWrapper.getController();
if (controller != null) {
- Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
- ? ACCEL_2 : LINEAR;
- float interpolated = interpolator.getInterpolation(shift);
- mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, interpolated);
+ mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targetSet, shift);
// TODO: This logic is spartanic!
boolean passedThreshold = shift > 0.12f;
@@ -793,7 +787,8 @@
}
mClipAnimationHelper.offsetTarget(
firstTask.getCurveScaleForInterpolation(interpolation), offsetFromFirstTask,
- mActivityControlHelper.getTranslationYForQuickScrub(mActivity));
+ mActivityControlHelper.getTranslationYForQuickScrub(mActivity),
+ QuickScrubController.QUICK_SCRUB_START_INTERPOLATOR);
}
}
diff --git a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
index d4cdd35..04153cc 100644
--- a/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
+++ b/quickstep/src/com/android/quickstep/logging/UserEventDispatcherExtension.java
@@ -15,15 +15,32 @@
*/
package com.android.quickstep.logging;
+import android.util.Log;
+
+import static com.android.launcher3.logging.LoggerUtils.newAction;
+import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CANCEL_TARGET;
+import static com.android.systemui.shared.system.LauncherEventUtil.VISIBLE;
+import static com.android.systemui.shared.system.LauncherEventUtil.DISMISS;
+import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_QUICK_SCRUB_ONBOARDING_TIP;
+import static com.android.systemui.shared.system.LauncherEventUtil.RECENTS_SWIPE_UP_ONBOARDING_TIP;
+
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.model.nano.LauncherDumpProto;
+import com.android.launcher3.userevent.nano.LauncherLogExtensions;
import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.systemui.shared.system.LauncherEventUtil;
import com.android.systemui.shared.system.MetricsLoggerCompat;
/**
- * This class handles AOSP MetricsLogger function calls.
+ * This class handles AOSP MetricsLogger function calls and logging around
+ * quickstep interactions.
*/
public class UserEventDispatcherExtension extends UserEventDispatcher {
+ private static final String TAG = "UserEventDispatcher";
+
public void logStateChangeAction(int action, int dir, int srcChildTargetType,
int srcParentContainerType, int dstContainerType,
int pageIndex) {
@@ -32,4 +49,37 @@
super.logStateChangeAction(action, dir, srcChildTargetType, srcParentContainerType,
dstContainerType, pageIndex);
}
+
+ public void logActionTip(int actionType, int viewType) {
+ LauncherLogProto.Action action = new LauncherLogProto.Action();
+ LauncherLogProto.Target target = new LauncherLogProto.Target();
+ switch(actionType) {
+ case VISIBLE:
+ action.type = LauncherLogProto.Action.Type.TIP;
+ target.type = LauncherLogProto.Target.Type.CONTAINER;
+ target.containerType = LauncherLogProto.ContainerType.TIP;
+ break;
+ case DISMISS:
+ action.type = LauncherLogProto.Action.Type.TOUCH;
+ action.touch = LauncherLogProto.Action.Touch.TAP;
+ target.type = LauncherLogProto.Target.Type.CONTROL;
+ target.controlType = CANCEL_TARGET;
+ break;
+ default:
+ Log.e(TAG, "Unexpected action type = " + actionType);
+ }
+
+ switch(viewType) {
+ case RECENTS_QUICK_SCRUB_ONBOARDING_TIP:
+ target.tipType = LauncherLogProto.TipType.QUICK_SCRUB_TEXT;
+ break;
+ case RECENTS_SWIPE_UP_ONBOARDING_TIP:
+ target.tipType = LauncherLogProto.TipType.SWIPE_UP_TEXT;
+ break;
+ default:
+ Log.e(TAG, "Unexpected viewType = " + viewType);
+ }
+ LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
+ dispatchUserEvent(event, null);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
index a25b77d..8c7f104 100644
--- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -16,7 +16,7 @@
package com.android.quickstep.util;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.SCROLL;
+import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_TRANSLATION_Y_FACTOR;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -30,11 +30,13 @@
import android.os.Build;
import android.os.RemoteException;
import android.support.annotation.Nullable;
+import android.view.animation.Interpolator;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
@@ -78,6 +80,9 @@
private final RectF mTmpRectF = new RectF();
private float mTargetScale = 1f;
+ private Interpolator mInterpolator = LINEAR;
+ // We translate y slightly faster than the rest of the animation for quick scrub.
+ private Interpolator mOffsetYInterpolator = LINEAR;
// Whether to boost the opening animation target layers, or the closing
private int mBoostModeTargetLayers = -1;
@@ -134,12 +139,13 @@
RectF currentRect;
mTmpRectF.set(mTargetRect);
Utilities.scaleRectFAboutCenter(mTmpRectF, mTargetScale);
+ float offsetYProgress = mOffsetYInterpolator.getInterpolation(progress);
+ progress = mInterpolator.getInterpolation(progress);
currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTmpRectF);
synchronized (mTargetOffset) {
// Stay lined up with the center of the target, since it moves for quick scrub.
- currentRect.offset(mTargetOffset.x * SCROLL.getInterpolation(progress),
- mTargetOffset.y * LINEAR.getInterpolation(progress));
+ currentRect.offset(mTargetOffset.x * progress, mTargetOffset.y * offsetYProgress);
}
mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
@@ -180,11 +186,14 @@
mTaskTransformCallback = callback;
}
- public void offsetTarget(float scale, float offsetX, float offsetY) {
+ public void offsetTarget(float scale, float offsetX, float offsetY, Interpolator interpolator) {
synchronized (mTargetOffset) {
- mTargetScale = scale;
mTargetOffset.set(offsetX, offsetY);
}
+ mTargetScale = scale;
+ mInterpolator = interpolator;
+ mOffsetYInterpolator = Interpolators.clampToProgress(mInterpolator, 0,
+ QUICK_SCRUB_TRANSLATION_Y_FACTOR);
}
public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv) {
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index a8e38a1..68432ab 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -21,8 +21,8 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -121,6 +121,8 @@
// Keeps track of the previously known visible tasks for purposes of loading/unloading task data
private final SparseBooleanArray mHasVisibleTaskData = new SparseBooleanArray();
+ private boolean mIsClearAllButtonFullyRevealed;
+
/**
* TODO: Call reloadIdNeeded in onTaskStackChanged.
*/
@@ -337,11 +339,15 @@
}
}
+ private int getScrollEnd() {
+ return mIsRtl ? 0 : mMaxScrollX;
+ }
+
private float calculateClearAllButtonAlpha() {
final int childCount = getChildCount();
if (mShowEmptyMessage || childCount == 0) return 0;
- final int scrollEnd = mIsRtl ? 0 : mMaxScrollX;
+ final int scrollEnd = getScrollEnd();
final int oldestChildScroll = getScrollForPage(childCount - 1);
return Utilities.boundToRange(
@@ -352,6 +358,7 @@
private void updateClearAllButtonAlpha() {
if (mClearAllButton != null) {
final float alpha = calculateClearAllButtonAlpha();
+ mIsClearAllButtonFullyRevealed = alpha == 1;
mClearAllButton.setAlpha(alpha * mContentAlpha);
}
}
@@ -363,9 +370,18 @@
}
@Override
+ protected void restoreScrollOnLayout() {
+ if (mIsClearAllButtonFullyRevealed) {
+ scrollAndForceFinish(getScrollEnd());
+ } else {
+ super.restoreScrollOnLayout();
+ }
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN && mTouchState == TOUCH_STATE_REST
- && mScroller.isFinished() && mClearAllButton.getAlpha() > 0) {
+ && mScroller.isFinished() && mIsClearAllButtonFullyRevealed) {
mClearAllButton.getHitRect(mTempRect);
mTempRect.offset(-getLeft(), -getTop());
if (mTempRect.contains((int) ev.getX(), (int) ev.getY())) {
@@ -402,6 +418,7 @@
final int requiredChildCount = tasks.size();
for (int i = getChildCount(); i < requiredChildCount; i++) {
final TaskView taskView = (TaskView) inflater.inflate(R.layout.task, this, false);
+ taskView.setOnClickListener(this::onTaskClicked);
addView(taskView);
}
while (getChildCount() > requiredChildCount) {
@@ -427,6 +444,17 @@
onTaskStackUpdated();
}
+ private void onTaskClicked(View v) {
+ TaskView taskView = (TaskView) v;
+ if (taskView.getTask() == null) {
+ return;
+ }
+ taskView.launchTask(true /* animate */);
+ mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
+ Touch.TAP, Direction.NONE, indexOfChild(taskView),
+ TaskUtils.getComponentKeyForTask(taskView.getTask().key));
+ }
+
protected void onTaskStackUpdated() { }
public void resetTaskVisuals() {
@@ -729,13 +757,13 @@
duration, LINEAR, anim);
}
- private void removeTask(Task task, PendingAnimation.OnEndListener onEndListener,
- boolean shouldLog) {
+ private void removeTask(Task task, int index, PendingAnimation.OnEndListener onEndListener,
+ boolean shouldLog) {
if (task != null) {
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
if (shouldLog) {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- onEndListener.logAction, Direction.UP,
+ onEndListener.logAction, Direction.UP, index,
TaskUtils.getComponentKeyForTask(task.key));
}
}
@@ -820,7 +848,7 @@
mPendingAnimation.addEndListener((onEndListener) -> {
if (onEndListener.isSuccess) {
if (shouldRemoveTask) {
- removeTask(taskView.getTask(), onEndListener, true);
+ removeTask(taskView.getTask(), draggedIndex, onEndListener, true);
}
int pageToSnapTo = mCurrentPage;
if (draggedIndex < pageToSnapTo) {
@@ -829,7 +857,7 @@
removeView(taskView);
if (getChildCount() == 0) {
onAllTasksRemoved();
- } else {
+ } else if (!mIsClearAllButtonFullyRevealed) {
snapToPageImmediately(pageToSnapTo);
}
}
@@ -856,7 +884,7 @@
if (onEndListener.isSuccess) {
while (getChildCount() != 0) {
TaskView taskView = getPageAt(getChildCount() - 1);
- removeTask(taskView.getTask(), onEndListener, false);
+ removeTask(taskView.getTask(), -1, onEndListener, false);
removeView(taskView);
}
onAllTasksRemoved();
@@ -1038,14 +1066,7 @@
}
updateClearAllButtonAlpha();
- if (!mShowEmptyMessage) return;
-
- // The icon needs to be centered. Need to scoll to horizontal 0 because with Clear-All
- // space on the right, it's not guaranteed that after deleting all tasks, the horizontal
- // scroll position will be zero.
- scrollTo(0, 0);
-
- if (hasValidSize && mEmptyTextLayout == null) {
+ if (mShowEmptyMessage && hasValidSize && mEmptyTextLayout == null) {
int availableWidth = mLastMeasureSize.x - mEmptyMessagePadding - mEmptyMessagePadding;
mEmptyTextLayout = StaticLayout.Builder.obtain(mEmptyMessage, 0, mEmptyMessage.length(),
mEmptyMessagePaint, availableWidth)
@@ -1072,7 +1093,7 @@
mTempRect.set(mInsets.left + getPaddingLeft(), mInsets.top + getPaddingTop(),
mInsets.right + getPaddingRight(), mInsets.bottom + getPaddingBottom());
canvas.save();
- canvas.translate((mTempRect.left - mTempRect.right) / 2,
+ canvas.translate(getScrollX() + (mTempRect.left - mTempRect.right) / 2,
(mTempRect.top - mTempRect.bottom) / 2);
mEmptyIcon.draw(canvas);
canvas.translate(mEmptyMessagePadding,
@@ -1195,7 +1216,7 @@
Task task = tv.getTask();
if (task != null) {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
- onEndListener.logAction, Direction.DOWN,
+ onEndListener.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getComponentKeyForTask(task.key));
}
} else {
@@ -1219,6 +1240,7 @@
if (currChild != null) {
currChild.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
}
+ loadVisibleTaskData();
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
index b47af2d..24afd48 100644
--- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
+++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java
@@ -28,6 +28,9 @@
import android.graphics.Path;
import android.graphics.Path.Direction;
import android.graphics.Path.Op;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.util.AttributeSet;
import com.android.launcher3.DeviceProfile;
@@ -47,6 +50,8 @@
private static final int THRESHOLD_ALPHA_DARK = 102;
private static final int THRESHOLD_ALPHA_LIGHT = 46;
+ private static final int THRESHOLD_ALPHA_SUPER_LIGHT = 128;
+ private static final int CLEAR_ALL_TASKS = R.string.recents_clear_all;
// In transposed layout, we simply draw a flat color.
private boolean mDrawingFlatColor;
@@ -76,8 +81,13 @@
mMaxScrimAlpha = OVERVIEW.getWorkspaceScrimAlpha(mLauncher);
mEndAlpha = Color.alpha(mEndScrim);
- mThresholdAlpha = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)
- ? THRESHOLD_ALPHA_DARK : THRESHOLD_ALPHA_LIGHT;
+ if (Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark)) {
+ mThresholdAlpha = THRESHOLD_ALPHA_DARK;
+ } else if (Themes.getAttrBoolean(mLauncher, R.attr.isWorkspaceDarkText)) {
+ mThresholdAlpha = THRESHOLD_ALPHA_SUPER_LIGHT;
+ } else {
+ mThresholdAlpha = THRESHOLD_ALPHA_LIGHT;
+ }
mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -207,4 +217,43 @@
mRadius, mRadius, mPaint);
return minTop - mDragHandleSize - top;
}
+
+ @NonNull
+ @Override
+ protected AccessibilityHelper createAccessibilityHelper() {
+ return new ShelfScrimAccessibilityHelper();
+ }
+
+ protected class ShelfScrimAccessibilityHelper extends AccessibilityHelper {
+ @Override
+ protected void onPopulateNodeForVirtualView(int virtualViewId,
+ AccessibilityNodeInfoCompat node) {
+ super.onPopulateNodeForVirtualView(virtualViewId, node);
+
+ if (mLauncher.isInState(OVERVIEW)) {
+ final RecentsView overviewPanel = mLauncher.getOverviewPanel();
+ if (overviewPanel.getChildCount() != 0) {
+ node.addAction(
+ new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
+ CLEAR_ALL_TASKS,
+ getContext().getText(CLEAR_ALL_TASKS)));
+ }
+ }
+ }
+
+ @Override
+ protected boolean onPerformActionForVirtualView(
+ int virtualViewId, int action, Bundle arguments) {
+ if (super.onPerformActionForVirtualView(virtualViewId, action, arguments)) return true;
+
+ if (action == CLEAR_ALL_TASKS) {
+ if (mLauncher.isInState(OVERVIEW)) {
+ mLauncher.<RecentsView>getOverviewPanel().dismissAllTasks();
+ }
+ return true;
+ }
+
+ return false;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
index af7a735..128a19e 100644
--- a/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskThumbnailView.java
@@ -16,7 +16,6 @@
package com.android.quickstep.views;
-import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
import android.content.Context;
@@ -52,16 +51,16 @@
private static final LightingColorFilter[] sDimFilterCache = new LightingColorFilter[256];
- public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
- new FloatProperty<TaskThumbnailView>("dimAlpha") {
+ public static final Property<TaskThumbnailView, Float> DIM_ALPHA_MULTIPLIER =
+ new FloatProperty<TaskThumbnailView>("dimAlphaMultiplier") {
@Override
- public void setValue(TaskThumbnailView thumbnail, float dimAlpha) {
- thumbnail.setDimAlpha(dimAlpha);
+ public void setValue(TaskThumbnailView thumbnail, float dimAlphaMultiplier) {
+ thumbnail.setDimAlphaMultipler(dimAlphaMultiplier);
}
@Override
public Float get(TaskThumbnailView thumbnailView) {
- return thumbnailView.mDimAlpha;
+ return thumbnailView.mDimAlphaMultiplier;
}
};
@@ -81,6 +80,7 @@
protected BitmapShader mBitmapShader;
private float mDimAlpha = 1f;
+ private float mDimAlphaMultiplier = 1f;
public TaskThumbnailView(Context context) {
this(context, null);
@@ -128,6 +128,11 @@
updateThumbnailPaintFilter();
}
+ public void setDimAlphaMultipler(float dimAlphaMultipler) {
+ mDimAlphaMultiplier = dimAlphaMultipler;
+ setDimAlpha(mDimAlpha);
+ }
+
/**
* Sets the alpha of the dim layer on top of this view.
*
@@ -191,7 +196,7 @@
}
private void updateThumbnailPaintFilter() {
- int mul = (int) ((1 - mDimAlpha) * 255);
+ int mul = (int) ((1 - mDimAlpha * mDimAlphaMultiplier) * 255);
if (mBitmapShader != null) {
LightingColorFilter filter = getLightingColorFilter(mul);
mPaint.setColorFilter(filter);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index 4f447b1..0ddeae7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -17,7 +17,7 @@
package com.android.quickstep.views;
import static android.widget.Toast.LENGTH_SHORT;
-import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA;
+import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA_MULTIPLIER;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -78,6 +78,7 @@
private static final float EDGE_SCALE_DOWN_FACTOR = 0.03f;
public static final long SCALE_ICON_DURATION = 120;
+ private static final long DIM_ANIM_DURATION = 700;
public static final Property<TaskView, Float> ZOOM_SCALE =
new FloatProperty<TaskView>("zoomScale") {
@@ -97,7 +98,6 @@
private IconView mIconView;
private float mCurveScale;
private float mZoomScale;
- private float mCurveDimAlpha;
private Animator mDimAlphaAnim;
public TaskView(Context context) {
@@ -110,13 +110,6 @@
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- setOnClickListener((view) -> {
- if (mTask != null) {
- launchTask(true /* animate */);
- BaseActivity.fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss(
- Touch.TAP, Direction.NONE, TaskUtils.getComponentKeyForTask(mTask.key));
- }
- });
setOutlineProvider(new TaskOutlineProvider(getResources()));
}
@@ -200,8 +193,9 @@
public void animateIconToScaleAndDim(float scale) {
mIconView.animate().scaleX(scale).scaleY(scale).setDuration(SCALE_ICON_DURATION).start();
- mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA, scale * mCurveDimAlpha);
- mDimAlphaAnim.setDuration(SCALE_ICON_DURATION);
+ mDimAlphaAnim = ObjectAnimator.ofFloat(mSnapshotView, DIM_ALPHA_MULTIPLIER, 1 - scale,
+ scale);
+ mDimAlphaAnim.setDuration(DIM_ANIM_DURATION);
mDimAlphaAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -218,7 +212,7 @@
if (mDimAlphaAnim != null) {
mDimAlphaAnim.cancel();
}
- mSnapshotView.setDimAlpha(iconScale * mCurveDimAlpha);
+ mSnapshotView.setDimAlphaMultipler(iconScale);
}
public void resetVisualProperties() {
@@ -235,11 +229,7 @@
float curveInterpolation =
CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
- mCurveDimAlpha = curveInterpolation * MAX_PAGE_SCRIM_ALPHA;
- if (mDimAlphaAnim == null && mIconView.getScaleX() > 0) {
- mSnapshotView.setDimAlpha(mCurveDimAlpha);
- }
-
+ mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
setCurveScale(getCurveScaleForCurveInterpolation(curveInterpolation));
}
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 8526d7c..631626f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -60,7 +60,7 @@
<item name="android:textColorTertiary">#CCFFFFFF</item>
<item name="android:textColorHint">#A0FFFFFF</item>
<item name="android:colorControlHighlight">#A0FFFFFF</item>
- <item name="android:colorPrimary">#FF333333</item>
+ <item name="android:colorPrimary">#FF212121</item>
<item name="allAppsScrimColor">#EA212121</item>
<item name="allAppsNavBarScrimColor">#80000000</item>
<item name="popupColorPrimary">?android:attr/colorPrimary</item>
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 1f70cfa..4219667 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.app.Activity;
@@ -24,15 +25,16 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.Configuration;
-import android.graphics.Point;
import android.support.annotation.IntDef;
-import android.view.Display;
import android.view.View.AccessibilityDelegate;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.uioverrides.UiFactory;
import com.android.launcher3.util.SystemUiController;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -223,4 +225,19 @@
public interface MultiWindowModeChangedListener {
void onMultiWindowModeChanged(boolean isInMultiWindowMode);
}
+
+ @Override
+ public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ if (!UiFactory.dumpActivity(this, writer)) {
+ super.dump(prefix, fd, writer, args);
+ }
+ }
+
+ protected void dumpMisc(PrintWriter writer) {
+ writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
+ writer.println(" orientation=" + getResources().getConfiguration().orientation);
+ writer.println(" mSystemUiController: " + mSystemUiController);
+ writer.println(" mActivityFlags: " + mActivityFlags);
+ writer.println(" mForceInvisible: " + mForceInvisible);
+ }
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 168bd08..8d79737 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -107,6 +107,8 @@
private final BitmapFactory.Options mLowResOptions;
private final BitmapFactory.Options mHighResOptions;
+ private int mPendingIconRequestCount = 0;
+
public IconCache(Context context, InvariantDeviceProfile inv) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -411,8 +413,13 @@
*/
public IconLoadRequest updateIconInBackground(final ItemInfoUpdateReceiver caller,
final ItemInfoWithIcon info) {
- Runnable request = new Runnable() {
+ Preconditions.assertUIThread();
+ if (mPendingIconRequestCount <= 0) {
+ LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_FOREGROUND);
+ }
+ mPendingIconRequestCount ++;
+ IconLoadRequest request = new IconLoadRequest(mWorkerHandler, this::onIconRequestEnd) {
@Override
public void run() {
if (info instanceof AppInfo || info instanceof ShortcutInfo) {
@@ -420,17 +427,21 @@
} else if (info instanceof PackageItemInfo) {
getTitleAndIconForApp((PackageItemInfo) info, false);
}
- mMainThreadExecutor.execute(new Runnable() {
-
- @Override
- public void run() {
- caller.reapplyItemInfo(info);
- }
+ mMainThreadExecutor.execute(() -> {
+ caller.reapplyItemInfo(info);
+ onEnd();
});
}
};
- mWorkerHandler.post(request);
- return new IconLoadRequest(request, mWorkerHandler);
+ Utilities.postAsyncCallback(mWorkerHandler, request);
+ return request;
+ }
+
+ private void onIconRequestEnd() {
+ mPendingIconRequestCount --;
+ if (mPendingIconRequestCount <= 0) {
+ LauncherModel.setWorkerPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ }
}
/**
@@ -707,17 +718,27 @@
return false;
}
- public static class IconLoadRequest {
- private final Runnable mRunnable;
+ public static abstract class IconLoadRequest implements Runnable {
private final Handler mHandler;
+ private final Runnable mEndRunnable;
- IconLoadRequest(Runnable runnable, Handler handler) {
- mRunnable = runnable;
+ private boolean mEnded = false;
+
+ IconLoadRequest(Handler handler, Runnable endRunnable) {
mHandler = handler;
+ mEndRunnable = endRunnable;
}
public void cancel() {
- mHandler.removeCallbacks(mRunnable);
+ mHandler.removeCallbacks(this);
+ onEnd();
+ }
+
+ public void onEnd() {
+ if (!mEnded) {
+ mEnded = true;
+ mEndRunnable.run();
+ }
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 12d29a8..e851499 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -240,7 +240,6 @@
private PendingRequestArgs mPendingRequestArgs;
public ViewGroupFocusHelper mFocusHandler;
- private boolean mAppLaunchSuccess;
private RotationHelper mRotationHelper;
@@ -730,10 +729,8 @@
}
mAppWidgetHost.setListenIfResumed(false);
- if (!mAppLaunchSuccess) {
- getUserEventDispatcher().logActionCommand(Action.Command.STOP,
- mStateManager.getState().containerType, -1);
- }
+ getUserEventDispatcher().logActionCommand(Action.Command.STOP,
+ mStateManager.getState().containerType, -1);
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
@@ -760,7 +757,6 @@
super.onResume();
TraceHelper.partitionSection("ON_RESUME", "superCall");
- mAppLaunchSuccess = false;
getUserEventDispatcher().resetElapsedSessionMillis();
setOnResumeCallback(null);
// Process any items that were added while Launcher was away.
@@ -1634,8 +1630,8 @@
}
public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
- mAppLaunchSuccess = super.startActivitySafely(v, intent, item);
- if (mAppLaunchSuccess && v instanceof BubbleTextView) {
+ boolean success = super.startActivitySafely(v, intent, item);
+ if (success && v instanceof BubbleTextView) {
// This is set to the view that launched the activity that navigated the user away
// from launcher. Since there is no callback for when the activity has finished
// launching, enable the press state and keep this reference to reset the press
@@ -1644,7 +1640,7 @@
btv.setStayPressed(true);
setOnResumeCallback(btv);
}
- return mAppLaunchSuccess;
+ return success;
}
boolean isHotseatLayout(View layout) {
@@ -2279,8 +2275,8 @@
writer.print(prefix + "\tmWorkspaceLoading=" + mWorkspaceLoading);
writer.print(" mPendingRequestArgs=" + mPendingRequestArgs);
writer.println(" mPendingActivityResult=" + mPendingActivityResult);
- writer.println(" deviceProfile isTransposed=" + getDeviceProfile().isVerticalBarLayout());
- writer.println(" orientation=" + getResources().getConfiguration().orientation);
+ writer.println(" mRotationHelper: " + mRotationHelper);
+ dumpMisc(writer);
try {
FileLog.flushAll(writer);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 87ee076..eb816c5 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -27,6 +27,7 @@
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Bundle;
+import android.provider.Settings;
import android.util.AttributeSet;
import android.util.Log;
import android.view.InputDevice;
@@ -44,6 +45,7 @@
import android.widget.ScrollView;
import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.pageindicators.PageIndicator;
import com.android.launcher3.touch.OverScroll;
import com.android.launcher3.util.Thunk;
@@ -92,7 +94,6 @@
@ViewDebug.ExportedProperty(category = "launcher")
protected int mCurrentPage;
- private int mChildCountOnLastLayout;
@ViewDebug.ExportedProperty(category = "launcher")
protected int mNextPage = INVALID_PAGE;
@@ -239,6 +240,12 @@
return index;
}
+ protected void scrollAndForceFinish(int scrollX) {
+ scrollTo(scrollX, 0);
+ mScroller.setFinalX(scrollX);
+ forceFinishScroller(true);
+ }
+
/**
* Updates the scroll of the current page immediately to its final scroll position. We use this
* in CustomizePagedView to allow tabs to share the same PagedView while resetting the scroll of
@@ -250,9 +257,7 @@
if (0 <= mCurrentPage && mCurrentPage < getPageCount()) {
newX = getScrollForPage(mCurrentPage);
}
- scrollTo(newX, 0);
- mScroller.setFinalX(newX);
- forceFinishScroller(true);
+ scrollAndForceFinish(newX);
}
private void abortScrollerAnimation(boolean resetNextPage) {
@@ -539,22 +544,27 @@
setMeasuredDimension(widthSize, heightSize);
}
+ protected void restoreScrollOnLayout() {
+ setCurrentPage(getNextPage());
+ }
+
@SuppressLint("DrawAllocation")
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mIsLayoutValid = true;
- if (getChildCount() == 0) {
+ final int childCount = getChildCount();
+ boolean pageScrollChanged = false;
+ if (mPageScrolls == null || childCount != mPageScrolls.length) {
+ mPageScrolls = new int[childCount];
+ pageScrollChanged = true;
+ }
+
+ if (childCount == 0) {
return;
}
if (DEBUG) Log.d(TAG, "PagedView.onLayout()");
- final int childCount = getChildCount();
- boolean pageScrollChanged = false;
- if (mPageScrolls == null || childCount != mChildCountOnLastLayout) {
- mPageScrolls = new int[childCount];
- pageScrollChanged = true;
- }
if (getPageScrolls(mPageScrolls, true, SIMPLE_SCROLL_LOGIC)) {
pageScrollChanged = true;
}
@@ -589,9 +599,8 @@
}
if (mScroller.isFinished() && pageScrollChanged) {
- setCurrentPage(getNextPage());
+ restoreScrollOnLayout();
}
- mChildCountOnLastLayout = childCount;
}
/**
@@ -1422,7 +1431,7 @@
return snapToPage(whichPage, duration, false, null);
}
- protected boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
+ public boolean snapToPage(int whichPage, int duration, TimeInterpolator interpolator) {
return snapToPage(whichPage, duration, false, interpolator);
}
@@ -1441,6 +1450,12 @@
protected boolean snapToPage(int whichPage, int delta, int duration, boolean immediate,
TimeInterpolator interpolator) {
+
+ if (FeatureFlags.IS_DOGFOOD_BUILD) {
+ duration *= Settings.System.getFloat(getContext().getContentResolver(),
+ Settings.System.WINDOW_ANIMATION_SCALE, 1);
+ }
+
whichPage = validateNewPage(whichPage);
mNextPage = whichPage;
diff --git a/src/com/android/launcher3/allapps/DiscoveryBounce.java b/src/com/android/launcher3/allapps/DiscoveryBounce.java
index e8c5f15..deaf8d3 100644
--- a/src/com/android/launcher3/allapps/DiscoveryBounce.java
+++ b/src/com/android/launcher3/allapps/DiscoveryBounce.java
@@ -18,6 +18,8 @@
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.HOTSEAT;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType.PREDICTION;
import android.animation.Animator;
import android.animation.AnimatorInflater;
@@ -127,6 +129,7 @@
AnimatorInflater.loadAnimator(launcher, R.animator.discovery_bounce));
view.mIsOpen = true;
launcher.getDragLayer().addView(view);
+ launcher.getUserEventDispatcher().logActionBounceTip(HOTSEAT);
}
public static void showForOverviewIfNeeded(Launcher launcher) {
@@ -173,5 +176,6 @@
DiscoveryBounce view = new DiscoveryBounce(launcher, animator);
view.mIsOpen = true;
launcher.getDragLayer().addView(view);
+ launcher.getUserEventDispatcher().logActionBounceTip(PREDICTION);
}
}
diff --git a/src/com/android/launcher3/anim/Interpolators.java b/src/com/android/launcher3/anim/Interpolators.java
index 0d388fe..8374f98 100644
--- a/src/com/android/launcher3/anim/Interpolators.java
+++ b/src/com/android/launcher3/anim/Interpolators.java
@@ -115,4 +115,24 @@
public static Interpolator scrollInterpolatorForVelocity(float velocity) {
return Math.abs(velocity) > FAST_FLING_PX_MS ? SCROLL : SCROLL_CUBIC;
}
+
+ /**
+ * Runs the given interpolator such that the entire progress is set between the given bounds.
+ * That is, we set the interpolation to 0 until lowerBound and reach 1 by upperBound.
+ */
+ public static Interpolator clampToProgress(Interpolator interpolator, float lowerBound,
+ float upperBound) {
+ if (upperBound <= lowerBound) {
+ throw new IllegalArgumentException("lowerBound must be less than upperBound");
+ }
+ return t -> {
+ if (t < lowerBound) {
+ return 0;
+ }
+ if (t > upperBound) {
+ return 1;
+ }
+ return interpolator.getInterpolation((t - lowerBound) / (upperBound - lowerBound));
+ };
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java
index 3a1837d..7fef904 100644
--- a/src/com/android/launcher3/dragndrop/DragLayer.java
+++ b/src/com/android/launcher3/dragndrop/DragLayer.java
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2008 The Android Open Source Project
*
@@ -479,6 +480,7 @@
case ANIMATION_END_REMAIN_VISIBLE:
break;
}
+ mDropAnim = null;
}
});
mDropAnim.start();
@@ -488,6 +490,7 @@
if (mDropAnim != null) {
mDropAnim.cancel();
}
+ mDropAnim = null;
if (mDropView != null) {
mDragController.onDeferredEndDrag(mDropView);
}
diff --git a/src/com/android/launcher3/logging/LoggerUtils.java b/src/com/android/launcher3/logging/LoggerUtils.java
index 9d97cb9..83593aa 100644
--- a/src/com/android/launcher3/logging/LoggerUtils.java
+++ b/src/com/android/launcher3/logging/LoggerUtils.java
@@ -31,6 +31,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.nano.LauncherLogProto.LauncherEvent;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
+import com.android.launcher3.userevent.nano.LauncherLogProto.TipType;
import com.android.launcher3.util.InstantAppResolver;
import java.lang.reflect.Field;
@@ -76,7 +77,7 @@
}
return str;
case Action.Type.COMMAND: return getFieldName(action.command, Action.Command.class);
- default: return UNKNOWN;
+ default: return getFieldName(action.type, Action.Type.class);
}
}
@@ -84,23 +85,32 @@
if (t == null){
return "";
}
+ String str = "";
switch (t.type) {
case Target.Type.ITEM:
- return getItemStr(t);
+ str = getItemStr(t);
+ break;
case Target.Type.CONTROL:
- return getFieldName(t.controlType, ControlType.class);
+ str = getFieldName(t.controlType, ControlType.class);
+ break;
case Target.Type.CONTAINER:
- String str = getFieldName(t.containerType, ContainerType.class);
+ str = getFieldName(t.containerType, ContainerType.class);
if (t.containerType == ContainerType.WORKSPACE ||
t.containerType == ContainerType.HOTSEAT) {
str += " id=" + t.pageIndex;
} else if (t.containerType == ContainerType.FOLDER) {
str += " grid(" + t.gridX + "," + t.gridY+ ")";
}
- return str;
+ break;
default:
- return "UNKNOWN TARGET TYPE";
+ str += "UNKNOWN TARGET TYPE";
}
+
+ if (t.tipType != TipType.DEFAULT_NONE) {
+ str += " " + getFieldName(t.tipType, TipType.class);
+ }
+
+ return str;
}
private static String getItemStr(Target t) {
@@ -121,6 +131,9 @@
+ "), pageIdx=" + t.pageIndex;
}
+ if (t.itemType == ItemType.TASK) {
+ typeStr += ", pageIdx=" + t.pageIndex;
+ }
return typeStr;
}
@@ -186,6 +199,12 @@
return t;
}
+ public static Target newControlTarget(int controlType) {
+ Target t = newTarget(Target.Type.CONTROL);
+ t.controlType = controlType;
+ return t;
+ }
+
public static Target newContainerTarget(int containerType) {
Target t = newTarget(Target.Type.CONTAINER);
t.containerType = containerType;
diff --git a/src/com/android/launcher3/logging/UserEventDispatcher.java b/src/com/android/launcher3/logging/UserEventDispatcher.java
index 2c1eb32..850c948 100644
--- a/src/com/android/launcher3/logging/UserEventDispatcher.java
+++ b/src/com/android/launcher3/logging/UserEventDispatcher.java
@@ -16,8 +16,10 @@
package com.android.launcher3.logging;
+import static com.android.launcher3.logging.LoggerUtils.newAction;
import static com.android.launcher3.logging.LoggerUtils.newCommandAction;
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
+import static com.android.launcher3.logging.LoggerUtils.newControlTarget;
import static com.android.launcher3.logging.LoggerUtils.newDropTarget;
import static com.android.launcher3.logging.LoggerUtils.newItemTarget;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
@@ -130,6 +132,7 @@
private boolean mIsInLandscapeMode;
private String mUuidStr;
protected InstantAppResolver mInstantAppResolver;
+ private boolean mAppOrTaskLaunch;
// APP_ICON SHORTCUT WIDGET
// --------------------------------------------------------------
@@ -161,9 +164,13 @@
fillIntentInfo(event.srcTarget[0], intent);
}
dispatchUserEvent(event, intent);
+ mAppOrTaskLaunch = true;
}
- public void logTaskLaunchOrDismiss(int action, int direction, ComponentKey componentKey) {
+ public void logActionTip(int actionType, int viewType) { }
+
+ public void logTaskLaunchOrDismiss(int action, int direction, int taskIndex,
+ ComponentKey componentKey) {
LauncherEvent event = newLauncherEvent(newTouchAction(action), // TAP or SWIPE or FLING
newTarget(Target.Type.ITEM));
if (action == Action.Touch.SWIPE || action == Action.Touch.FLING) {
@@ -171,8 +178,10 @@
event.action.dir = direction;
}
event.srcTarget[0].itemType = LauncherLogProto.ItemType.TASK;
+ event.srcTarget[0].pageIndex = taskIndex;
fillComponentInfo(event.srcTarget[0], componentKey.componentName);
dispatchUserEvent(event, null);
+ mAppOrTaskLaunch = true;
}
protected void fillIntentInfo(Target target, Intent intent) {
@@ -207,6 +216,11 @@
public void logActionCommand(int command, Target srcTarget, Target dstTarget) {
LauncherEvent event = newLauncherEvent(newCommandAction(command), srcTarget);
+ if (command == Action.Command.STOP && mAppOrTaskLaunch) {
+ // Prevent double logging by skipping STOP when app or task has been launched.
+ return;
+ }
+
if (dstTarget != null) {
event.destTarget = new Target[1];
event.destTarget[0] = dstTarget;
@@ -243,6 +257,15 @@
logActionOnControl(action, controlType, controlInContainer, -1);
}
+ public void logActionOnControl(int action, int controlType, int parentContainer,
+ int grandParentContainer){
+ LauncherEvent event = newLauncherEvent(newTouchAction(action),
+ newControlTarget(controlType),
+ newContainerTarget(parentContainer),
+ newContainerTarget(grandParentContainer));
+ dispatchUserEvent(event, null);
+ }
+
public void logActionOnControl(int action, int controlType, @Nullable View controlInContainer,
int parentContainerType) {
final LauncherEvent event = (controlInContainer == null && parentContainerType < 0)
@@ -269,6 +292,13 @@
dispatchUserEvent(event, null);
}
+ public void logActionBounceTip(int containerType) {
+ LauncherEvent event = newLauncherEvent(newAction(Action.Type.TIP),
+ newContainerTarget(containerType));
+ event.srcTarget[0].tipType = LauncherLogProto.TipType.BOUNCE;
+ dispatchUserEvent(event, null);
+ }
+
public void logActionOnContainer(int action, int dir, int containerType) {
logActionOnContainer(action, dir, containerType, 0);
}
@@ -385,6 +415,7 @@
}
public void dispatchUserEvent(LauncherEvent ev, Intent intent) {
+ mAppOrTaskLaunch = false;
ev.isInLandscapeMode = mIsInLandscapeMode;
ev.isInMultiWindowMode = mIsInMultiWindowMode;
ev.elapsedContainerMillis = SystemClock.uptimeMillis() - mElapsedContainerMillis;
@@ -393,7 +424,8 @@
if (!IS_VERBOSE) {
return;
}
- String log = "action:" + LoggerUtils.getActionStr(ev.action);
+ String log = "\n-----------------------------------------------------"
+ + "\naction:" + LoggerUtils.getActionStr(ev.action);
if (ev.srcTarget != null && ev.srcTarget.length > 0) {
log += "\n Source " + getTargetsStr(ev.srcTarget);
}
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index a8a41f6..7d8a4db 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -73,6 +73,18 @@
return getDefaultView(this);
}
+ @Override
+ protected View getDefaultView() {
+ View v = super.getDefaultView();
+ v.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Launcher.getLauncher(getContext()).startSearch("", false, null, true);
+ }
+ });
+ return v;
+ }
+
public static View getDefaultView(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.qsb_default_view, parent, false);
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 0036bb9..e866445 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -150,4 +150,12 @@
mActivity.setRequestedOrientation(activityFlags);
}
}
+
+ @Override
+ public String toString() {
+ return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
+ + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mAutoRotateEnabled=%b]",
+ mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
+ mIgnoreAutoRotateSettings, mAutoRotateEnabled);
+ }
}
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 7ef53a9..86995b7 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -16,11 +16,14 @@
package com.android.launcher3.util;
+import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import com.android.launcher3.Utilities;
+import java.util.Arrays;
+
/**
* Utility class to manage various window flags to control system UI.
*/
@@ -78,4 +81,9 @@
mWindow.getDecorView().setSystemUiVisibility(newFlags);
}
}
+
+ @Override
+ public String toString() {
+ return "mStates=" + Arrays.toString(mStates);
+ }
}
diff --git a/src/com/android/launcher3/views/ScrimView.java b/src/com/android/launcher3/views/ScrimView.java
index 28602f5..49e96be 100644
--- a/src/com/android/launcher3/views/ScrimView.java
+++ b/src/com/android/launcher3/views/ScrimView.java
@@ -18,16 +18,27 @@
import static android.content.Context.ACCESSIBILITY_SERVICE;
import static android.support.v4.graphics.ColorUtils.compositeColors;
import static android.support.v4.graphics.ColorUtils.setAlphaComponent;
+import static android.view.MotionEvent.ACTION_DOWN;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.anim.Interpolators.ACCEL;
+import static com.android.launcher3.anim.Interpolators.DEACCEL;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+import android.animation.RectEvaluator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
+import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
@@ -85,6 +96,8 @@
protected final int mDragHandleSize;
private final Rect mDragHandleBounds;
+ private final RectF mHitRect = new RectF();
+
private final AccessibilityHelper mAccessibilityHelper;
@Nullable
protected Drawable mDragHandle;
@@ -101,15 +114,21 @@
.getDimensionPixelSize(R.dimen.vertical_drag_handle_size);
mDragHandleBounds = new Rect(0, 0, mDragHandleSize, mDragHandleSize);
- mAccessibilityHelper = new AccessibilityHelper();
+ mAccessibilityHelper = createAccessibilityHelper();
ViewCompat.setAccessibilityDelegate(this, mAccessibilityHelper);
mAM = (AccessibilityManager) context.getSystemService(ACCESSIBILITY_SERVICE);
}
+ @NonNull
+ protected AccessibilityHelper createAccessibilityHelper() {
+ return new AccessibilityHelper();
+ }
+
@Override
public void setInsets(Rect insets) {
updateDragHandleBounds();
+ updateDragHandleVisibility(null);
}
@Override
@@ -177,6 +196,49 @@
if (mCurrentFlatColor != 0) {
canvas.drawColor(mCurrentFlatColor);
}
+ if (mDragHandle != null) {
+ mDragHandle.draw(canvas);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ boolean value = super.onTouchEvent(event);
+ if (!value && mDragHandle != null && event.getAction() == ACTION_DOWN
+ && mDragHandle.getAlpha() == 255
+ && mHitRect.contains(event.getX(), event.getY())) {
+
+ final Drawable drawable = mDragHandle;
+ mDragHandle = null;
+ drawable.setBounds(mDragHandleBounds);
+
+ Rect topBounds = new Rect(mDragHandleBounds);
+ topBounds.offset(0, -mDragHandleBounds.height() / 2);
+
+ Rect invalidateRegion = new Rect(mDragHandleBounds);
+ invalidateRegion.top = topBounds.top;
+
+ Keyframe frameTop = Keyframe.ofObject(0.6f, topBounds);
+ frameTop.setInterpolator(DEACCEL);
+ Keyframe frameBot = Keyframe.ofObject(1, mDragHandleBounds);
+ frameBot.setInterpolator(ACCEL);
+ PropertyValuesHolder holder = PropertyValuesHolder .ofKeyframe("bounds",
+ Keyframe.ofObject(0, mDragHandleBounds), frameTop, frameBot);
+ holder.setEvaluator(new RectEvaluator());
+
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, holder);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ getOverlay().remove(drawable);
+ updateDragHandleVisibility(drawable);
+ }
+ });
+ anim.addUpdateListener((v) -> invalidate(invalidateRegion));
+ getOverlay().add(drawable);
+ anim.start();
+ }
+ return value;
}
protected void updateDragHandleBounds() {
@@ -198,6 +260,9 @@
topMargin = grid.hotseatBarSizePx;
}
mDragHandleBounds.offsetTo(left, top - topMargin);
+ mHitRect.set(mDragHandleBounds);
+ float inset = -mDragHandleSize / 2;
+ mHitRect.inset(inset, inset);
if (mDragHandle != null) {
mDragHandle.setBounds(mDragHandleBounds);
@@ -210,17 +275,29 @@
stateManager.removeStateListener(this);
if (enabled) {
- mDragHandle = mLauncher.getDrawable(R.drawable.drag_handle_indicator);
- mDragHandle.setBounds(mDragHandleBounds);
-
stateManager.addStateListener(this);
onStateSetImmediately(mLauncher.getStateManager().getState());
-
- updateDragHandleAlpha();
} else {
- mDragHandle = null;
+ setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
}
- invalidate();
+ updateDragHandleVisibility(null);
+ }
+
+ private void updateDragHandleVisibility(Drawable recycle) {
+ boolean visible = mLauncher.getDeviceProfile().isVerticalBarLayout() || mAM.isEnabled();
+ boolean wasVisible = mDragHandle != null;
+ if (visible != wasVisible) {
+ if (visible) {
+ mDragHandle = recycle != null ? recycle :
+ mLauncher.getDrawable(R.drawable.drag_handle_indicator);
+ mDragHandle.setBounds(mDragHandleBounds);
+
+ updateDragHandleAlpha();
+ } else {
+ mDragHandle = null;
+ }
+ invalidate();
+ }
}
@Override
@@ -255,7 +332,7 @@
: IMPORTANT_FOR_ACCESSIBILITY_AUTO);
}
- private class AccessibilityHelper extends ExploreByTouchHelper {
+ protected class AccessibilityHelper extends ExploreByTouchHelper {
private static final int DRAG_HANDLE_ID = 1;
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
index b8cd035..e9a47a7 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java
@@ -16,12 +16,15 @@
package com.android.launcher3.uioverrides;
+import android.app.Activity;
import android.content.Context;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.util.TouchController;
+import java.io.PrintWriter;
+
public class UiFactory {
public static TouchController[] createTouchControllers(Launcher launcher) {
@@ -47,4 +50,9 @@
public static void onLauncherStateOrResumeChanged(Launcher launcher) { }
public static void onTrimMemory(Launcher launcher, int level) { }
+
+ public static boolean dumpActivity(Activity activity, PrintWriter writer) {
+ return false;
+ }
+
}