diff options
Diffstat (limited to 'quickstep/src')
25 files changed, 456 insertions, 209 deletions
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 2e31ef239..6703bb546 100644 --- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java +++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -184,7 +184,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag // before our internal listeners. mLauncher.getStateManager().setCurrentAnimation(anim); - anim.play(getIconAnimator(v)); + Rect windowTargetBounds = getWindowTargetBounds(targetCompats); + anim.play(getIconAnimator(v, windowTargetBounds)); if (launcherClosing) { Pair<AnimatorSet, Runnable> launcherContentAnimator = getLauncherContentAnimator(true /* isAppOpening */); @@ -196,7 +197,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag } }); } - anim.play(getOpeningWindowAnimators(v, targetCompats)); + anim.play(getOpeningWindowAnimators(v, targetCompats, windowTargetBounds)); } if (launcherClosing) { @@ -213,7 +214,26 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat( runner, duration, statusBarTransitionDelay)); } - return getDefaultActivityLaunchOptions(launcher, v); + return super.getActivityLaunchOptions(launcher, v); + } + + /** + * Return the window bounds of the opening target. + * In multiwindow mode, we need to get the final size of the opening app window target to help + * figure out where the floating view should animate to. + */ + private Rect getWindowTargetBounds(RemoteAnimationTargetCompat[] targets) { + Rect bounds = new Rect(0, 0, mDeviceProfile.widthPx, mDeviceProfile.heightPx); + if (mLauncher.isInMultiWindowModeCompat()) { + for (RemoteAnimationTargetCompat target : targets) { + if (target.mode == MODE_OPENING) { + bounds.set(target.sourceContainerBounds); + bounds.offsetTo(target.position.x, target.position.y); + return bounds; + } + } + } + return bounds; } public void setRemoteAnimationProvider(RemoteAnimationProvider animationProvider) { @@ -382,7 +402,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag /** * @return Animator that controls the icon used to launch the target. */ - private AnimatorSet getIconAnimator(View v) { + private AnimatorSet getIconAnimator(View v, Rect windowTargetBounds) { final boolean isBubbleTextView = v instanceof BubbleTextView; mFloatingView = new View(mLauncher); if (isBubbleTextView && v.getTag() instanceof ItemInfoWithIcon ) { @@ -418,7 +438,7 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag viewLocationLeft += rect.left; viewLocationTop += rect.top; int viewLocationStart = mIsRtl - ? mDeviceProfile.widthPx - rect.right + ? windowTargetBounds.width() - rect.right : viewLocationLeft; LayoutParams lp = new LayoutParams(rect.width(), rect.height()); lp.ignoreInsets = true; @@ -438,12 +458,15 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag v.setVisibility(View.INVISIBLE); AnimatorSet appIconAnimatorSet = new AnimatorSet(); - // Animate the app icon to the center - float centerX = mDeviceProfile.widthPx / 2; - float centerY = mDeviceProfile.heightPx / 2; + int[] dragLayerBounds = new int[2]; + mDragLayer.getLocationOnScreen(dragLayerBounds); + + // Animate the app icon to the center of the window bounds in screen coordinates. + float centerX = windowTargetBounds.centerX() - dragLayerBounds[0]; + float centerY = windowTargetBounds.centerY() - dragLayerBounds[1]; float xPosition = mIsRtl - ? mDeviceProfile.widthPx - lp.getMarginStart() - rect.width() + ? windowTargetBounds.width() - lp.getMarginStart() - rect.width() : lp.getMarginStart(); float dX = centerX - xPosition - (lp.width / 2); float dY = centerY - lp.topMargin - (lp.height / 2); @@ -469,8 +492,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag // Scale the app icon to take up the entire screen. This simplifies the math when // animating the app window position / scale. - float maxScaleX = mDeviceProfile.widthPx / (float) rect.width(); - float maxScaleY = mDeviceProfile.heightPx / (float) rect.height(); + float maxScaleX = windowTargetBounds.width() / (float) rect.width(); + float maxScaleY = windowTargetBounds.height() / (float) rect.height(); float scale = Math.max(maxScaleX, maxScaleY); ObjectAnimator scaleAnim = ObjectAnimator .ofFloat(mFloatingView, SCALE_PROPERTY, startScale, scale); @@ -505,7 +528,8 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag /** * @return Animator that controls the window of the opening targets. */ - private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets) { + private ValueAnimator getOpeningWindowAnimators(View v, RemoteAnimationTargetCompat[] targets, + Rect windowTargetBounds) { Rect bounds = new Rect(); if (v.getParent() instanceof DeepShortcutView) { // Deep shortcut views have their icon drawn in a separate view. @@ -544,31 +568,35 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag float iconHeight = bounds.height() * mFloatingView.getScaleY(); // Scale the app window to match the icon size. - float scaleX = iconWidth / mDeviceProfile.widthPx; - float scaleY = iconHeight / mDeviceProfile.heightPx; + float scaleX = iconWidth / windowTargetBounds.width(); + float scaleY = iconHeight / windowTargetBounds.height(); float scale = Math.min(1f, Math.min(scaleX, scaleY)); matrix.setScale(scale, scale); // Position the scaled window on top of the icon - int deviceWidth = mDeviceProfile.widthPx; - int deviceHeight = mDeviceProfile.heightPx; - float scaledWindowWidth = deviceWidth * scale; - float scaledWindowHeight = deviceHeight * scale; + int windowWidth = windowTargetBounds.width(); + int windowHeight = windowTargetBounds.height(); + float scaledWindowWidth = windowWidth * scale; + float scaledWindowHeight = windowHeight * scale; float offsetX = (scaledWindowWidth - iconWidth) / 2; float offsetY = (scaledWindowHeight - iconHeight) / 2; - mFloatingView.getLocationInWindow(floatingViewBounds); + if (mLauncher.isInMultiWindowModeCompat()) { + mFloatingView.getLocationOnScreen(floatingViewBounds); + } else { + mFloatingView.getLocationInWindow(floatingViewBounds); + } float transX0 = floatingViewBounds[0] - offsetX; float transY0 = floatingViewBounds[1] - offsetY; matrix.postTranslate(transX0, transY0); // Animate the window crop so that it starts off as a square, and then reveals // horizontally. - float cropHeight = deviceHeight * easePercent + deviceWidth * (1 - easePercent); - float initialTop = (deviceHeight - deviceWidth) / 2f; + float cropHeight = windowHeight * easePercent + windowWidth * (1 - easePercent); + float initialTop = (windowHeight - windowWidth) / 2f; crop.left = 0; crop.top = (int) (initialTop * (1 - easePercent)); - crop.right = deviceWidth; + crop.right = windowWidth; crop.bottom = (int) (crop.top + cropHeight); TransactionCompat t = new TransactionCompat(); @@ -579,10 +607,6 @@ public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManag for (RemoteAnimationTargetCompat target : targets) { if (target.mode == MODE_OPENING) { t.setAlpha(target.leash, mAlpha.value); - - // TODO: This isn't correct at the beginning of the animation, but better - // than nothing. - matrix.postTranslate(target.position.x, target.position.y); t.setMatrix(target.leash, matrix); t.setWindowCrop(target.leash, crop); t.deferTransactionUntil(target.leash, surface, getNextFrameNumber(surface)); diff --git a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java index d86ba6aa6..1eaa8bc92 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/AllAppsState.java @@ -18,8 +18,6 @@ package com.android.launcher3.uioverrides; import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS; import static com.android.launcher3.anim.Interpolators.DEACCEL_2; -import android.view.View; - import com.android.launcher3.AbstractFloatingView; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; @@ -62,11 +60,6 @@ public class AllAppsState extends LauncherState { } @Override - public View getFinalFocus(Launcher launcher) { - return launcher.getAppsView(); - } - - @Override public float[] getWorkspaceScaleAndTranslation(Launcher launcher) { float[] scaleAndTranslation = LauncherState.OVERVIEW.getWorkspaceScaleAndTranslation( launcher); diff --git a/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java new file mode 100644 index 000000000..2e6dcc0e7 --- /dev/null +++ b/quickstep/src/com/android/launcher3/uioverrides/BackButtonAlphaHandler.java @@ -0,0 +1,62 @@ +/* + * 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.launcher3.uioverrides; + +import android.animation.ValueAnimator; + +import com.android.launcher3.Launcher; +import com.android.launcher3.LauncherState; +import com.android.launcher3.LauncherStateManager; +import com.android.launcher3.anim.AnimatorSetBuilder; +import com.android.quickstep.OverviewInteractionState; + +public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler { + + private static final String TAG = "BackButtonAlphaHandler"; + + private final Launcher mLauncher; + private final OverviewInteractionState mOverviewInteractionState; + + public BackButtonAlphaHandler(Launcher launcher) { + mLauncher = launcher; + mOverviewInteractionState = OverviewInteractionState.getInstance(mLauncher); + } + + @Override + public void setState(LauncherState state) { + UiFactory.onLauncherStateOrFocusChanged(mLauncher); + } + + @Override + public void setStateWithAnimation(LauncherState toState, + AnimatorSetBuilder builder, LauncherStateManager.AnimationConfig config) { + if (!config.playNonAtomicComponent()) { + return; + } + float fromAlpha = mOverviewInteractionState.getBackButtonAlpha(); + float toAlpha = toState.hideBackButton ? 0 : 1; + if (Float.compare(fromAlpha, toAlpha) != 0) { + ValueAnimator anim = ValueAnimator.ofFloat(fromAlpha, toAlpha); + anim.setDuration(config.duration); + anim.addUpdateListener(valueAnimator -> { + final float alpha = (float) valueAnimator.getAnimatedValue(); + mOverviewInteractionState.setBackButtonAlpha(alpha, false); + }); + builder.play(anim); + } + } +} diff --git a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java index a11625a34..43d982230 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/FastOverviewState.java @@ -15,7 +15,13 @@ */ package com.android.launcher3.uioverrides; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Rect; + +import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; +import com.android.launcher3.R; import com.android.quickstep.QuickScrubController; import com.android.quickstep.views.RecentsView; @@ -24,6 +30,12 @@ import com.android.quickstep.views.RecentsView; */ public class FastOverviewState extends OverviewState { + private static final float MAX_PREVIEW_SCALE_UP = 1.3f; + /** + * Vertical transition of the task previews relative to the full container. + */ + public static final float OVERVIEW_TRANSLATION_FACTOR = 0.5f; + private static final int STATE_FLAGS = FLAG_DISABLE_RESTORE | FLAG_DISABLE_INTERACTION | FLAG_OVERVIEW_UI | FLAG_HIDE_BACK_BUTTON | FLAG_DISABLE_ACCESSIBILITY; @@ -45,6 +57,23 @@ public class FastOverviewState extends OverviewState { @Override public float[] getOverviewScaleAndTranslationYFactor(Launcher launcher) { - return new float[] {1f, 0.5f}; + RecentsView recentsView = launcher.getOverviewPanel(); + recentsView.getTaskSize(sTempRect); + + return new float[] {getOverviewScale(launcher.getDeviceProfile(), sTempRect, launcher), + OVERVIEW_TRANSLATION_FACTOR}; + } + + public static float getOverviewScale(DeviceProfile dp, Rect taskRect, Context context) { + if (dp.isVerticalBarLayout()) { + return 1f; + } + + Resources res = context.getResources(); + float usedHeight = taskRect.height() + res.getDimension(R.dimen.task_thumbnail_top_margin); + float usedWidth = taskRect.width() + 2 * (res.getDimension(R.dimen.recents_page_spacing) + + res.getDimension(R.dimen.quickscrub_adjacent_visible_width)); + return Math.min(Math.min(dp.availableHeightPx / usedHeight, + dp.availableWidthPx / usedWidth), MAX_PREVIEW_SCALE_UP); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java index 68773b418..6d1061990 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeEdgeSwipeController.java @@ -72,7 +72,7 @@ public class LandscapeEdgeSwipeController extends AbstractStateChangeTouchContro @Override protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { super.onSwipeInteractionCompleted(targetState, logAction); - if (mFromState == NORMAL && targetState == OVERVIEW) { + if (mStartState == NORMAL && targetState == OVERVIEW) { RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java index 3a49294c5..9169ffbc1 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java +++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java @@ -83,11 +83,6 @@ public class OverviewState extends LauncherState { DiscoveryBounce.showForOverviewIfNeeded(launcher); } - @Override - public View getFinalFocus(Launcher launcher) { - return launcher.getOverviewPanel(); - } - public PageAlphaProvider getWorkspacePageAlphaProvider(Launcher launcher) { return new PageAlphaProvider(DEACCEL_2) { @Override @@ -100,9 +95,9 @@ public class OverviewState extends LauncherState { @Override public int getVisibleElements(Launcher launcher) { if (launcher.getDeviceProfile().isVerticalBarLayout()) { - return 0; + return VERTICAL_SWIPE_INDICATOR; } else { - return HOTSEAT_SEARCH_BOX | + return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | (launcher.getAppsView().getFloatingHeaderView().hasVisibleContent() ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java index 987f952ba..3fb7cd480 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java @@ -202,7 +202,7 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr @Override protected void onSwipeInteractionCompleted(LauncherState targetState, int logAction) { super.onSwipeInteractionCompleted(targetState, logAction); - if (mFromState == NORMAL && targetState == OVERVIEW) { + if (mStartState == NORMAL && targetState == OVERVIEW) { RecentsModel.getInstance(mLauncher).onOverviewShown(true, TAG); } } diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java index e43b24a6a..e3aabd6c8 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java +++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java @@ -15,6 +15,7 @@ */ package com.android.launcher3.uioverrides; +import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY; import static com.android.launcher3.LauncherState.FAST_OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.anim.AnimatorSetBuilder.ANIM_OVERVIEW_FADE; @@ -24,7 +25,6 @@ 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; import android.animation.ValueAnimator; @@ -59,7 +59,7 @@ public class RecentsViewStateController implements StateHandler { public void setState(LauncherState state) { mRecentsViewContainer.setContentAlpha(state.overviewUi ? 1 : 0); float[] scaleTranslationYFactor = state.getOverviewScaleAndTranslationYFactor(mLauncher); - mRecentsView.setAdjacentScale(scaleTranslationYFactor[0]); + SCALE_PROPERTY.set(mRecentsView, scaleTranslationYFactor[0]); mRecentsView.setTranslationYFactor(scaleTranslationYFactor[1]); if (state.overviewUi) { mRecentsView.updateEmptyMessage(); @@ -77,7 +77,7 @@ public class RecentsViewStateController implements StateHandler { PropertySetter setter = config.getPropertySetter(builder); float[] scaleTranslationYFactor = toState.getOverviewScaleAndTranslationYFactor(mLauncher); Interpolator scaleInterpolator = builder.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR); - setter.setFloat(mRecentsView, ADJACENT_SCALE, scaleTranslationYFactor[0], scaleInterpolator); + setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleTranslationYFactor[0], scaleInterpolator); Interpolator transYInterpolator = scaleInterpolator; if (mLauncher.getStateManager().getState() == OVERVIEW && toState == FAST_OVERVIEW) { transYInterpolator = Interpolators.clampToProgress(QUICK_SCRUB_START_INTERPOLATOR, 0, diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index b371677f3..d0c7b2117 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -19,6 +19,7 @@ package com.android.launcher3.uioverrides; import static android.view.View.VISIBLE; import static com.android.launcher3.AbstractFloatingView.TYPE_ALL; import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON; +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; @@ -78,11 +79,13 @@ public class UiFactory { } public static StateHandler[] getStateHandler(Launcher launcher) { - return new StateHandler[] { - launcher.getAllAppsController(), launcher.getWorkspace(), - new RecentsViewStateController(launcher)}; + return new StateHandler[] {launcher.getAllAppsController(), launcher.getWorkspace(), + new RecentsViewStateController(launcher), new BackButtonAlphaHandler(launcher)}; } + /** + * Sets the back button visibility based on the current state/window focus. + */ public static void onLauncherStateOrFocusChanged(Launcher launcher) { boolean shouldBackButtonBeHidden = launcher != null && launcher.getStateManager().getState().hideBackButton @@ -96,10 +99,6 @@ public class UiFactory { .setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */); } - public static void setBackButtonAlpha(Launcher launcher, float alpha, boolean animate) { - OverviewInteractionState.getInstance(launcher).setBackButtonAlpha(alpha,animate); - } - public static void resetOverview(Launcher launcher) { RecentsView recents = launcher.getOverviewPanel(); recents.reset(); @@ -209,7 +208,7 @@ public class UiFactory { public static void prepareToShowOverview(Launcher launcher) { RecentsView overview = launcher.getOverviewPanel(); if (overview.getVisibility() != VISIBLE || overview.getContentAlpha() == 0) { - overview.setAdjacentScale(1.33f); + SCALE_PROPERTY.set(overview, 1.33f); } } diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java index ae0affee0..e202c574a 100644 --- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java +++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java @@ -20,6 +20,8 @@ import static com.android.launcher3.LauncherState.FAST_OVERVIEW; import static com.android.launcher3.LauncherState.OVERVIEW; import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS; import static com.android.launcher3.anim.Interpolators.LINEAR; +import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; +import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_BACK; import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_ROTATION; @@ -48,9 +50,12 @@ import com.android.launcher3.allapps.AllAppsTransitionController; import com.android.launcher3.allapps.DiscoveryBounce; import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.dragndrop.DragLayer; +import com.android.launcher3.uioverrides.FastOverviewState; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.MultiValueAlpha.AlphaProperty; +import com.android.quickstep.TouchConsumer.InteractionType; import com.android.quickstep.util.LayoutUtils; +import com.android.quickstep.util.TransformedRect; import com.android.quickstep.util.RemoteAnimationProvider; import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.views.LauncherLayoutListener; @@ -83,7 +88,8 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { void onTransitionCancelled(T activity, boolean activityVisible); - int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect); + int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, + @InteractionType int interactionType, TransformedRect outRect); void onSwipeUpComplete(T activity); @@ -147,8 +153,8 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { @Override public float getTranslationYForQuickScrub(Launcher activity) { LauncherRecentsView recentsView = activity.getOverviewPanel(); - float transYFactor = FAST_OVERVIEW.getOverviewScaleAndTranslationYFactor(activity)[1]; - return recentsView.computeTranslationYForFactor(transYFactor); + return recentsView.computeTranslationYForFactor( + FastOverviewState.OVERVIEW_TRANSLATION_FACTOR); } @Override @@ -157,14 +163,18 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { } @Override - public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { - LayoutUtils.calculateLauncherTaskSize(context, dp, outRect); + public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, + @InteractionType int interactionType, TransformedRect outRect) { + LayoutUtils.calculateLauncherTaskSize(context, dp, outRect.rect); + if (interactionType == INTERACTION_QUICK_SCRUB) { + outRect.scale = FastOverviewState.getOverviewScale(dp, outRect.rect, context); + } if (dp.isVerticalBarLayout()) { Rect targetInsets = dp.getInsets(); int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; } else { - return dp.heightPx - outRect.bottom; + return dp.heightPx - outRect.rect.bottom; } } @@ -204,9 +214,10 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { return new AnimationFactory() { @Override - public void createActivityController(long transitionLength) { + public void createActivityController(long transitionLength, + @InteractionType int interactionType) { createActivityControllerInternal(activity, activityVisible, startState, - transitionLength, callback); + transitionLength, interactionType, callback); } @Override @@ -218,13 +229,16 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { private void createActivityControllerInternal(Launcher activity, boolean wasVisible, LauncherState startState, long transitionLength, + @InteractionType int interactionType, Consumer<AnimatorPlaybackController> callback) { + LauncherState endState = interactionType == INTERACTION_QUICK_SCRUB + ? FAST_OVERVIEW : OVERVIEW; if (wasVisible) { DeviceProfile dp = activity.getDeviceProfile(); long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx); activity.getStateManager().goToState(startState, false); callback.accept(activity.getStateManager() - .createAnimationToNewWorkspace(OVERVIEW, accuracy)); + .createAnimationToNewWorkspace(endState, accuracy)); return; } @@ -238,7 +252,7 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { float scrollRange = Math.max(controller.getShiftRange(), 1); float progressDelta = (transitionLength / scrollRange); - float endProgress = OVERVIEW.getVerticalProgress(activity); + float endProgress = endState.getVerticalProgress(activity); float startProgress = endProgress + progressDelta; ObjectAnimator shiftAnim = ObjectAnimator.ofFloat( controller, ALL_APPS_PROGRESS, startProgress, endProgress); @@ -381,14 +395,15 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { } @Override - public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) { - LayoutUtils.calculateFallbackTaskSize(context, dp, outRect); + public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, + @InteractionType int interactionType, TransformedRect outRect) { + LayoutUtils.calculateFallbackTaskSize(context, dp, outRect.rect); if (dp.isVerticalBarLayout()) { Rect targetInsets = dp.getInsets(); int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right; return dp.hotseatBarSizePx + dp.hotseatBarSidePaddingPx + hotseatInset; } else { - return dp.heightPx - outRect.bottom; + return dp.heightPx - outRect.rect.bottom; } } @@ -401,7 +416,7 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible, Consumer<AnimatorPlaybackController> callback) { if (activityVisible) { - return (transitionLength) -> { }; + return (transitionLength, interactionType) -> { }; } RecentsViewContainer rv = activity.getOverviewPanelContainer(); @@ -418,11 +433,12 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { rv.setContentAlpha(1); } createActivityController(getSwipeUpDestinationAndLength( - activity.getDeviceProfile(), activity, new Rect())); + activity.getDeviceProfile(), activity, INTERACTION_NORMAL, + new TransformedRect()), INTERACTION_NORMAL); } @Override - public void createActivityController(long transitionLength) { + public void createActivityController(long transitionLength, int interactionType) { if (!isAnimatingHome) { return; } @@ -543,7 +559,7 @@ public interface ActivityControlHelper<T extends BaseDraggingActivity> { default void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) { } - void createActivityController(long transitionLength); + void createActivityController(long transitionLength, @InteractionType int interactionType); default void onTransitionCancelled() { } } diff --git a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java index 23738fb25..9ba332831 100644 --- a/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java +++ b/quickstep/src/com/android/quickstep/OtherActivityTouchConsumer.java @@ -265,12 +265,10 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC if (Looper.myLooper() != Looper.getMainLooper()) { startActivity.run(); - if (!mIsDeferredDownTarget) { - try { - drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS); - } catch (Exception e) { - // We have waited long enough for launcher to draw - } + try { + drawWaitLock.await(LAUNCHER_DRAW_TIMEOUT_MS, TimeUnit.MILLISECONDS); + } catch (Exception e) { + // We have waited long enough for launcher to draw } } else { // We should almost always get touch-town on background thread. This is an edge case @@ -326,10 +324,10 @@ public class OtherActivityTouchConsumer extends ContextWrapper implements TouchC mPassedInitialSlop = true; } - notifyGestureStarted(); if (mInteractionHandler != null) { mInteractionHandler.updateInteractionType(interactionType); } + notifyGestureStarted(); } @Override diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java index 7b2932383..41a45501d 100644 --- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java +++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java @@ -21,6 +21,7 @@ import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; +import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL; import static com.android.systemui.shared.system.ActivityManagerWrapper .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; import static com.android.systemui.shared.system.PackageManagerWrapper @@ -59,6 +60,7 @@ import com.android.quickstep.ActivityControlHelper.AnimationFactory; import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper; import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper; import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.TransformedRect; import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -282,7 +284,7 @@ public class OverviewCommandHelper { }); factory.onRemoteAnimationReceived(null); if (wasVisible) { - factory.createActivityController(RECENTS_LAUNCH_DURATION); + factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL); } mActivity = activity; mRecentsView = mActivity.getOverviewPanel(); @@ -342,9 +344,9 @@ public class OverviewCommandHelper { loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight()); clipHelper.updateSource(homeBounds, runningTaskTarget); - Rect targetRect = new Rect(); - mHelper.getSwipeUpDestinationAndLength( - mActivity.getDeviceProfile(), mActivity, targetRect); + TransformedRect targetRect = new TransformedRect(); + mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity, + INTERACTION_NORMAL, targetRect); clipHelper.updateTargetRect(targetRect); clipHelper.prepareAnimation(false /* isOpening */); diff --git a/quickstep/src/com/android/quickstep/OverviewInteractionState.java b/quickstep/src/com/android/quickstep/OverviewInteractionState.java index d60574676..922a7ff29 100644 --- a/quickstep/src/com/android/quickstep/OverviewInteractionState.java +++ b/quickstep/src/com/android/quickstep/OverviewInteractionState.java @@ -91,6 +91,7 @@ public class OverviewInteractionState { // These are updated on the background thread private ISystemUiProxy mISystemUiProxy; private boolean mSwipeUpEnabled = true; + private float mBackButtonAlpha = 1; private Runnable mOnSwipeUpSettingChangedListener; @@ -117,7 +118,14 @@ public class OverviewInteractionState { return mSwipeUpEnabled; } + public float getBackButtonAlpha() { + return mBackButtonAlpha; + } + public void setBackButtonAlpha(float alpha, boolean animate) { + if (!mSwipeUpEnabled) { + alpha = 1; + } mUiHandler.removeMessages(MSG_SET_BACK_BUTTON_ALPHA); mUiHandler.obtainMessage(MSG_SET_BACK_BUTTON_ALPHA, animate ? 1 : 0, 0, alpha) .sendToTarget(); @@ -128,6 +136,9 @@ public class OverviewInteractionState { } private boolean handleUiMessage(Message msg) { + if (msg.what == MSG_SET_BACK_BUTTON_ALPHA) { + mBackButtonAlpha = (float) msg.obj; + } mBgHandler.obtainMessage(msg.what, msg.arg1, msg.arg2, msg.obj).sendToTarget(); return true; } diff --git a/quickstep/src/com/android/quickstep/QuickScrubController.java b/quickstep/src/com/android/quickstep/QuickScrubController.java index abb479dea..8e1a3d5d8 100644 --- a/quickstep/src/com/android/quickstep/QuickScrubController.java +++ b/quickstep/src/com/android/quickstep/QuickScrubController.java @@ -49,7 +49,7 @@ public class QuickScrubController implements OnAlarmListener { * Snap to a new page when crossing these thresholds. The first and last auto-advance. */ private static final float[] QUICK_SCRUB_THRESHOLDS = new float[] { - 0.04f, 0.27f, 0.50f, 0.73f, 0.96f + 0.05f, 0.20f, 0.35f, 0.50f, 0.65f, 0.80f, 0.95f }; private static final String TAG = "QuickScrubController"; diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java index b472d611a..3adb290bb 100644 --- a/quickstep/src/com/android/quickstep/RecentsActivity.java +++ b/quickstep/src/com/android/quickstep/RecentsActivity.java @@ -176,8 +176,8 @@ public class RecentsActivity extends BaseDraggingActivity { } @Override - public ActivityOptions getActivityLaunchOptions(final View v, boolean useDefaultLaunchOptions) { - if (useDefaultLaunchOptions || !(v instanceof TaskView)) { + public ActivityOptions getActivityLaunchOptions(final View v) { + if (!(v instanceof TaskView)) { return null; } diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java index 84b217648..191c237f6 100644 --- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java +++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java @@ -65,6 +65,7 @@ import com.android.quickstep.ActivityControlHelper.AnimationFactory; import com.android.quickstep.ActivityControlHelper.LayoutListener; import com.android.quickstep.TouchConsumer.InteractionType; import com.android.quickstep.util.ClipAnimationHelper; +import com.android.quickstep.util.TransformedRect; import com.android.quickstep.util.RemoteAnimationTargetSet; import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.TaskView; @@ -181,7 +182,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { private LayoutListener mLayoutListener; private RecentsView mRecentsView; private QuickScrubController mQuickScrubController; - private AnimationFactory mAnimationFactory = (t) -> { }; + private AnimationFactory mAnimationFactory = (t, i) -> { }; private Runnable mLauncherDrawnCallback; @@ -272,8 +273,8 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { | STATE_SCALED_CONTROLLER_APP, this::notifyTransitionCancelled); - mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START, - this::onQuickScrubStart); + mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START + | STATE_APP_CONTROLLER_RECEIVED, this::onQuickScrubStart); mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_QUICK_SCRUB_START | STATE_SCALED_CONTROLLER_RECENTS, this::onFinishedTransitionToQuickScrub); mStateCallback.addCallback(STATE_LAUNCHER_STARTED | STATE_CURRENT_TASK_FINISHED @@ -302,9 +303,9 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { private void initTransitionEndpoints(DeviceProfile dp) { mDp = dp; - Rect tempRect = new Rect(); + TransformedRect tempRect = new TransformedRect(); mTransitionDragLength = mActivityControlHelper - .getSwipeUpDestinationAndLength(dp, mContext, tempRect); + .getSwipeUpDestinationAndLength(dp, mContext, mInteractionType, tempRect); mClipAnimationHelper.updateTargetRect(tempRect); } @@ -488,7 +489,7 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { */ public void buildAnimationController() { initTransitionEndpoints(mActivity.getDeviceProfile()); - mAnimationFactory.createActivityController(mTransitionDragLength); + mAnimationFactory.createActivityController(mTransitionDragLength, mInteractionType); } private void onAnimatorPlaybackControllerCreated(AnimatorPlaybackController anim) { @@ -793,13 +794,16 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity> { int scrollForFirstTask = mRecentsView.getScrollForPage(0); int scrollForSecondTask = mRecentsView.getChildCount() > 1 ? mRecentsView.getScrollForPage(1) : scrollForFirstTask; - int offsetFromFirstTask = scrollForFirstTask - scrollForSecondTask; - final float interpolation; - if (mRecentsView.getWidth() == 0) { - interpolation = scrollForSecondTask == scrollForFirstTask ? 0 : 1; - } else { - interpolation = (float) offsetFromFirstTask / (mRecentsView.getWidth() / 2); - } + float offsetFromFirstTask = scrollForFirstTask - scrollForSecondTask; + + TransformedRect tempRect = new TransformedRect(); + mActivityControlHelper + .getSwipeUpDestinationAndLength(mDp, mContext, mInteractionType, tempRect); + float distanceToReachEdge = mDp.widthPx / 2 + tempRect.rect.width() / 2 + + mContext.getResources().getDimensionPixelSize(R.dimen.recents_page_spacing); + float interpolation = Math.min(1, + Math.abs(offsetFromFirstTask) / distanceToReachEdge); + mClipAnimationHelper.offsetTarget( firstTask.getCurveScaleForInterpolation(interpolation), offsetFromFirstTask, mActivityControlHelper.getTranslationYForQuickScrub(mActivity), diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java index 9e2de3395..c5d74c7d9 100644 --- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java +++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java @@ -20,8 +20,10 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityNodeInfo; import com.android.launcher3.DeviceProfile; +import com.android.launcher3.R; import com.android.quickstep.RecentsActivity; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.views.RecentsView; @@ -71,4 +73,21 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity> { // Just use the activity task size for multi-window as well. return false; } + + @Override + public void addTaskAccessibilityActionsExtra(AccessibilityNodeInfo info) { + info.addAction( + new AccessibilityNodeInfo.AccessibilityAction( + R.string.recents_clear_all, + getContext().getText(R.string.recents_clear_all))); + } + + @Override + public boolean performTaskAccessibilityActionExtra(int action) { + if (action == R.string.recents_clear_all) { + dismissAllTasks(); + return true; + } + return false; + } } diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java index 8c7f104a6..a654482f9 100644 --- a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java +++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java @@ -80,6 +80,7 @@ public class ClipAnimationHelper { private final RectF mTmpRectF = new RectF(); private float mTargetScale = 1f; + private float mOffsetScale = 1f; private Interpolator mInterpolator = LINEAR; // We translate y slightly faster than the rest of the animation for quick scrub. private Interpolator mOffsetYInterpolator = LINEAR; @@ -106,11 +107,13 @@ public class ClipAnimationHelper { updateSourceStack(target); } - public void updateTargetRect(Rect targetRect) { + public void updateTargetRect(TransformedRect targetRect) { + mOffsetScale = targetRect.scale; mSourceRect.set(mSourceInsets.left, mSourceInsets.top, mSourceStackBounds.width() - mSourceInsets.right, mSourceStackBounds.height() - mSourceInsets.bottom); - mTargetRect.set(targetRect); + mTargetRect.set(targetRect.rect); + Utilities.scaleRectFAboutCenter(mTargetRect, targetRect.scale); mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left, mHomeStackBounds.top - mSourceStackBounds.top); @@ -145,7 +148,8 @@ public class ClipAnimationHelper { synchronized (mTargetOffset) { // Stay lined up with the center of the target, since it moves for quick scrub. - currentRect.offset(mTargetOffset.x * progress, mTargetOffset.y * offsetYProgress); + currentRect.offset(mTargetOffset.x * mOffsetScale * progress, + mTargetOffset.y * offsetYProgress); } mClipRect.left = (int) (mSourceWindowClipInsets.left * progress); @@ -219,16 +223,20 @@ public class ClipAnimationHelper { mSourceInsets.set(activity.getDeviceProfile().getInsets()); } - Rect targetRect = new Rect(); - dl.getDescendantRectRelativeToSelf(ttv, targetRect); + TransformedRect targetRect = new TransformedRect(); + dl.getDescendantRectRelativeToSelf(ttv, targetRect.rect); updateTargetRect(targetRect); - // Transform the clip relative to the target rect. - float scale = mTargetRect.width() / mSourceRect.width(); - mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale; - mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale; - mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale; - mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale; + if (target == null) { + // Transform the clip relative to the target rect. Only do this in the case where we + // aren't applying the insets to the app windows (where the clip should be in target app + // space) + float scale = mTargetRect.width() / mSourceRect.width(); + mSourceWindowClipInsets.left = mSourceWindowClipInsets.left * scale; + mSourceWindowClipInsets.top = mSourceWindowClipInsets.top * scale; + mSourceWindowClipInsets.right = mSourceWindowClipInsets.right * scale; + mSourceWindowClipInsets.bottom = mSourceWindowClipInsets.bottom * scale; + } } private void updateStackBoundsToMultiWindowTaskSize(BaseDraggingActivity activity) { diff --git a/quickstep/src/com/android/quickstep/util/TransformedRect.java b/quickstep/src/com/android/quickstep/util/TransformedRect.java new file mode 100644 index 000000000..79f11e405 --- /dev/null +++ b/quickstep/src/com/android/quickstep/util/TransformedRect.java @@ -0,0 +1,32 @@ +/* + * 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.util; + +import android.graphics.Rect; + +/** + * A wrapper around {@link Rect} with additional transformation properties + */ +public class TransformedRect { + + public final Rect rect = new Rect(); + public float scale = 1; + + public void set(TransformedRect transformedRect) { + rect.set(transformedRect.rect); + scale = transformedRect.scale; + } +} diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java index d5c43a0f5..25e3dc6c1 100644 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java @@ -19,6 +19,7 @@ package com.android.quickstep.views; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import android.content.Context; +import android.graphics.Rect; import android.os.Bundle; import android.support.annotation.Nullable; import android.util.AttributeSet; @@ -43,4 +44,12 @@ public class ClearAllButton extends Button { } return res; } + + @Override + protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { + super.onFocusChanged(focused, direction, previouslyFocusedRect); + if (focused) { + mRecentsView.revealClearAllButton(); + } + } } diff --git a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java index 950f7fb99..5aca4f326 100644 --- a/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java +++ b/quickstep/src/com/android/quickstep/views/LauncherRecentsView.java @@ -35,6 +35,9 @@ import android.view.ViewDebug; import com.android.launcher3.DeviceProfile; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherState; +import com.android.launcher3.R; +import com.android.launcher3.anim.Interpolators; +import com.android.launcher3.views.ScrimView; import com.android.quickstep.OverviewInteractionState; import com.android.quickstep.util.ClipAnimationHelper; import com.android.quickstep.util.LayoutUtils; @@ -136,6 +139,12 @@ public class LauncherRecentsView extends RecentsView<Launcher> { } anim.play(ObjectAnimator.ofFloat( mActivity.getAllAppsController(), ALL_APPS_PROGRESS, allAppsProgressOffscreen)); + + ObjectAnimator dragHandleAnim = ObjectAnimator.ofInt( + mActivity.findViewById(R.id.scrim_view), ScrimView.DRAG_HANDLE_ALPHA, 0); + dragHandleAnim.setInterpolator(Interpolators.ACCEL_2); + anim.play(dragHandleAnim); + return anim; } @@ -150,7 +159,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> { mActivity.getStateManager().goToState(NORMAL, false /* animate */); } else { LauncherState state = mActivity.getStateManager().getState(); - mActivity.getAllAppsController().setProgress(state.getVerticalProgress(mActivity)); + mActivity.getAllAppsController().setState(state); } super.onTaskLaunched(success); } diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index a6da89f20..dee15d0d7 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -41,18 +41,21 @@ import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.UserHandle; +import android.support.annotation.Nullable; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.ArraySet; import android.util.AttributeSet; -import android.util.FloatProperty; import android.util.SparseBooleanArray; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewDebug; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.ListView; import com.android.launcher3.BaseActivity; import com.android.launcher3.DeviceProfile; @@ -96,19 +99,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private final Rect mTempRect = new Rect(); - public static final FloatProperty<RecentsView> ADJACENT_SCALE = - new FloatProperty<RecentsView>("adjacentScale") { - @Override - public void setValue(RecentsView recentsView, float v) { - recentsView.setAdjacentScale(v); - } - - @Override - public Float get(RecentsView recentsView) { - return recentsView.mAdjacentScale; - } - }; - public static final boolean FLIP_RECENTS = true; private static final int DISMISS_TASK_DURATION = 300; // The threshold at which we update the SystemUI flags when animating from the task into the app private static final float UPDATE_SYSUI_FLAGS_THRESHOLD = 0.6f; @@ -119,6 +109,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private final QuickScrubController mQuickScrubController; private final float mFastFlingVelocity; private final RecentsModel mModel; + private final int mTaskTopMargin; private final ScrollState mScrollState = new ScrollState(); // Keeps track of the previously known visible tasks for purposes of loading/unloading task data @@ -229,8 +220,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl @ViewDebug.ExportedProperty(category = "launcher") private float mContentAlpha = 1; - @ViewDebug.ExportedProperty(category = "launcher") - private float mAdjacentScale = 1; // Keeps track of task views whose visual state should not be reset private ArraySet<TaskView> mIgnoreResetTaskViews = new ArraySet<>(); @@ -266,11 +255,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl mQuickScrubController = new QuickScrubController(mActivity, this); mModel = RecentsModel.getInstance(context); - mIsRtl = Utilities.isRtl(getResources()); - if (FLIP_RECENTS) { - mIsRtl = !mIsRtl; - } + mIsRtl = !Utilities.isRtl(getResources()); setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR); + mTaskTopMargin = getResources() + .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); mEmptyIcon = context.getDrawable(R.drawable.ic_empty_recents); mEmptyIcon.setCallback(this); @@ -283,6 +271,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl .getDimensionPixelSize(R.dimen.recents_empty_message_text_padding); setWillNotDraw(false); updateEmptyMessage(); + setFocusable(false); } public boolean isRtl() { @@ -518,8 +507,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl DeviceProfile dp = mActivity.getDeviceProfile(); getTaskSize(dp, mTempRect); - mTempRect.top -= getResources() - .getDimensionPixelSize(R.dimen.task_thumbnail_top_margin); + mTempRect.top -= mTaskTopMargin; setPadding(mTempRect.left - mInsets.left, mTempRect.top - mInsets.top, dp.availableWidthPx + mInsets.left - mTempRect.right, dp.availableHeightPx + mInsets.top - mTempRect.bottom); @@ -581,6 +569,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl * and unloads the associated task data for tasks that are no longer visible. */ public void loadVisibleTaskData() { + if (!mOverviewStateEnabled) { + // Skip loading visible task data if we've already left the overview state + return; + } + RecentsTaskLoader loader = mModel.getRecentsTaskLoader(); int centerPageIndex = getPageNearestToCenterOfScreen(); int lower = Math.max(0, centerPageIndex - 2); @@ -927,21 +920,16 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl set.play(anim); } - private void snapToPageRelative(int delta) { + private boolean snapToPageRelative(int delta, boolean cycle) { if (getPageCount() == 0) { - return; + return false; } - snapToPage((getNextPage() + getPageCount() + delta) % getPageCount()); - } - - @Override - public void onVisibilityAggregated(boolean isVisible) { - super.onVisibilityAggregated(isVisible); - if (isVisible && !isFocused()) { - // Having focus, even in touch mode, keeps us from losing [Alt+]Tab by preventing - // switching to keyboard mode. - requestFocus(); + final int newPageUnbound = getNextPage() + delta; + if (!cycle && (newPageUnbound < 0 || newPageUnbound >= getChildCount())) { + return false; } + snapToPage((newPageUnbound + getPageCount()) % getPageCount()); + return true; } private void runDismissAnimation(PendingAnimation pendingAnim) { @@ -967,14 +955,12 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_TAB: - snapToPageRelative(event.isShiftPressed() ? -1 : 1); - return true; + return snapToPageRelative(event.isShiftPressed() ? -1 : 1, + event.isAltPressed() /* cycle */); case KeyEvent.KEYCODE_DPAD_RIGHT: - snapToPageRelative(mIsRtl ? -1 : 1); - return true; + return snapToPageRelative(mIsRtl ? -1 : 1, false /* cycle */); case KeyEvent.KEYCODE_DPAD_LEFT: - snapToPageRelative(mIsRtl ? 1 : -1); - return true; + return snapToPageRelative(mIsRtl ? 1 : -1, false /* cycle */); case KeyEvent.KEYCODE_DEL: case KeyEvent.KEYCODE_FORWARD_DEL: dismissTask((TaskView) getChildAt(getNextPage()), true /*animateTaskView*/, @@ -992,8 +978,22 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl return super.dispatchKeyEvent(event); } - public void snapToTaskAfterNext() { - snapToPageRelative(1); + @Override + protected void onFocusChanged(boolean gainFocus, int direction, + @Nullable Rect previouslyFocusedRect) { + super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); + if (gainFocus && getChildCount() > 0) { + switch (direction) { + case FOCUS_FORWARD: + setCurrentPage(0); + break; + case FOCUS_BACKWARD: + case FOCUS_RIGHT: + case FOCUS_LEFT: + setCurrentPage(getChildCount() - 1); + break; + } + } } public float getContentAlpha() { @@ -1016,36 +1016,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl updateClearAllButtonAlpha(); } - public void setAdjacentScale(float adjacentScale) { - if (mAdjacentScale == adjacentScale) { - return; - } - mAdjacentScale = adjacentScale; - TaskView currTask = getPageAt(mCurrentPage); - if (currTask == null) { - return; - } - currTask.setZoomScale(mAdjacentScale); - - if (mCurrentPage - 1 >= 0) { - TaskView adjacentTask = getPageAt(mCurrentPage - 1); - float[] scaleAndTranslation = getAdjacentScaleAndTranslation(currTask, adjacentTask, - mAdjacentScale, 0); - adjacentTask.setZoomScale(scaleAndTranslation[0]); - adjacentTask.setTranslationX(-scaleAndTranslation[1]); - adjacentTask.setTranslationY(scaleAndTranslation[2]); - } - if (mCurrentPage + 1 < getChildCount()) { - TaskView adjacentTask = getPageAt(mCurrentPage + 1); - float[] scaleAndTranslation = getAdjacentScaleAndTranslation(currTask, adjacentTask, - mAdjacentScale, 0); - adjacentTask.setZoomScale(scaleAndTranslation[0]); - adjacentTask.setTranslationX(scaleAndTranslation[1]); - adjacentTask.setTranslationY(scaleAndTranslation[2]); - } - } - - private float[] getAdjacentScaleAndTranslation(TaskView currTask, TaskView adjacentTask, + private float[] getAdjacentScaleAndTranslation(TaskView currTask, float currTaskToScale, float currTaskToTranslationY) { float displacement = currTask.getWidth() * (currTaskToScale - currTask.getCurveScale()); sTempFloatArray[0] = currTaskToScale; @@ -1058,7 +1029,6 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl public void onViewAdded(View child) { super.onViewAdded(child); child.setAlpha(mContentAlpha); - setAdjacentScale(mAdjacentScale); onChildViewsChanged(); } @@ -1084,6 +1054,12 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); updateEmptyStateUi(changed); + + // Set the pivot points to match the task preview center + setPivotY(((mInsets.top + getPaddingTop() + mTaskTopMargin) + + (getHeight() - mInsets.bottom - getPaddingBottom())) / 2); + setPivotX(((mInsets.left + getPaddingLeft()) + + (getWidth() - mInsets.right - getPaddingRight())) / 2); } private void updateEmptyStateUi(boolean sizeChanged) { @@ -1154,14 +1130,14 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl if (taskIndex - 1 >= 0) { TaskView adjacentTask = getPageAt(taskIndex - 1); float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask, - adjacentTask, toScale, toTranslationY); + toScale, toTranslationY); scaleAndTranslation[1] = -scaleAndTranslation[1]; anim.play(createAnimForChild(adjacentTask, scaleAndTranslation)); } if (taskIndex + 1 < getPageCount()) { TaskView adjacentTask = getPageAt(taskIndex + 1); float[] scaleAndTranslation = getAdjacentScaleAndTranslation(centerTask, - adjacentTask, toScale, toTranslationY); + toScale, toTranslationY); anim.play(createAnimForChild(adjacentTask, scaleAndTranslation)); } } else { @@ -1300,25 +1276,59 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl private void onChildViewsChanged() { final int childCount = getChildCount(); mClearAllButton.setVisibility(childCount == 0 ? INVISIBLE : VISIBLE); + setFocusable(childCount != 0); } public void revealClearAllButton() { + setCurrentPage(getChildCount() - 1); // Loads tasks info if needed. scrollTo(mIsRtl ? 0 : computeMaxScrollX(), 0); } @Override public void addChildrenForAccessibility(ArrayList<View> outChildren) { - if (FLIP_RECENTS) { - for (int i = getChildCount() - 1; i >= 0; --i) { - outChildren.add(getChildAt(i)); - } - } else { - super.addChildrenForAccessibility(outChildren); + for (int i = getChildCount() - 1; i >= 0; --i) { + outChildren.add(getChildAt(i)); } } @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + + final AccessibilityNodeInfo.CollectionInfo + collectionInfo = AccessibilityNodeInfo.CollectionInfo.obtain( + 1, getChildCount(), false, + AccessibilityNodeInfo.CollectionInfo.SELECTION_MODE_NONE); + info.setCollectionInfo(collectionInfo); + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) { + final int visiblePageNumber = getChildCount() - getCurrentPage() - 1; + event.setFromIndex(visiblePageNumber); + event.setToIndex(visiblePageNumber); + event.setItemCount(getChildCount()); + } + } + + @Override + public CharSequence getAccessibilityClassName() { + // To hear position-in-list related feedback from Talkback. + return ListView.class.getName(); + } + + @Override protected boolean isPageOrderFlipped() { - return FLIP_RECENTS; + return true; + } + + public void addTaskAccessibilityActionsExtra(AccessibilityNodeInfo info) { + } + + public boolean performTaskAccessibilityActionExtra(int action) { + return false; } } diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java index 429432b2f..31c8b6448 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java +++ b/quickstep/src/com/android/quickstep/views/RecentsViewContainer.java @@ -23,12 +23,14 @@ import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.util.FloatProperty; -import android.view.Gravity; import android.view.MotionEvent; +import android.view.View; import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.R; +import java.util.ArrayList; + public class RecentsViewContainer extends InsettableFrameLayout { public static final FloatProperty<RecentsViewContainer> CONTENT_ALPHA = new FloatProperty<RecentsViewContainer>("contentAlpha") { @@ -64,14 +66,18 @@ public class RecentsViewContainer extends InsettableFrameLayout { }); mRecentsView = findViewById(R.id.overview_panel); - final InsettableFrameLayout.LayoutParams params = - (InsettableFrameLayout.LayoutParams) mClearAllButton.getLayoutParams(); - params.gravity = Gravity.TOP | (RecentsView.FLIP_RECENTS ? Gravity.START : Gravity.END); - mClearAllButton.setLayoutParams(params); mClearAllButton.forceHasOverlappingRendering(false); mRecentsView.setClearAllButton(mClearAllButton); mClearAllButton.setRecentsView(mRecentsView); + + if (mRecentsView.isRtl()) { + mClearAllButton.setNextFocusRightId(mRecentsView.getId()); + mRecentsView.setNextFocusLeftId(mClearAllButton.getId()); + } else { + mClearAllButton.setNextFocusLeftId(mRecentsView.getId()); + mRecentsView.setNextFocusRightId(mClearAllButton.getId()); + } } @Override @@ -104,4 +110,18 @@ public class RecentsViewContainer extends InsettableFrameLayout { mRecentsView.setContentAlpha(alpha); setVisibility(alpha > 0 ? VISIBLE : GONE); } + + @Override + public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { + if (mRecentsView.getChildCount() > 0) { + // Carousel is first in tab order. + views.add(mRecentsView); + views.add(mClearAllButton); + } + } + + public boolean requestFocus(int direction, Rect previouslyFocusedRect) { + return mRecentsView.requestFocus(direction, previouslyFocusedRect) || + super.requestFocus(direction, previouslyFocusedRect); + } }
\ No newline at end of file diff --git a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java index 24afd4868..aca8351a0 100644 --- a/quickstep/src/com/android/quickstep/views/ShelfScrimView.java +++ b/quickstep/src/com/android/quickstep/views/ShelfScrimView.java @@ -154,15 +154,6 @@ public class ShelfScrimView extends ScrimView { } @Override - protected void updateDragHandleAlpha() { - if (mDrawingFlatColor) { - super.updateDragHandleAlpha(); - } else if (mDragHandle != null) { - mDragHandle.setAlpha(255); - } - } - - @Override protected void onDraw(Canvas canvas) { float translate = drawBackground(canvas); diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 82aa45a18..5413a1319 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -17,6 +17,7 @@ package com.android.quickstep.views; import static android.widget.Toast.LENGTH_SHORT; + import static com.android.quickstep.views.TaskThumbnailView.DIM_ALPHA_MULTIPLIER; import android.animation.Animator; @@ -116,7 +117,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback } launchTask(true /* animate */); BaseActivity.fromContext(context).getUserEventDispatcher().logTaskLaunchOrDismiss( - Touch.TAP, Direction.NONE, ((RecentsView) getParent()).indexOfChild(this), + Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this), TaskUtils.getComponentKeyForTask(getTask().key)); }); setOutlineProvider(new TaskOutlineProvider(getResources())); @@ -168,7 +169,7 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback final ActivityOptions opts; if (animate) { opts = BaseDraggingActivity.fromContext(getContext()) - .getActivityLaunchOptions(this, false); + .getActivityLaunchOptions(this); } else { opts = ActivityOptions.makeCustomAnimation(getContext(), 0, 0); } @@ -318,12 +319,21 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback context.getText(menuOption.labelResId))); } } + + final RecentsView recentsView = getRecentsView(); + recentsView.addTaskAccessibilityActionsExtra(info); + + final AccessibilityNodeInfo.CollectionItemInfo itemInfo = + AccessibilityNodeInfo.CollectionItemInfo.obtain( + 0, 1, recentsView.getChildCount() - recentsView.indexOfChild(this) - 1, 1, + false); + info.setCollectionItemInfo(itemInfo); } @Override public boolean performAccessibilityAction(int action, Bundle arguments) { if (action == R.string.accessibility_close_task) { - ((RecentsView) getParent()).dismissTask(this, true /*animateTaskView*/, + getRecentsView().dismissTask(this, true /*animateTaskView*/, true /*removeTask*/); return true; } @@ -339,9 +349,15 @@ public class TaskView extends FrameLayout implements TaskCallbacks, PageCallback } } + if (getRecentsView().performTaskAccessibilityActionExtra(action)) return true; + return super.performAccessibilityAction(action, arguments); } + private RecentsView getRecentsView() { + return (RecentsView) getParent(); + } + public void notifyTaskLaunchFailed(String tag) { String msg = "Failed to launch task"; if (mTask != null) { |