Merge "No more waiting around for resume" into ub-launcher3-master
diff --git a/proguard.flags b/proguard.flags
index a315cdc..987fb6f 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -104,3 +104,8 @@
-keep interface com.android.launcher3.model.nano.LauncherDumpProto.** {
*;
}
+
+# BUG(70852369): Surpress additional warnings after changing from Proguard to R8
+-dontwarn android.app.**
+-dontwarn android.view.**
+-dontwarn android.os.**
diff --git a/quickstep/res/layout/task_menu.xml b/quickstep/res/layout/task_menu.xml
new file mode 100644
index 0000000..6e3fb4f
--- /dev/null
+++ b/quickstep/res/layout/task_menu.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+<com.android.quickstep.TaskMenuView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/bg_popup_item_width"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:elevation="@dimen/deep_shortcuts_elevation"
+ android:orientation="vertical"
+ android:background="?attr/popupColorPrimary"
+ android:divider="@drawable/all_apps_divider"
+ android:showDividers="middle"
+ android:animateLayoutChanges="true">
+ <TextView
+ android:id="@+id/task_icon_and_name"
+ android:layout_width="match_parent"
+ android:layout_height="112dp"
+ android:textSize="14sp"
+ android:paddingTop="18dp"
+ android:drawablePadding="8dp"
+ android:gravity="center_horizontal"/>
+</com.android.quickstep.TaskMenuView>
\ No newline at end of file
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 4f85957..587261d 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -18,8 +18,10 @@
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
+ <dimen name="task_menu_background_radius">12dp</dimen>
<dimen name="quickstep_fling_threshold_velocity">500dp</dimen>
<dimen name="quickstep_fling_min_velocity">250dp</dimen>
+ <dimen name="workspace_overview_offset_x">-30dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index f34aa85..f1da817 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -22,6 +22,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
+import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -49,10 +50,12 @@
}
RecentsView rv = launcher.getOverviewPanel();
+ float overlap = 0;
if (rv.getCurrentPage() >= rv.getFirstTaskIndex()) {
Utilities.scaleRectAboutCenter(pageRect, WORKSPACE_SCALE_ON_SCROLL);
+ overlap = launcher.getResources().getDimension(R.dimen.workspace_overview_offset_x);
}
- return getScaleAndTranslationForPageRect(launcher, pageRect);
+ return getScaleAndTranslationForPageRect(launcher, overlap, pageRect);
}
@Override
@@ -77,15 +80,23 @@
return launcher.getOverviewPanel();
}
- public static float[] getScaleAndTranslationForPageRect(Launcher launcher, Rect pageRect) {
+ public static float[] getScaleAndTranslationForPageRect(Launcher launcher, float offsetX,
+ Rect pageRect) {
Workspace ws = launcher.getWorkspace();
float childWidth = ws.getNormalChildWidth();
Rect insets = launcher.getDragLayer().getInsets();
float scale = pageRect.width() / childWidth;
+ float translationX = offsetX / scale;
+ if (Utilities.isRtl(launcher.getResources())) {
+ translationX = -translationX;
+ }
+
float halfHeight = ws.getHeight() / 2;
float childTop = halfHeight - scale * (halfHeight - ws.getPaddingTop() - insets.top);
- return new float[] {scale, pageRect.top - childTop};
+ float translationY = pageRect.top - childTop;
+
+ return new float[] {scale, translationX, translationY};
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
index 435d57e..410a36f 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/TwoStepSwipeController.java
@@ -29,7 +29,6 @@
import android.support.animation.SpringAnimation;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
@@ -42,7 +41,6 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
-import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.SpringAnimationHandler;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -50,6 +48,10 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.FloatRange;
import com.android.launcher3.util.TouchController;
+import com.android.quickstep.RecentsModel;
+import com.android.quickstep.RecentsView;
+import com.android.quickstep.TouchInteractionService;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import java.util.ArrayList;
@@ -86,6 +88,8 @@
private static final int FLAG_OVERVIEW_DISABLED_OUT_OF_RANGE = 1 << 0;
private static final int FLAG_OVERVIEW_DISABLED_FLING = 1 << 1;
private static final int FLAG_OVERVIEW_DISABLED_CANCEL_STATE = 1 << 2;
+ private static final int FLAG_RECENTS_PLAN_LOADING = 1 << 3;
+ private static final int FLAG_OVERVIEW_DISABLED = 1 << 4;
private final Launcher mLauncher;
private final SwipeDetector mDetector;
@@ -98,9 +102,10 @@
private TaggedAnimatorSetBuilder mTaggedAnimatorSetBuilder;
private AnimatorSet mQuickOverviewAnimation;
private boolean mAnimatingToOverview;
- private TwoStateAnimationController mTwoStateAnimationController;
+ private CroppedAnimationController mCroppedAnimationController;
private AnimatorPlaybackController mCurrentAnimation;
+ private LauncherState mFromState;
private LauncherState mToState;
private float mStartProgress;
@@ -240,11 +245,27 @@
+ MAX_PROGRESS_TO_OVERVIEW - MIN_PROGRESS_TO_OVERVIEW;
// Build current animation
+ mFromState = mLauncher.getStateManager().getState();
mToState = mLauncher.isInState(ALL_APPS) ? NORMAL : ALL_APPS;
mTaggedAnimatorSetBuilder = new TaggedAnimatorSetBuilder();
mCurrentAnimation = mLauncher.getStateManager().createAnimationToNewWorkspace(
mToState, mTaggedAnimatorSetBuilder, maxAccuracy);
+ if (TouchInteractionService.isConnected()) {
+ // Load recents plan
+ RecentsModel recentsModel = RecentsModel.getInstance(mLauncher);
+ if (recentsModel.getLastLoadPlan() != null) {
+ onRecentsPlanLoaded(recentsModel.getLastLoadPlan());
+ } else {
+ mDragPauseDetector.addDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+ }
+ // Reload again so that we get the latest list
+ // TODO: Use callback instead of polling everytime
+ recentsModel.loadTasks(-1, this::onRecentsPlanLoaded);
+ } else {
+ mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED);
+ }
+
mCurrentAnimation.getTarget().addListener(this);
mStartProgress = 0;
mProgressMultiplier = (mLauncher.isInState(ALL_APPS) ? 1 : -1) / range;
@@ -262,6 +283,14 @@
}
}
+ private void onRecentsPlanLoaded(RecentsTaskLoadPlan plan) {
+ RecentsView recentsView = mLauncher.getOverviewPanel();
+ recentsView.update(plan);
+ recentsView.initToPage(0);
+
+ mDragPauseDetector.clearDisabledFlags(FLAG_RECENTS_PLAN_LOADING);
+ }
+
private float getShiftRange() {
return mLauncher.getAllAppsController().getShiftRange();
}
@@ -287,16 +316,11 @@
@Override
public void onDragEnd(float velocity, boolean fling) {
- if (!fling && mDragPauseDetector.isEnabled() && mDragPauseDetector.isTriggered()) {
- snapToOverview(velocity);
- return;
- }
-
mDragPauseDetector.addDisabledFlags(FLAG_OVERVIEW_DISABLED_FLING);
final long animationDuration;
final int logAction;
- final LauncherState targetState;
+ LauncherState targetState;
final float progress = mCurrentAnimation.getProgressFraction();
if (fling) {
@@ -317,7 +341,7 @@
targetState = mToState;
animationDuration = SwipeDetector.calculateDuration(velocity, 1 - progress);
} else {
- targetState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
+ targetState = mFromState;
animationDuration = SwipeDetector.calculateDuration(velocity, progress);
}
}
@@ -328,7 +352,13 @@
h.animateToFinalPosition(0 /* pos */, 1 /* startValue */);
}
}
- mCurrentAnimation.setEndAction(() -> onSwipeInteractionCompleted(targetState, logAction));
+ mCurrentAnimation.setEndAction(() -> {
+ LauncherState finalState = targetState;
+ if (mDragPauseDetector.isTriggered() && targetState == NORMAL) {
+ finalState = OVERVIEW;
+ }
+ onSwipeInteractionCompleted(finalState, logAction);
+ });
float nextFrameProgress = Utilities.boundToRange(
progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
@@ -341,7 +371,7 @@
}
private void onSwipeInteractionCompleted(LauncherState targetState, int logAction) {
- if (targetState == mToState) {
+ if (targetState != mFromState) {
// Transition complete. log the action
mLauncher.getUserEventDispatcher().logActionOnContainer(logAction,
mToState == ALL_APPS ? Direction.UP : Direction.DOWN,
@@ -354,33 +384,6 @@
mLauncher.getStateManager().goToState(targetState, false /* animated */);
}
- private void snapToOverview(float velocity) {
- mAnimatingToOverview = true;
-
- final float progress = mCurrentAnimation.getProgressFraction();
- float endProgress = mToState == NORMAL ? 1f : 0f;
- long animationDuration = SwipeDetector.calculateDuration(
- velocity, Math.abs(endProgress - progress));
- float nextFrameProgress = Utilities.boundToRange(
- progress + velocity * SINGLE_FRAME_MS / getShiftRange(), 0f, 1f);
-
- mCurrentAnimation.setEndAction(() -> {
- // TODO: Add logging
- clearState();
- mLauncher.getStateManager().goToState(OVERVIEW, true /* animated */);
- });
-
- if (mTwoStateAnimationController != null) {
- mTwoStateAnimationController.goBackToStart(endProgress);
- }
-
- ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
- anim.setFloatValues(nextFrameProgress, endProgress);
- anim.setDuration(animationDuration);
- anim.setInterpolator(scrollInterpolatorForVelocity(velocity));
- anim.start();
- }
-
private void onDragPauseDetected() {
final ValueAnimator twoStepAnimator = ValueAnimator.ofFloat(0, 1);
twoStepAnimator.setDuration(mCurrentAnimation.getDuration());
@@ -409,33 +412,29 @@
mQuickOverviewAnimation.start();
}
- private void onQuickOverviewAnimationComplete(ValueAnimator twoStepAnimator) {
+ private void onQuickOverviewAnimationComplete(ValueAnimator animator) {
if (mAnimatingToOverview) {
return;
}
- // The remaining state handlers are on the OVERVIEW state. Create two animations, one
- // towards the NORMAL state and one towards ALL_APPS state and control them based on the
- // swipe progress.
+ // For the remainder to the interaction, the user can either go to the ALL_APPS state or
+ // the OVERVIEW state.
+ // The remaining state handlers are on the OVERVIEW state. Create one animation towards the
+ // ALL_APPS state and only call it when the user moved above the current range.
AnimationConfig config = new AnimationConfig();
config.duration = (long) (2 * getShiftRange());
config.userControlled = true;
- LauncherState fromState = mToState == ALL_APPS ? NORMAL : ALL_APPS;
- AnimatorSetBuilder builderToTargetState = new AnimatorSetBuilder();
- AnimatorSetBuilder builderToSourceState = new AnimatorSetBuilder();
-
+ AnimatorSetBuilder builderToAllAppsState = new AnimatorSetBuilder();
StateHandler[] handlers = mLauncher.getStateManager().getStateHandlers();
for (int i = OTHER_HANDLERS_START_INDEX; i < handlers.length; i++) {
- handlers[i].setStateWithAnimation(mToState, builderToTargetState, config);
- handlers[i].setStateWithAnimation(fromState, builderToSourceState, config);
+ handlers[i].setStateWithAnimation(ALL_APPS, builderToAllAppsState, config);
}
- mTwoStateAnimationController = new TwoStateAnimationController(
- AnimatorPlaybackController.wrap(builderToSourceState.build(), config.duration),
- AnimatorPlaybackController.wrap(builderToTargetState.build(), config.duration),
- twoStepAnimator.getAnimatedFraction());
- twoStepAnimator.addUpdateListener(mTwoStateAnimationController);
+ mCroppedAnimationController = new CroppedAnimationController(
+ AnimatorPlaybackController.wrap(builderToAllAppsState.build(), config.duration),
+ new FloatRange(animator.getAnimatedFraction(), mToState == ALL_APPS ? 1 : 0));
+ animator.addUpdateListener(mCroppedAnimationController);
}
private void clearState() {
@@ -450,69 +449,49 @@
mQuickOverviewAnimation.cancel();
mQuickOverviewAnimation = null;
}
- mTwoStateAnimationController = null;
+ mCroppedAnimationController = null;
mAnimatingToOverview = false;
mDetector.finishedScrolling();
}
/**
- * {@link AnimatorUpdateListener} which interpolates two animations based the progress
+ * {@link AnimatorUpdateListener} which controls another animation for a fraction of range
*/
- private static class TwoStateAnimationController implements AnimatorUpdateListener {
+ private static class CroppedAnimationController implements AnimatorUpdateListener {
- private final AnimatorPlaybackController mControllerTowardsStart;
- private final AnimatorPlaybackController mControllerTowardsEnd;
+ private final AnimatorPlaybackController mTarget;
+ private final FloatRange mRange;
- private Interpolator mInterpolator = Interpolators.LINEAR;
- private float mStartFraction;
- private float mLastFraction;
-
- TwoStateAnimationController(AnimatorPlaybackController controllerTowardsStart,
- AnimatorPlaybackController controllerTowardsEnd, float startFraction) {
- mControllerTowardsStart = controllerTowardsStart;
- mControllerTowardsEnd = controllerTowardsEnd;
- mLastFraction = mStartFraction = startFraction;
+ CroppedAnimationController(AnimatorPlaybackController target, FloatRange range) {
+ mTarget = target;
+ mRange = range;
}
+
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
- mLastFraction = mInterpolator.getInterpolation(valueAnimator.getAnimatedFraction());
- if (mLastFraction > mStartFraction) {
- if (mStartFraction >= 1) {
- mControllerTowardsEnd.setPlayFraction(0);
- } else {
- mControllerTowardsEnd.setPlayFraction(
- (mLastFraction - mStartFraction) / (1 - mStartFraction));
- }
- } else {
- if (mStartFraction <= 0) {
- mControllerTowardsStart.setPlayFraction(0);
- } else {
- mControllerTowardsStart.setPlayFraction(
- (mStartFraction - mLastFraction) / mStartFraction);
- }
- }
- }
+ float fraction = valueAnimator.getAnimatedFraction();
- /**
- * Changes the interpolator such that from this point ({@link #mLastFraction}), the
- * animation run towards {@link #mStartFraction}. This allows us to animate the UI back
- * to the original point.
- * @param endFraction expected end point for this animation. Should either be 0 or 1.
- */
- public void goBackToStart(float endFraction) {
- if (mLastFraction == mStartFraction || mLastFraction == endFraction) {
- mInterpolator = (v) -> mStartFraction;
- } else if (mLastFraction > mStartFraction && endFraction < mStartFraction) {
- mInterpolator = (v) -> Math.max(v, mStartFraction);
- } else if (mLastFraction < mStartFraction && endFraction > mStartFraction) {
- mInterpolator = (v) -> Math.min(mStartFraction, v);
+ if (mRange.start < mRange.end) {
+ if (fraction <= mRange.start) {
+ mTarget.setPlayFraction(0);
+ } else if (fraction >= mRange.end) {
+ mTarget.setPlayFraction(1);
+ } else {
+ mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+ }
+ } else if (mRange.start > mRange.end) {
+ if (fraction >= mRange.start) {
+ mTarget.setPlayFraction(0);
+ } else if (fraction <= mRange.end) {
+ mTarget.setPlayFraction(1);
+ } else {
+ mTarget.setPlayFraction((fraction - mRange.start) / (mRange.end - mRange.start));
+ }
} else {
- final float start = mLastFraction;
- final float range = endFraction - mLastFraction;
- mInterpolator = (v) ->
- SwipeDetector.interpolate(start, mStartFraction, (v - start) / range);
+ // mRange.start == mRange.end
+ mTarget.setPlayFraction(0);
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
index 990d286..9f7cffe 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/WorkspaceCard.java
@@ -39,7 +39,7 @@
public class WorkspaceCard extends FrameLayout implements PageCallbacks, OnClickListener {
private final Rect mTempRect = new Rect();
- private final float[] mEvaluatedFloats = new float[2];
+ private final float[] mEvaluatedFloats = new float[3];
private final FloatArrayEvaluator mEvaluator = new FloatArrayEvaluator(mEvaluatedFloats);
// UI related information
@@ -151,21 +151,25 @@
@Override
public int onPageScroll(ScrollState scrollState) {
- setTranslationX(scrollState.distanceFromScreenCenter);
-
float factor = scrollState.linearInterpolation;
float scale = factor * WORKSPACE_SCALE_ON_SCROLL + (1 - factor);
setScaleX(scale);
setScaleY(scale);
+ float translateX = scrollState.distanceFromScreenCenter;
if (mIsWorkspaceScrollingEnabled) {
initUiData();
mEvaluator.evaluate(factor, mScaleAndTranslatePage0, mScaleAndTranslatePage1);
mWorkspace.setScaleX(mEvaluatedFloats[0]);
mWorkspace.setScaleY(mEvaluatedFloats[0]);
- mWorkspace.setTranslationY(mEvaluatedFloats[1]);
+ mWorkspace.setTranslationX(mEvaluatedFloats[1]);
+ mWorkspace.setTranslationY(mEvaluatedFloats[2]);
+ translateX += mEvaluatedFloats[1];
}
+
+ setTranslationX(translateX);
+
return SCROLL_TYPE_WORKSPACE;
}
@@ -174,13 +178,15 @@
return;
}
+ float overlap = getResources().getDimension(R.dimen.workspace_overview_offset_x);
+
RecentsView.getPageRect(mLauncher, mTempRect);
mScaleAndTranslatePage0 = OverviewState
- .getScaleAndTranslationForPageRect(mLauncher, mTempRect);
+ .getScaleAndTranslationForPageRect(mLauncher, 0, mTempRect);
Rect scaledDown = new Rect(mTempRect);
Utilities.scaleRectAboutCenter(scaledDown, WORKSPACE_SCALE_ON_SCROLL);
mScaleAndTranslatePage1 = OverviewState
- .getScaleAndTranslationForPageRect(mLauncher, scaledDown);
+ .getScaleAndTranslationForPageRect(mLauncher, overlap, scaledDown);
mUIDataValid = true;
}
}
diff --git a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
index 095b445..09fd8f0 100644
--- a/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
+++ b/quickstep/src/com/android/quickstep/NavBarSwipeInteractionHandler.java
@@ -44,6 +44,7 @@
import com.android.launcher3.states.InternalStateHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.views.AllAppsScrim;
import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey;
@@ -97,6 +98,7 @@
private RecentsView mRecentsView;
private RecentsViewStateController mStateController;
private Hotseat mHotseat;
+ private AllAppsScrim mAllAppsScrim;
private RecentsTaskLoadPlan mLoadPlan;
private boolean mLauncherReady;
@@ -182,6 +184,7 @@
mRecentsView = mLauncher.getOverviewPanel();
mStateController = mRecentsView.getStateController();
mHotseat = mLauncher.getHotseat();
+ mAllAppsScrim = mLauncher.findViewById(R.id.all_apps_scrim);
// Optimization
mLauncher.getAppsView().setVisibility(View.GONE);
@@ -222,7 +225,9 @@
float shift = mCurrentShift.value * mActivityMultiplier.value;
int hotseatSize = getHotseatSize();
- mHotseat.setTranslationY((1 - shift) * hotseatSize);
+ float hotseatTranslation = (1 - shift) * hotseatSize;
+ mHotseat.setTranslationY(hotseatTranslation);
+ mAllAppsScrim.setTranslationY(hotseatTranslation);
mRectEvaluator.evaluate(shift, mSourceRect, mTargetRect);
@@ -324,6 +329,7 @@
private void cleanupLauncher() {
// TODO: These should be done as part of ActivityOptions#OnAnimationStarted
mHotseat.setTranslationY(0);
+ mAllAppsScrim.setTranslationY(0);
mLauncher.setOnResumeCallback(() -> mDragView.close(false));
}
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
new file mode 100644
index 0000000..112f156
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -0,0 +1,104 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Looper;
+import android.os.UserHandle;
+
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.R;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
+import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
+import com.android.systemui.shared.recents.model.RecentsTaskLoader;
+import com.android.systemui.shared.system.BackgroundExecutor;
+
+import java.util.concurrent.ExecutionException;
+import java.util.function.Consumer;
+
+/**
+ * Singleton class to load and manage recents model.
+ */
+public class RecentsModel {
+
+ // We do not need any synchronization for this variable as its only written on UI thread.
+ private static RecentsModel INSTANCE;
+
+ public static RecentsModel getInstance(final Context context) {
+ if (INSTANCE == null) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ INSTANCE = new RecentsModel(context.getApplicationContext());
+ } else {
+ try {
+ return new MainThreadExecutor().submit(
+ () -> RecentsModel.getInstance(context)).get();
+ } catch (InterruptedException|ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ private final Context mContext;
+ private final RecentsTaskLoader mRecentsTaskLoader;
+ private final MainThreadExecutor mMainThreadExecutor;
+
+ private RecentsTaskLoadPlan mLastLoadPlan;
+ private RecentsModel(Context context) {
+ mContext = context;
+
+ Resources res = context.getResources();
+ mRecentsTaskLoader = new RecentsTaskLoader(mContext,
+ res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
+ res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
+ mRecentsTaskLoader.startLoader(mContext);
+
+ mMainThreadExecutor = new MainThreadExecutor();
+ }
+
+ public RecentsTaskLoader getRecentsTaskLoader() {
+ return mRecentsTaskLoader;
+ }
+
+ /**
+ * Preloads the task plan
+ * @param taskId The running task id or -1
+ * @param callback The callback to receive the task plan once its complete or null. This is
+ * always called on the UI thread.
+ */
+ public void loadTasks(int taskId, Consumer<RecentsTaskLoadPlan> callback) {
+ BackgroundExecutor.get().submit(() -> {
+ // Preload the plan
+ RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(mContext);
+ PreloadOptions opts = new PreloadOptions();
+ opts.loadTitles = false;
+ loadPlan.preloadPlan(opts, mRecentsTaskLoader, taskId, UserHandle.myUserId());
+ // Set the load plan on UI thread
+ mMainThreadExecutor.execute(() -> {
+ mLastLoadPlan = loadPlan;
+ if (callback != null) {
+ callback.accept(loadPlan);
+ }
+ });
+ });
+ }
+
+ public RecentsTaskLoadPlan getLastLoadPlan() {
+ return mLastLoadPlan;
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/RecentsView.java b/quickstep/src/com/android/quickstep/RecentsView.java
index 6161858..00901c6 100644
--- a/quickstep/src/com/android/quickstep/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/RecentsView.java
@@ -146,7 +146,8 @@
}
public void update(RecentsTaskLoadPlan loadPlan) {
- final RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
+ final RecentsTaskLoader loader = RecentsModel.getInstance(getContext())
+ .getRecentsTaskLoader();
TaskStack stack = loadPlan != null ? loadPlan.getTaskStack() : null;
if (stack == null) {
removeAllViews();
diff --git a/quickstep/src/com/android/quickstep/TaskMenuView.java b/quickstep/src/com/android/quickstep/TaskMenuView.java
new file mode 100644
index 0000000..70542c2
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskMenuView.java
@@ -0,0 +1,223 @@
+/*
+ * 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;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Outline;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.TextView;
+
+import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAnimUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.anim.AnimationSuccessListener;
+import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.shortcuts.DeepShortcutView;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * Contains options for a recent task when long-pressing its icon.
+ */
+public class TaskMenuView extends AbstractFloatingView {
+
+ private static final Rect sTempRect = new Rect();
+
+ /** Note that these will be shown in order from top to bottom, if available for the task. */
+ private static final TaskSystemShortcut[] MENU_OPTIONS = new TaskSystemShortcut[] {
+ new TaskSystemShortcut.Widgets(),
+ new TaskSystemShortcut.AppInfo(),
+ new TaskSystemShortcut.Install()
+ };
+
+ private static final long OPEN_CLOSE_DURATION = 220;
+
+ private Launcher mLauncher;
+ private TextView mTaskIconAndName;
+ private AnimatorSet mOpenCloseAnimator;
+
+ public TaskMenuView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TaskMenuView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mLauncher = Launcher.getLauncher(context);
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ float r = getResources().getDimensionPixelSize(R.dimen.task_menu_background_radius);
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), r);
+ }
+ });
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTaskIconAndName = findViewById(R.id.task_icon_and_name);
+ }
+
+ @Override
+ public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ DragLayer dl = mLauncher.getDragLayer();
+ if (!dl.isEventOverView(this, ev)) {
+ // TODO: log this once we have a new container type for it?
+ close(true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void handleClose(boolean animate) {
+ if (animate) {
+ animateClose();
+ } else {
+ closeComplete();
+ }
+ }
+
+ @Override
+ public void logActionCommand(int command) {
+ // TODO
+ }
+
+ @Override
+ protected boolean isOfType(int type) {
+ return (type & TYPE_TASK_MENU) != 0;
+ }
+
+ public static boolean showForTask(TaskView taskView) {
+ Launcher launcher = Launcher.getLauncher(taskView.getContext());
+ final TaskMenuView taskMenuView = (TaskMenuView) launcher.getLayoutInflater().inflate(
+ R.layout.task_menu, launcher.getDragLayer(), false);
+ return taskMenuView.populateAndShowForTask(taskView);
+ }
+
+ private boolean populateAndShowForTask(TaskView taskView) {
+ if (isAttachedToWindow()) {
+ return false;
+ }
+ mLauncher.getDragLayer().addView(this);
+ addMenuOptions(taskView.getTask());
+ orientAroundTaskView(taskView);
+ post(this::animateOpen);
+ return true;
+ }
+
+ private void addMenuOptions(Task task) {
+ Drawable icon = task.icon.getConstantState().newDrawable();
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
+ icon.setBounds(0, 0, iconSize, iconSize);
+ mTaskIconAndName.setCompoundDrawables(null, icon, null, null);
+ mTaskIconAndName.setText(TaskUtils.getTitle(mLauncher, task));
+
+ LayoutInflater inflater = mLauncher.getLayoutInflater();
+ for (TaskSystemShortcut menuOption : MENU_OPTIONS) {
+ OnClickListener onClickListener = menuOption.getOnClickListener(mLauncher, task);
+ if (onClickListener != null) {
+ DeepShortcutView menuOptionView = (DeepShortcutView) inflater.inflate(
+ R.layout.system_shortcut, this, false);
+ menuOptionView.getIconView().setBackgroundResource(menuOption.iconResId);
+ menuOptionView.getBubbleText().setText(menuOption.labelResId);
+ menuOptionView.setOnClickListener(onClickListener);
+ addView(menuOptionView);
+ }
+ }
+ }
+
+ private void orientAroundTaskView(TaskView taskView) {
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ mLauncher.getDragLayer().getDescendantRectRelativeToSelf(taskView, sTempRect);
+ Rect insets = mLauncher.getDragLayer().getInsets();
+ setX(sTempRect.left + (sTempRect.width() - getMeasuredWidth()) / 2 - insets.left);
+ setY(sTempRect.top - mTaskIconAndName.getPaddingTop() - insets.top);
+ }
+
+ private void animateOpen() {
+ animateOpenOrClosed(false);
+ mIsOpen = true;
+ }
+
+ private void animateClose() {
+ animateOpenOrClosed(true);
+ }
+
+ private void animateOpenOrClosed(boolean closing) {
+ if (mOpenCloseAnimator != null && mOpenCloseAnimator.isRunning()) {
+ return;
+ }
+ mOpenCloseAnimator = LauncherAnimUtils.createAnimatorSet();
+ mOpenCloseAnimator.play(createOpenCloseOutlineProvider()
+ .createRevealAnimator(this, closing));
+ mOpenCloseAnimator.addListener(new AnimationSuccessListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void onAnimationSuccess(Animator animator) {
+ if (closing) {
+ closeComplete();
+ }
+ }
+ });
+ mOpenCloseAnimator.play(ObjectAnimator.ofFloat(this, ALPHA, closing ? 0 : 1));
+ mOpenCloseAnimator.setDuration(OPEN_CLOSE_DURATION);
+ mOpenCloseAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
+ mOpenCloseAnimator.start();
+ }
+
+ private void closeComplete() {
+ mIsOpen = false;
+ mLauncher.getDragLayer().removeView(this);
+ }
+
+ private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
+ int iconSize = getResources().getDimensionPixelSize(R.dimen.task_thumbnail_icon_size);
+ float fromRadius = iconSize / 2;
+ float toRadius = getResources().getDimensionPixelSize(
+ R.dimen.task_menu_background_radius);
+ Point iconCenter = new Point(getWidth() / 2, mTaskIconAndName.getPaddingTop() + iconSize / 2);
+ Rect fromRect = new Rect(iconCenter.x, iconCenter.y, iconCenter.x, iconCenter.y);
+ Rect toRect = new Rect(0, 0, getWidth(), getHeight());
+ return new RoundedRectRevealOutlineProvider(fromRadius, toRadius, fromRect, toRect) {
+ @Override
+ public boolean shouldRemoveElevationDuringAnimation() {
+ return true;
+ }
+ };
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskSystemShortcut.java b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
new file mode 100644
index 0000000..1ba7ce4
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskSystemShortcut.java
@@ -0,0 +1,92 @@
+/*
+ * 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;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.view.View;
+
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.popup.SystemShortcut;
+import com.android.launcher3.util.InstantAppResolver;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * Represents a system shortcut that can be shown for a recent task.
+ */
+public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut {
+
+ protected T mSystemShortcut;
+
+ protected TaskSystemShortcut(T systemShortcut) {
+ super(systemShortcut.iconResId, systemShortcut.labelResId);
+ mSystemShortcut = systemShortcut;
+ }
+
+ @Override
+ public View.OnClickListener getOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ return null;
+ }
+
+ public View.OnClickListener getOnClickListener(final Launcher launcher, final Task task) {
+ ShortcutInfo dummyInfo = new ShortcutInfo();
+ dummyInfo.intent = new Intent();
+ ComponentName component = task.getTopComponent();
+ dummyInfo.intent.setComponent(component);
+ dummyInfo.user = UserHandle.getUserHandleForUid(task.key.userId);
+ dummyInfo.title = TaskUtils.getTitle(launcher, task);
+
+ return getOnClickListenerForTask(launcher, task, dummyInfo);
+ }
+
+ protected View.OnClickListener getOnClickListenerForTask(final Launcher launcher,
+ final Task task, final ItemInfo dummyInfo) {
+ return mSystemShortcut.getOnClickListener(launcher, dummyInfo);
+ }
+
+
+ public static class Widgets extends TaskSystemShortcut<SystemShortcut.Widgets> {
+ public Widgets() {
+ super(new SystemShortcut.Widgets());
+ }
+ }
+
+ public static class AppInfo extends TaskSystemShortcut<SystemShortcut.AppInfo> {
+ public AppInfo() {
+ super(new SystemShortcut.AppInfo());
+ }
+ }
+
+ public static class Install extends TaskSystemShortcut<SystemShortcut.Install> {
+ public Install() {
+ super(new SystemShortcut.Install());
+ }
+
+ @Override
+ protected View.OnClickListener getOnClickListenerForTask(Launcher launcher, Task task,
+ ItemInfo itemInfo) {
+ if (InstantAppResolver.newInstance(launcher).isInstantApp(launcher,
+ task.getTopComponent().getPackageName())) {
+ return mSystemShortcut.createOnClickListener(launcher, itemInfo);
+ }
+ return null;
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskThumbnailView.java b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
index 4a9bfea..3d4d451 100644
--- a/quickstep/src/com/android/quickstep/TaskThumbnailView.java
+++ b/quickstep/src/com/android/quickstep/TaskThumbnailView.java
@@ -173,4 +173,10 @@
}
invalidate();
}
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ updateThumbnailMatrix();
+ }
}
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
new file mode 100644
index 0000000..a95e7c1
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import com.android.launcher3.Launcher;
+import com.android.systemui.shared.recents.model.Task;
+
+/**
+ * Contains helpful methods for retrieving data from {@link Task}s.
+ * TODO: remove this once we switch to getting the icon and label from IconCache.
+ */
+public class TaskUtils {
+ private static final String TAG = "TaskUtils";
+
+ public static CharSequence getTitle(Launcher launcher, Task task) {
+ PackageManager pm = launcher.getPackageManager();
+ try {
+ return pm.getPackageInfo(task.getTopComponent().getPackageName(), 0)
+ .applicationInfo.loadLabel(pm);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Failed to get title for task " + task, e);
+ }
+ return "";
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/TaskView.java b/quickstep/src/com/android/quickstep/TaskView.java
index 6b37ada..94d85ee 100644
--- a/quickstep/src/com/android/quickstep/TaskView.java
+++ b/quickstep/src/com/android/quickstep/TaskView.java
@@ -208,12 +208,14 @@
public void onTaskDataLoaded(Task task, ThumbnailData thumbnailData) {
mSnapshotView.setThumbnail(thumbnailData);
mIconView.setImageDrawable(task.icon);
+ mIconView.setOnLongClickListener(icon -> TaskMenuView.showForTask(this));
}
@Override
public void onTaskDataUnloaded() {
mSnapshotView.setThumbnail(null);
mIconView.setImageDrawable(null);
+ mIconView.setOnLongClickListener(null);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index f457a59..4321791 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -28,10 +28,8 @@
import android.app.ActivityOptions;
import android.app.Service;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
@@ -39,7 +37,6 @@
import android.os.Build;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.Log;
import android.view.Choreographer;
import android.view.Display;
@@ -52,14 +49,9 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.R;
import com.android.launcher3.util.TraceHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.ISystemUiProxy;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan;
-import com.android.systemui.shared.recents.model.RecentsTaskLoadPlan.PreloadOptions;
-import com.android.systemui.shared.recents.model.RecentsTaskLoader;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.BackgroundExecutor;
import com.android.systemui.shared.system.WindowManagerWrapper;
@@ -74,8 +66,6 @@
private static final String TAG = "TouchInteractionService";
- private static RecentsTaskLoader sRecentsTaskLoader;
-
private final IBinder mMyBinder = new IOverviewProxy.Stub() {
@Override
@@ -93,12 +83,18 @@
= this::handleTouchDownOnOtherActivity;
private final Consumer<MotionEvent> mNoOpTouchConsumer = (ev) -> {};
+ private static boolean sConnected = false;
+
+ public static boolean isConnected() {
+ return sConnected;
+ }
+
private ActivityManagerWrapper mAM;
private RunningTaskInfo mRunningTask;
+ private RecentsModel mRecentsModel;
private Intent mHomeIntent;
private ComponentName mLauncher;
private MotionEventQueue mEventQueue;
- private MainThreadExecutor mMainThreadExecutor;
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
@@ -117,6 +113,7 @@
public void onCreate() {
super.onCreate();
mAM = ActivityManagerWrapper.getInstance();
+ mRecentsModel = RecentsModel.getInstance(this);
mHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
@@ -126,16 +123,15 @@
mLauncher = new ComponentName(getPackageName(), info.activityInfo.name);
mHomeIntent.setComponent(mLauncher);
- Resources res = getResources();
- if (sRecentsTaskLoader == null) {
- sRecentsTaskLoader = new RecentsTaskLoader(this,
- res.getInteger(R.integer.config_recentsMaxThumbnailCacheSize),
- res.getInteger(R.integer.config_recentsMaxIconCacheSize), 0);
- sRecentsTaskLoader.startLoader(this);
- }
-
- mMainThreadExecutor = new MainThreadExecutor();
mEventQueue = new MotionEventQueue(Choreographer.getInstance(), this::handleMotionEvent);
+ mRecentsModel.loadTasks(-1, null);
+ sConnected = true;
+ }
+
+ @Override
+ public void onDestroy() {
+ sConnected = false;
+ super.onDestroy();
}
@Override
@@ -144,10 +140,6 @@
return mMyBinder;
}
- public static RecentsTaskLoader getRecentsTaskLoader() {
- return sRecentsTaskLoader;
- }
-
private void handleMotionEvent(MotionEvent ev) {
if (ev.getActionMasked() == ACTION_DOWN) {
mRunningTask = mAM.getRunningTask();
@@ -255,12 +247,9 @@
final NavBarSwipeInteractionHandler handler =
new NavBarSwipeInteractionHandler(mRunningTask, this);
- // Preload and start the recents activity on a background thread
- final Context context = this;
- final RecentsTaskLoadPlan loadPlan = new RecentsTaskLoadPlan(context);
- final int taskId = mRunningTask.id;
TraceHelper.partitionSection("TouchInt", "Thershold crossed ");
+ // Start the recents activity on a background thread
BackgroundExecutor.get().submit(() -> {
// Get the snap shot before
handler.setTaskSnapshot(getCurrentTaskSnapshot());
@@ -275,15 +264,10 @@
ActivityOptions.makeCustomAnimation(this, 0, 0), UserHandle.myUserId(),
null, null);
*/
-
- // Preload the plan
- RecentsTaskLoader loader = TouchInteractionService.getRecentsTaskLoader();
- PreloadOptions opts = new PreloadOptions();
- opts.loadTitles = false;
- loadPlan.preloadPlan(opts, loader, taskId, UserHandle.myUserId());
- // Set the load plan on UI thread
- mMainThreadExecutor.execute(() -> handler.setRecentsTaskLoadPlan(loadPlan));
});
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTask.id, handler::setRecentsTaskLoadPlan);
mInteractionHandler = handler;
}
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1ae7cbf..36eb34b 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -199,10 +199,6 @@
<dimen name="popup_arrow_horizontal_center_start">28dp</dimen>
<!-- popup_padding_end + deep_shortcut_drag_handle_size / 2 -->
<dimen name="popup_arrow_horizontal_center_end">24dp</dimen>
- <!-- popup_arrow_center_start - popup_arrow_width / 2-->
- <dimen name="popup_arrow_horizontal_offset_start">23dp</dimen>
- <!-- popup_arrow_center_end - popup_arrow_width / 2-->
- <dimen name="popup_arrow_horizontal_offset_end">19dp</dimen>
<dimen name="popup_arrow_corner_radius">2dp</dimen>
<!-- popup_padding_start + icon_size + 10dp -->
<dimen name="deep_shortcuts_text_padding_start">56dp</dimen>
diff --git a/src/com/android/launcher3/AbstractFloatingView.java b/src/com/android/launcher3/AbstractFloatingView.java
index da464c0..9a6be0b 100644
--- a/src/com/android/launcher3/AbstractFloatingView.java
+++ b/src/com/android/launcher3/AbstractFloatingView.java
@@ -43,7 +43,8 @@
TYPE_WIDGET_RESIZE_FRAME,
TYPE_WIDGETS_FULL_SHEET,
TYPE_QUICKSTEP_PREVIEW,
- TYPE_ON_BOARD_POPUP
+ TYPE_ON_BOARD_POPUP,
+ TYPE_TASK_MENU
})
@Retention(RetentionPolicy.SOURCE)
public @interface FloatingViewType {}
@@ -54,10 +55,11 @@
public static final int TYPE_WIDGETS_FULL_SHEET = 1 << 4;
public static final int TYPE_QUICKSTEP_PREVIEW = 1 << 5;
public static final int TYPE_ON_BOARD_POPUP = 1 << 6;
+ public static final int TYPE_TASK_MENU = 1 << 7;
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
- | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP;
+ | TYPE_QUICKSTEP_PREVIEW | TYPE_ON_BOARD_POPUP | TYPE_TASK_MENU;
// Type of popups which should be kept open during launcher rebind
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 2beaca1..a1f5879 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -137,7 +137,7 @@
}
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
- return new float[] {1, 0};
+ return new float[] {1, 0, 0};
}
public float getHoseatAlpha(Launcher launcher) {
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index 80818f2..9ed86ed 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -123,10 +123,8 @@
* Starts a transition animation for the workspace.
*/
private void setWorkspaceProperty(LauncherState state, PropertySetter propertySetter) {
- float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher);
- mNewScale = scaleAndTranslationY[0];
- final float finalWorkspaceTranslationY = scaleAndTranslationY[1];
-
+ float[] scaleAndTranslation = state.getWorkspaceScaleAndTranslation(mLauncher);
+ mNewScale = scaleAndTranslation[0];
PageAlphaProvider pageAlphaProvider = state.getWorkspacePageAlphaProvider(mLauncher);
final int childCount = mWorkspace.getChildCount();
for (int i = 0; i < childCount; i++) {
@@ -135,8 +133,10 @@
}
propertySetter.setFloat(mWorkspace, SCALE_PROPERTY, mNewScale, Interpolators.ZOOM_IN);
+ propertySetter.setFloat(mWorkspace, View.TRANSLATION_X,
+ scaleAndTranslation[1], Interpolators.ZOOM_IN);
propertySetter.setFloat(mWorkspace, View.TRANSLATION_Y,
- finalWorkspaceTranslationY, Interpolators.ZOOM_IN);
+ scaleAndTranslation[2], Interpolators.ZOOM_IN);
float hotseatAlpha = state.getHoseatAlpha(mLauncher);
propertySetter.setViewAlpha(mWorkspace.createHotseatAlphaAnimator(hotseatAlpha),
diff --git a/src/com/android/launcher3/anim/RevealOutlineAnimation.java b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
index 1312da9..c6b62fa 100644
--- a/src/com/android/launcher3/anim/RevealOutlineAnimation.java
+++ b/src/com/android/launcher3/anim/RevealOutlineAnimation.java
@@ -34,7 +34,6 @@
final float elevation = revealView.getElevation();
va.addListener(new AnimatorListenerAdapter() {
- private boolean mWasCanceled = false;
private boolean mIsClippedToOutline;
private ViewOutlineProvider mOldOutlineProvider;
@@ -49,18 +48,11 @@
}
}
- @Override
- public void onAnimationCancel(Animator animation) {
- mWasCanceled = true;
- }
-
public void onAnimationEnd(Animator animation) {
- if (!mWasCanceled) {
- revealView.setOutlineProvider(mOldOutlineProvider);
- revealView.setClipToOutline(mIsClippedToOutline);
- if (shouldRemoveElevationDuringAnimation()) {
- revealView.setTranslationZ(0);
- }
+ revealView.setOutlineProvider(mOldOutlineProvider);
+ revealView.setClipToOutline(mIsClippedToOutline);
+ if (shouldRemoveElevationDuringAnimation()) {
+ revealView.setTranslationZ(0);
}
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index a166dff..6481183 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -341,15 +341,17 @@
updateDividers();
// Add the arrow.
- final int arrowHorizontalOffset = getResources().getDimensionPixelSize(isAlignedWithStart()
- ? R.dimen.popup_arrow_horizontal_offset_start
- : R.dimen.popup_arrow_horizontal_offset_end);
+ final Resources res = getResources();
+ final int arrowCenterOffset = res.getDimensionPixelSize(isAlignedWithStart()
+ ? R.dimen.popup_arrow_horizontal_center_start
+ : R.dimen.popup_arrow_horizontal_center_end);
+ final int halfArrowWidth = res.getDimensionPixelSize(R.dimen.popup_arrow_width) / 2;
mLauncher.getDragLayer().addView(mArrow);
DragLayer.LayoutParams arrowLp = (DragLayer.LayoutParams) mArrow.getLayoutParams();
if (mIsLeftAligned) {
- mArrow.setX(getX() + arrowHorizontalOffset);
+ mArrow.setX(getX() + arrowCenterOffset - halfArrowWidth);
} else {
- mArrow.setX(getX() + getMeasuredWidth() - arrowHorizontalOffset);
+ mArrow.setX(getX() + getMeasuredWidth() - arrowCenterOffset - halfArrowWidth);
}
if (Gravity.isVertical(mGravity)) {
@@ -435,9 +437,6 @@
x = rightAlignedX;
}
mIsLeftAligned = x == leftAlignedX;
- if (mIsRtl) {
- x -= dragLayer.getWidth() - width;
- }
// Offset x so that the arrow and shortcut icons are center-aligned with the original icon.
int iconWidth = mOriginalIcon.getWidth()
@@ -529,8 +528,7 @@
// enforce contained is within screen
DragLayer dragLayer = mLauncher.getDragLayer();
- if (getTranslationX() + l < 0 ||
- getTranslationX() + r > dragLayer.getWidth()) {
+ if (getTranslationX() + l < 0 || getTranslationX() + r > dragLayer.getWidth()) {
// If we are still off screen, center horizontally too.
mGravity |= Gravity.CENTER_HORIZONTAL;
}
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index c398aaa..42aa12b 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,5 +1,8 @@
package com.android.launcher3.popup;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
+import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
+
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
@@ -19,9 +22,6 @@
import java.util.List;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.Action;
-import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
-
/**
* Represents a system shortcut for a given app. The shortcut should have a static label and
* icon, and an onClickListener that depends on the item that the shortcut services.
@@ -110,14 +110,15 @@
if (!enabled) {
return null;
}
- return new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- Intent intent = PackageManagerHelper.getMarketIntent(itemInfo
- .getTargetComponent().getPackageName());
- launcher.startActivitySafely(view, intent, itemInfo);
- AbstractFloatingView.closeAllOpenViews(launcher);
- }
+ return createOnClickListener(launcher, itemInfo);
+ }
+
+ public View.OnClickListener createOnClickListener(Launcher launcher, ItemInfo itemInfo) {
+ return view -> {
+ Intent intent = PackageManagerHelper.getMarketIntent(itemInfo
+ .getTargetComponent().getPackageName());
+ launcher.startActivitySafely(view, intent, itemInfo);
+ AbstractFloatingView.closeAllOpenViews(launcher);
};
}
}
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index e86bfb2..704d82f 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -1,6 +1,7 @@
package com.android.launcher3.shortcuts;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
@@ -17,6 +18,10 @@
super(new ComponentName(packageName, id), user);
}
+ public ShortcutKey(Context context, String componentKeyStr) {
+ super(context, componentKeyStr);
+ }
+
public String getId() {
return componentName.getClassName();
}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
index 995cdaa..da656db 100644
--- a/src/com/android/launcher3/states/SpringLoadedState.java
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -70,7 +70,7 @@
float myCenter = ws.getTop() + halfHeight;
float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
float actualCellTop = myCenter - cellTopFromCenter * scale;
- return new float[] { scale, (desiredCellTop - actualCellTop) / scale};
+ return new float[] { scale, 0, (desiredCellTop - actualCellTop) / scale};
}
@Override
diff --git a/src/com/android/launcher3/util/InstantAppResolver.java b/src/com/android/launcher3/util/InstantAppResolver.java
index 99ce7ca..601a5ab 100644
--- a/src/com/android/launcher3/util/InstantAppResolver.java
+++ b/src/com/android/launcher3/util/InstantAppResolver.java
@@ -18,8 +18,11 @@
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
@@ -44,6 +47,17 @@
return false;
}
+ public boolean isInstantApp(Launcher launcher, String packageName) {
+ PackageManager packageManager = launcher.getPackageManager();
+ try {
+ return isInstantApp(packageManager.getPackageInfo(packageName, 0).applicationInfo);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e("InstantAppResolver", "Failed to determine whether package is instant app "
+ + packageName, e);
+ }
+ return false;
+ }
+
public List<ApplicationInfo> getInstantApps() {
return Collections.emptyList();
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
index d67156f..485e97b 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsState.java
@@ -68,7 +68,7 @@
@Override
public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
- return new float[] { 1f,
+ return new float[] { 1f, 0,
-launcher.getAllAppsController().getShiftRange()
* AllAppsTransitionController.PARALLAX_COEFFICIENT};
}
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
index 73f208e..19967ae 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/OverviewState.java
@@ -59,7 +59,7 @@
int workspaceOffsetTopEdge =
workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
- return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
+ return new float[] {SCALE_FACTOR, 0, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
}
@Override