diff --git a/proguard.flags b/proguard.flags index 987fb6f124..b8cade5726 100644 --- a/proguard.flags +++ b/proguard.flags @@ -97,6 +97,11 @@ # support jar. -keep class android.support.v7.widget.RecyclerView { *; } +# LauncherAppTransitionManager +-keep class com.android.launcher3.LauncherAppTransitionManagerImpl { + public (...); +} + -keep interface com.android.launcher3.userevent.nano.LauncherLogProto.** { *; } diff --git a/quickstep/res/values/override.xml b/quickstep/res/values/override.xml new file mode 100644 index 0000000000..ba99d81c07 --- /dev/null +++ b/quickstep/res/values/override.xml @@ -0,0 +1,20 @@ + + + + + com.android.launcher3.LauncherAppTransitionManagerImpl + + diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java similarity index 79% rename from quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java rename to quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java index 47179c5a02..9968ca72c8 100644 --- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManager.java +++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java @@ -26,6 +26,8 @@ import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; +import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Matrix; @@ -55,11 +57,15 @@ import com.android.systemui.shared.system.WindowManagerWrapper; /** * Manages the opening and closing app transitions from Launcher. */ -public class LauncherAppTransitionManager { +public class LauncherAppTransitionManagerImpl extends LauncherAppTransitionManager { private static final String TAG = "LauncherTransition"; private static final int REFRESH_RATE_MS = 16; + private static final String CONTROL_REMOTE_APP_TRANSITION_PERMISSION = + "android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"; + + private static final int LAUNCHER_RESUME_START_DELAY = 150; private static final int CLOSING_TRANSITION_DURATION_MS = 350; // Progress = 0: All apps is fully pulled up, Progress = 1: All apps is fully pulled down. @@ -76,60 +82,79 @@ public class LauncherAppTransitionManager { private ImageView mFloatingView; private boolean mIsRtl; - public LauncherAppTransitionManager(Launcher launcher) { - mLauncher = launcher; - mDragLayer = launcher.getDragLayer(); - mDeviceProfile = launcher.getDeviceProfile(); + private Animator mCurrentAnimator; - mIsRtl = Utilities.isRtl(launcher.getResources()); + public LauncherAppTransitionManagerImpl(Context context) { + mLauncher = Launcher.getLauncher(context); + mDragLayer = mLauncher.getDragLayer(); + mDeviceProfile = mLauncher.getDeviceProfile(); - Resources res = launcher.getResources(); + mIsRtl = Utilities.isRtl(mLauncher.getResources()); + + Resources res = mLauncher.getResources(); mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y); mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y); } + private void setCurrentAnimator(Animator animator) { + if (mCurrentAnimator != null && mCurrentAnimator.isRunning()) { + mCurrentAnimator.cancel(); + } + mCurrentAnimator = animator; + } + /** * @return A Bundle with remote animations that controls how the window of the opening * targets are displayed. */ - public Bundle getActivityLauncherOptions(View v) { - RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) { - @Override - public void onAnimationStart(RemoteAnimationTargetCompat[] targets, - Runnable finishedCallback) { - // Post at front of queue ignoring sync barriers to make sure it gets processed - // before the next frame. - postAtFrontOfQueueAsynchronously(v.getHandler(), () -> { - mAnimator = new AnimatorSet(); - mAnimator.play(getLauncherAnimators(v)); - mAnimator.play(getWindowAnimators(v, targets)); - mAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - // Reset launcher to normal state - v.setVisibility(View.VISIBLE); - ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView); + @Override + public Bundle getActivityLaunchOptions(Launcher launcher, View v) { + if (hasControlRemoteAppTransitionPermission()) { + try { + RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mLauncher) { + @Override + public void onAnimationStart(RemoteAnimationTargetCompat[] targets, + Runnable finishedCallback) { + // Post at front of queue ignoring sync barriers to make sure it gets + // processed before the next frame. + postAtFrontOfQueueAsynchronously(v.getHandler(), () -> { + mAnimator = new AnimatorSet(); + setCurrentAnimator(mAnimator); + mAnimator.play(getLauncherAnimators(v)); + mAnimator.play(getWindowAnimators(v, targets)); + mAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + // Reset launcher to normal state + v.setVisibility(View.VISIBLE); + ((ViewGroup) mDragLayer.getParent()).removeView(mFloatingView); - mDragLayer.setAlpha(1f); - mDragLayer.setTranslationY(0f); + mDragLayer.setAlpha(1f); + mDragLayer.setTranslationY(0f); - View appsView = mLauncher.getAppsView(); - appsView.setAlpha(1f); - appsView.setTranslationY(0f); + View appsView = mLauncher.getAppsView(); + appsView.setAlpha(1f); + appsView.setTranslationY(0f); - finishedCallback.run(); - } - }); - mAnimator.start(); - // Because t=0 has the app icon in its original spot, we can skip the first - // frame and have the same movement one frame earlier. - mAnimator.setCurrentPlayTime(REFRESH_RATE_MS); - }); + finishedCallback.run(); + } + }); + mAnimator.start(); + // Because t=0 has the app icon in its original spot, we can skip the + // first frame and have the same movement one frame earlier. + mAnimator.setCurrentPlayTime(REFRESH_RATE_MS); + }); + } + }; + + return ActivityOptionsCompat.makeRemoteAnimation( + new RemoteAnimationAdapterCompat(runner, 500, 380)).toBundle(); + } catch (NoClassDefFoundError e) { + // Gracefully fall back to default launch options if the user's platform doesn't + // have the latest changes. } - }; - - return ActivityOptionsCompat.makeRemoteAnimation( - new RemoteAnimationAdapterCompat(runner, 500, 380)).toBundle(); + } + return getDefaultActivityLaunchOptions(launcher, v); } /** @@ -149,7 +174,7 @@ public class LauncherAppTransitionManager { * Else: Animate the content so that it moves downwards and fades out. */ private AnimatorSet getLauncherContentAnimator(boolean show) { - AnimatorSet hideLauncher = new AnimatorSet(); + AnimatorSet launcherAnimator = new AnimatorSet(); float[] alphas = show ? new float[] {0, 1} @@ -161,6 +186,9 @@ public class LauncherAppTransitionManager { if (mLauncher.isInState(LauncherState.ALL_APPS) && !mDeviceProfile.isVerticalBarLayout()) { // All Apps in portrait mode is full screen, so we only animate AllAppsContainerView. View appsView = mLauncher.getAppsView(); + appsView.setAlpha(alphas[0]); + appsView.setTranslationY(trans[0]); + ObjectAnimator alpha = ObjectAnimator.ofFloat(appsView, View.ALPHA, alphas); alpha.setDuration(217); alpha.setInterpolator(Interpolators.LINEAR); @@ -168,9 +196,12 @@ public class LauncherAppTransitionManager { transY.setInterpolator(Interpolators.AGGRESSIVE_EASE); transY.setDuration(350); - hideLauncher.play(alpha); - hideLauncher.play(transY); + launcherAnimator.play(alpha); + launcherAnimator.play(transY); } else { + mDragLayer.setAlpha(alphas[0]); + mDragLayer.setTranslationY(trans[0]); + ObjectAnimator dragLayerAlpha = ObjectAnimator.ofFloat(mDragLayer, View.ALPHA, alphas); dragLayerAlpha.setDuration(217); dragLayerAlpha.setInterpolator(Interpolators.LINEAR); @@ -179,10 +210,10 @@ public class LauncherAppTransitionManager { dragLayerTransY.setInterpolator(Interpolators.AGGRESSIVE_EASE); dragLayerTransY.setDuration(350); - hideLauncher.play(dragLayerAlpha); - hideLauncher.play(dragLayerTransY); + launcherAnimator.play(dragLayerAlpha); + launcherAnimator.play(dragLayerTransY); } - return hideLauncher; + return launcherAnimator; } /** @@ -361,15 +392,22 @@ public class LauncherAppTransitionManager { /** * Registers remote animations used when closing apps to home screen. */ + @Override public void registerRemoteAnimations() { - RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat(); - definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN, - new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(), 0, - CLOSING_TRANSITION_DURATION_MS)); + if (hasControlRemoteAppTransitionPermission()) { + try { + RemoteAnimationDefinitionCompat definition = new RemoteAnimationDefinitionCompat(); + definition.addRemoteAnimation(WindowManagerWrapper.TRANSIT_WALLPAPER_OPEN, + new RemoteAnimationAdapterCompat(getWallpaperOpenRunner(), 0, + CLOSING_TRANSITION_DURATION_MS)); // TODO: App controlled transition for unlock to home TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER - new ActivityCompat(mLauncher).registerRemoteAnimations(definition); + new ActivityCompat(mLauncher).registerRemoteAnimations(definition); + } catch (NoClassDefFoundError e) { + // Gracefully fall back if the user's platform doesn't have the latest changes + } + } } /** @@ -385,11 +423,13 @@ public class LauncherAppTransitionManager { postAtFrontOfQueueAsynchronously(handler, () -> { // We use a separate transition for Overview mode. if (mLauncher.isInState(LauncherState.OVERVIEW)) { + setCurrentAnimator(null); finishedCallback.run(); return; } mAnimator = new AnimatorSet(); + setCurrentAnimator(mAnimator); mAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -465,7 +505,9 @@ public class LauncherAppTransitionManager { private AnimatorSet getLauncherResumeAnimation() { if (mLauncher.isInState(LauncherState.ALL_APPS) || mLauncher.getDeviceProfile().isVerticalBarLayout()) { - return getLauncherContentAnimator(true /* show */); + AnimatorSet contentAnimator = getLauncherContentAnimator(true /* show */); + contentAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY); + return contentAnimator; } else { AnimatorSet workspaceAnimator = new AnimatorSet(); mLauncher.getWorkspace().setTranslationY(mWorkspaceTransY); @@ -474,7 +516,7 @@ public class LauncherAppTransitionManager { View.TRANSLATION_Y, mWorkspaceTransY, 0)); workspaceAnimator.play(ObjectAnimator.ofFloat(mLauncher.getWorkspace(), View.ALPHA, 0, 1f)); - workspaceAnimator.setStartDelay(150); + workspaceAnimator.setStartDelay(LAUNCHER_RESUME_START_DELAY); workspaceAnimator.setDuration(333); workspaceAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); @@ -489,7 +531,7 @@ public class LauncherAppTransitionManager { Animator allAppsSlideIn = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS, startY, slideEnd); - allAppsSlideIn.setStartDelay(150); + allAppsSlideIn.setStartDelay(LAUNCHER_RESUME_START_DELAY); allAppsSlideIn.setDuration(317); allAppsSlideIn.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); @@ -505,6 +547,11 @@ public class LauncherAppTransitionManager { } } + private boolean hasControlRemoteAppTransitionPermission() { + return mLauncher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION) + == PackageManager.PERMISSION_GRANTED; + } + /** * Helper method that allows us to get interpolated values for embedded * animations with a delay and/or different duration. diff --git a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java index a004dacded..b4f40c2151 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java +++ b/quickstep/src/com/android/launcher3/uioverrides/UiFactory.java @@ -25,7 +25,6 @@ import android.view.View; import android.view.View.AccessibilityDelegate; import com.android.launcher3.Launcher; -import com.android.launcher3.LauncherAppTransitionManager; import com.android.launcher3.LauncherStateManager.StateHandler; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.graphics.BitmapRenderer; @@ -84,31 +83,4 @@ public class UiFactory { RecentsView recents = launcher.getOverviewPanel(); recents.reset(); } - - private static boolean hasControlRemoteAppTransitionPermission(Launcher launcher) { - return launcher.checkSelfPermission(CONTROL_REMOTE_APP_TRANSITION_PERMISSION) - == PackageManager.PERMISSION_GRANTED; - } - - public static Bundle getActivityLaunchOptions(Launcher launcher, View v) { - if (hasControlRemoteAppTransitionPermission(launcher)) { - try { - return new LauncherAppTransitionManager(launcher).getActivityLauncherOptions(v); - } catch (NoClassDefFoundError e) { - // Gracefully fall back to default launch options if the user's platform doesn't - // have the latest changes. - } - } - return launcher.getDefaultActivityLaunchOptions(v); - } - - public static void registerRemoteAnimations(Launcher launcher) { - if (hasControlRemoteAppTransitionPermission(launcher)) { - try { - new LauncherAppTransitionManager(launcher).registerRemoteAnimations(); - } catch (NoClassDefFoundError e) { - // Gracefully fall back if the user's platform doesn't have the latest changes - } - } - } } diff --git a/res/values/config.xml b/res/values/config.xml index 2096200385..3dddac22d8 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -92,6 +92,9 @@ + + + diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 5db0a3b72d..a91907d44e 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -214,6 +214,8 @@ public class Launcher extends BaseActivity private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500; + private LauncherAppTransitionManager mAppTransitionManager; + @Thunk Workspace mWorkspace; private View mLauncherView; @Thunk DragLayer mDragLayer; @@ -403,8 +405,11 @@ public class Launcher extends BaseActivity getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW, Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText)); + mAppTransitionManager = Utilities.getOverrideObject(LauncherAppTransitionManager.class, + this, R.string.app_transition_manager_class); + if (!isInMultiWindowModeCompat()) { - UiFactory.registerRemoteAnimations(this); + mAppTransitionManager.registerRemoteAnimations(); } if (mLauncherCallbacks != null) { @@ -1918,38 +1923,11 @@ public class Launcher extends BaseActivity } } - public Bundle getDefaultActivityLaunchOptions(View v) { - if (Utilities.ATLEAST_MARSHMALLOW) { - int left = 0, top = 0; - int width = v.getMeasuredWidth(), height = v.getMeasuredHeight(); - if (v instanceof BubbleTextView) { - // Launch from center of icon, not entire view - Drawable icon = ((BubbleTextView) v).getIcon(); - if (icon != null) { - Rect bounds = icon.getBounds(); - left = (width - bounds.width()) / 2; - top = v.getPaddingTop(); - width = bounds.width(); - height = bounds.height(); - } - } - return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height) - .toBundle(); - } else if (Utilities.ATLEAST_LOLLIPOP_MR1) { - // On L devices, we use the device default slide-up transition. - // On L MR1 devices, we use a custom version of the slide-up transition which - // doesn't have the delay present in the device default. - return ActivityOptions.makeCustomAnimation( - this, R.anim.task_open_enter, R.anim.no_anim).toBundle(); - } - return null; - } - @TargetApi(Build.VERSION_CODES.M) public Bundle getActivityLaunchOptions(View v, boolean useDefaultLaunchOptions) { return useDefaultLaunchOptions - ? getDefaultActivityLaunchOptions(v) - : UiFactory.getActivityLaunchOptions(this, v); + ? mAppTransitionManager.getDefaultActivityLaunchOptions(this, v) + : mAppTransitionManager.getActivityLaunchOptions(this, v); } public Rect getViewBounds(View v) { diff --git a/src/com/android/launcher3/LauncherAppTransitionManager.java b/src/com/android/launcher3/LauncherAppTransitionManager.java new file mode 100644 index 0000000000..9d68dc9e8f --- /dev/null +++ b/src/com/android/launcher3/LauncherAppTransitionManager.java @@ -0,0 +1,64 @@ +/* + * 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; + + +import android.app.ActivityOptions; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; + +/** + * Manages the opening and closing app transitions from Launcher. + */ +public class LauncherAppTransitionManager { + + public Bundle getDefaultActivityLaunchOptions(Launcher launcher, View v) { + if (Utilities.ATLEAST_MARSHMALLOW) { + int left = 0, top = 0; + int width = v.getMeasuredWidth(), height = v.getMeasuredHeight(); + if (v instanceof BubbleTextView) { + // Launch from center of icon, not entire view + Drawable icon = ((BubbleTextView) v).getIcon(); + if (icon != null) { + Rect bounds = icon.getBounds(); + left = (width - bounds.width()) / 2; + top = v.getPaddingTop(); + width = bounds.width(); + height = bounds.height(); + } + } + return ActivityOptions.makeClipRevealAnimation(v, left, top, width, height) + .toBundle(); + } else if (Utilities.ATLEAST_LOLLIPOP_MR1) { + // On L devices, we use the device default slide-up transition. + // On L MR1 devices, we use a custom version of the slide-up transition which + // doesn't have the delay present in the device default. + return ActivityOptions.makeCustomAnimation(launcher, R.anim.task_open_enter, + R.anim.no_anim).toBundle(); + } + return null; + } + + public Bundle getActivityLaunchOptions(Launcher launcher, View v) { + return getDefaultActivityLaunchOptions(launcher, v); + } + + public void registerRemoteAnimations() { + } +} diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java index c857bf6a07..744125e157 100644 --- a/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java +++ b/src_ui_overrides/com/android/launcher3/uioverrides/UiFactory.java @@ -61,10 +61,4 @@ public class UiFactory { } public static void resetOverview(Launcher launcher) { } - - public static Bundle getActivityLaunchOptions(Launcher launcher, View v) { - return launcher.getDefaultActivityLaunchOptions(v); - } - - public static void registerRemoteAnimations(Launcher launcher) { } }