diff options
59 files changed, 3414 insertions, 597 deletions
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 488fb180723e..12571b1ef521 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -912,6 +912,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + if (!st.hasPanelItems()) { + // Ensure that |st.decorView| has its actual content. Otherwise, an empty window can be + // created and cause ANR. + return; + } + st.isHandled = false; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( diff --git a/libs/WindowManager/Shell/res/color/taskbar_background.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml index 329e5b9b31a0..b3d260299106 100644 --- a/libs/WindowManager/Shell/res/color/taskbar_background.xml +++ b/libs/WindowManager/Shell/res/color/taskbar_background.xml @@ -14,6 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> +<!-- Should be the same as in packages/apps/Launcher3/res/color-v31/taskbar_background.xml --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@android:color/system_neutral1_500" android:lStar="35" /> + <item android:color="@android:color/system_neutral1_500" android:lStar="15" /> </selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml b/libs/WindowManager/Shell/res/color/unfold_transition_background.xml deleted file mode 100644 index 63289a3f75d9..000000000000 --- a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 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. ---> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Matches taskbar color --> - <item android:color="@android:color/system_neutral2_500" android:lStar="35" /> -</selector> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 62fb840d29d1..ecdccd7b1419 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -34,6 +34,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.UnfoldTransitionHandler; @@ -131,6 +132,13 @@ public class ShellInitImpl { if (Transitions.ENABLE_SHELL_TRANSITIONS) { mTransitions.register(mShellTaskOrganizer); mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init); + if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) { + final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions, + mPipTouchHandlerOptional.get().getTransitionHandler(), + mSplitScreenOptional.get().getTransitionHandler()); + // Added at end so that it has highest priority. + mTransitions.addHandler(mixedHandler); + } } // TODO(b/181599115): This should really be the pip controller, but until we can provide the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 4ad08688bd51..24d500434c45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -319,26 +319,27 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController( - @DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController, + @DynamicOverride Lazy<Optional<FullscreenUnfoldController>> fullscreenUnfoldController, Optional<ShellUnfoldProgressProvider> progressProvider) { if (progressProvider.isPresent() && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) { - return fullscreenUnfoldController; + return fullscreenUnfoldController.get(); } return Optional.empty(); } + @BindsOptionalOf + @DynamicOverride + abstract UnfoldTransitionHandler optionalUnfoldTransitionHandler(); + @WMSingleton @Provides static Optional<UnfoldTransitionHandler> provideUnfoldTransitionHandler( Optional<ShellUnfoldProgressProvider> progressProvider, - TransactionPool transactionPool, - Transitions transitions, - @ShellMainThread ShellExecutor executor) { - if (progressProvider.isPresent()) { - return Optional.of( - new UnfoldTransitionHandler(progressProvider.get(), transactionPool, executor, - transitions)); + @DynamicOverride Lazy<Optional<UnfoldTransitionHandler>> handler) { + if (progressProvider.isPresent() + && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) { + return handler.get(); } return Optional.empty(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 965bd26da158..4fa5a1029e28 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -70,6 +70,8 @@ import com.android.wm.shell.splitscreen.StageTaskUnfoldController; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.UnfoldBackgroundController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; +import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import java.util.Optional; @@ -344,15 +346,37 @@ public class WMShellModule { @Provides @DynamicOverride static FullscreenUnfoldController provideFullscreenUnfoldController( - Context context, Optional<ShellUnfoldProgressProvider> progressProvider, - Lazy<UnfoldBackgroundController> unfoldBackgroundController, - DisplayInsetsController displayInsetsController, + Optional<UnfoldTransitionHandler> unfoldTransitionHandler, + FullscreenUnfoldTaskAnimator fullscreenUnfoldTaskAnimator, + UnfoldBackgroundController unfoldBackgroundController, @ShellMainThread ShellExecutor mainExecutor ) { - return new FullscreenUnfoldController(context, mainExecutor, - unfoldBackgroundController.get(), progressProvider.get(), - displayInsetsController); + return new FullscreenUnfoldController(mainExecutor, + unfoldBackgroundController, progressProvider.get(), + unfoldTransitionHandler.get(), fullscreenUnfoldTaskAnimator); + } + + @Provides + static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator( + Context context, + DisplayInsetsController displayInsetsController + ) { + return new FullscreenUnfoldTaskAnimator(context, displayInsetsController); + } + + @WMSingleton + @Provides + @DynamicOverride + static UnfoldTransitionHandler provideUnfoldTransitionHandler( + Optional<ShellUnfoldProgressProvider> progressProvider, + FullscreenUnfoldTaskAnimator animator, + UnfoldBackgroundController backgroundController, + TransactionPool transactionPool, + Transitions transitions, + @ShellMainThread ShellExecutor executor) { + return new UnfoldTransitionHandler(progressProvider.get(), animator, + transactionPool, backgroundController, executor, transitions); } @Provides diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 73e6cba43ec0..1fc1215b6cea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -76,6 +76,8 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent)); + mAnimatableTasksListener.onTaskAppeared(taskInfo); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; mSyncQueue.runInSync(t -> { // Reset several properties back to fullscreen (PiP, for example, leaves all these @@ -87,15 +89,15 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { t.show(leash); }); - mAnimatableTasksListener.onTaskAppeared(taskInfo); updateRecentsForVisibleFullscreenTask(taskInfo); } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { + mAnimatableTasksListener.onTaskInfoChanged(taskInfo); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; - mAnimatableTasksListener.onTaskInfoChanged(taskInfo); updateRecentsForVisibleFullscreenTask(taskInfo); final TaskData data = mDataByTaskId.get(taskInfo.taskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java index aa3868cfca84..99f15f65f74e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java @@ -17,213 +17,119 @@ package com.android.wm.shell.fullscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.util.MathUtils.lerp; -import static android.view.Display.DEFAULT_DISPLAY; -import android.animation.RectEvaluator; -import android.animation.TypeEvaluator; import android.annotation.NonNull; import android.app.ActivityManager; -import android.app.TaskInfo; -import android.content.Context; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.util.SparseArray; -import android.view.InsetsSource; -import android.view.InsetsState; import android.view.SurfaceControl; -import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; import com.android.wm.shell.unfold.UnfoldBackgroundController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; +import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import java.util.concurrent.Executor; /** * Controls full screen app unfold transition: animating cropping window and scaling when * folding or unfolding a foldable device. + * + * - When Shell transitions are disabled (legacy mode) this controller animates task surfaces + * when doing both fold and unfold. + * + * - When Shell transitions are enabled this controller animates the surfaces only when + * folding a foldable device. It's not done as a shell transition because we are not committed + * to the display size WM changes yet. + * In this case unfolding is handled by + * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler}. */ -public final class FullscreenUnfoldController implements UnfoldListener, - OnInsetsChangedListener { - - private static final float[] FLOAT_9 = new float[9]; - private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); - - private static final float HORIZONTAL_START_MARGIN = 0.08f; - private static final float VERTICAL_START_MARGIN = 0.03f; - private static final float END_SCALE = 1f; - private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2; +public final class FullscreenUnfoldController implements UnfoldListener { private final Executor mExecutor; private final ShellUnfoldProgressProvider mProgressProvider; - private final DisplayInsetsController mDisplayInsetsController; - - private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); private final UnfoldBackgroundController mBackgroundController; - - private InsetsSource mTaskbarInsetsSource; - - private final float mWindowCornerRadiusPx; - private final float mExpandedTaskBarHeight; - private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); + private final FullscreenUnfoldTaskAnimator mAnimator; + private final UnfoldTransitionHandler mUnfoldTransitionHandler; + + private boolean mShouldHandleAnimation = false; public FullscreenUnfoldController( - @NonNull Context context, @NonNull Executor executor, @NonNull UnfoldBackgroundController backgroundController, @NonNull ShellUnfoldProgressProvider progressProvider, - @NonNull DisplayInsetsController displayInsetsController + @NonNull UnfoldTransitionHandler unfoldTransitionHandler, + @NonNull FullscreenUnfoldTaskAnimator animator ) { mExecutor = executor; mProgressProvider = progressProvider; - mDisplayInsetsController = displayInsetsController; - mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); - mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.taskbar_frame_height); mBackgroundController = backgroundController; + mUnfoldTransitionHandler = unfoldTransitionHandler; + mAnimator = animator; } /** * Initializes the controller */ public void init() { + mAnimator.init(); mProgressProvider.addListener(mExecutor, this); - mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); + } + + @Override + public void onStateChangeStarted() { + mShouldHandleAnimation = !mUnfoldTransitionHandler.willHandleTransition(); } @Override public void onStateChangeProgress(float progress) { - if (mAnimationContextByTaskId.size() == 0) return; + if (!mAnimator.hasActiveTasks() || !mShouldHandleAnimation) return; mBackgroundController.ensureBackground(mTransaction); - - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - final AnimationContext context = mAnimationContextByTaskId.valueAt(i); - - context.mCurrentCropRect.set(RECT_EVALUATOR - .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); - - float scale = lerp(START_SCALE, END_SCALE, progress); - context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(), - context.mCurrentCropRect.exactCenterY()); - - mTransaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) - .setMatrix(context.mLeash, context.mMatrix, FLOAT_9) - .setCornerRadius(context.mLeash, mWindowCornerRadiusPx); - } - + mAnimator.applyAnimationProgress(progress, mTransaction); mTransaction.apply(); } @Override public void onStateChangeFinished() { - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - final AnimationContext context = mAnimationContextByTaskId.valueAt(i); - resetSurface(context); + if (!mShouldHandleAnimation) { + return; } + mShouldHandleAnimation = false; + mAnimator.resetAllSurfaces(mTransaction); mBackgroundController.removeBackground(mTransaction); mTransaction.apply(); } - @Override - public void insetsChanged(InsetsState insetsState) { - mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - AnimationContext context = mAnimationContextByTaskId.valueAt(i); - context.update(mTaskbarInsetsSource, context.mTaskInfo); - } - } - /** * Called when a new matching task appeared */ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource, - taskInfo); - mAnimationContextByTaskId.put(taskInfo.taskId, animationContext); + mAnimator.addTask(taskInfo, leash); } /** * Called when matching task changed */ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); - if (animationContext != null) { - animationContext.update(mTaskbarInsetsSource, taskInfo); - } + mAnimator.onTaskInfoChanged(taskInfo); } /** * Called when matching task vanished */ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); - if (animationContext != null) { - // PiP task has its own cleanup path, ignore surface reset to avoid conflict. - if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) { - resetSurface(animationContext); - } - mAnimationContextByTaskId.remove(taskInfo.taskId); + // PiP task has its own cleanup path, ignore surface reset to avoid conflict. + if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) { + mAnimator.resetSurface(taskInfo, mTransaction); } + mAnimator.removeTask(taskInfo); - if (mAnimationContextByTaskId.size() == 0) { + if (!mAnimator.hasActiveTasks()) { mBackgroundController.removeBackground(mTransaction); } mTransaction.apply(); } - - private void resetSurface(AnimationContext context) { - mTransaction - .setWindowCrop(context.mLeash, null) - .setCornerRadius(context.mLeash, 0.0F) - .setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F) - .setPosition(context.mLeash, - (float) context.mTaskInfo.positionInParent.x, - (float) context.mTaskInfo.positionInParent.y); - } - - private class AnimationContext { - final SurfaceControl mLeash; - final Rect mStartCropRect = new Rect(); - final Rect mEndCropRect = new Rect(); - final Rect mCurrentCropRect = new Rect(); - final Matrix mMatrix = new Matrix(); - - TaskInfo mTaskInfo; - - private AnimationContext(SurfaceControl leash, - InsetsSource taskBarInsetsSource, - TaskInfo taskInfo) { - this.mLeash = leash; - update(taskBarInsetsSource, taskInfo); - } - - private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) { - mTaskInfo = taskInfo; - mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds()); - - if (taskBarInsetsSource != null) { - // Only insets the cropping window with task bar when it's expanded - if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { - mStartCropRect.inset(taskBarInsetsSource - .calculateVisibleInsets(mStartCropRect)); - } - } - - mEndCropRect.set(mStartCropRect); - - int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN); - mStartCropRect.left = mEndCropRect.left + horizontalMargin; - mStartCropRect.right = mEndCropRect.right - horizontalMargin; - int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN); - mStartCropRect.top = mEndCropRect.top + verticalMargin; - mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin; - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b6635f3b8dd7..6d0aad09fcea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -295,6 +295,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, displayController.addDisplayWindowListener(this); } + public PipTransitionController getTransitionController() { + return mPipTransitionController; + } + public Rect getCurrentOrAnimatingBounds() { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getCurrentAnimator(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 48df28ee4cde..5d17f85bb2ca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; -import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; @@ -52,6 +51,7 @@ import android.graphics.Rect; import android.os.IBinder; import android.view.Surface; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; @@ -217,8 +217,9 @@ public class PipTransition extends PipTransitionController { } // Entering PIP. - if (isEnteringPip(info, mCurrentPipTaskToken)) { - return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback); + if (isEnteringPip(info)) { + startEnterAnimation(info, startTransaction, finishTransaction, finishCallback); + return true; } // For transition that we don't animate, but contains the PIP leash, we need to update the @@ -245,16 +246,9 @@ public class PipTransition extends PipTransitionController { @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - if (request.getType() == TRANSIT_PIP) { + if (requestHasPipEnter(request)) { WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - mRequestedEnterTransition = transition; - mRequestedEnterTask = request.getTriggerTask().token; - wct.setActivityWindowingMode(request.getTriggerTask().token, - WINDOWING_MODE_UNDEFINED); - final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - wct.setBounds(request.getTriggerTask().token, destinationBounds); - } + augmentRequest(transition, request, wct); return wct; } else { return null; @@ -262,6 +256,22 @@ public class PipTransition extends PipTransitionController { } @Override + public void augmentRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { + if (!requestHasPipEnter(request)) { + throw new IllegalStateException("Called PiP augmentRequest when request has no PiP"); + } + if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mRequestedEnterTransition = transition; + mRequestedEnterTask = request.getTriggerTask().token; + outWCT.setActivityWindowingMode(request.getTriggerTask().token, + WINDOWING_MODE_UNDEFINED); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + outWCT.setBounds(request.getTriggerTask().token, destinationBounds); + } + } + + @Override public boolean handleRotateDisplay(int startRotation, int endRotation, WindowContainerTransaction wct) { if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) { @@ -559,92 +569,94 @@ public class PipTransition extends PipTransitionController { } /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */ - private static boolean isEnteringPip(@NonNull TransitionInfo info, - @Nullable WindowContainerToken currentPipTaskToken) { + private boolean isEnteringPip(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() != null - && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED - && !change.getContainer().equals(currentPipTaskToken)) { - // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps - // that enter PiP instantly on opening, mostly from CTS/Flicker tests) - if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) { - return true; - } - // This can happen if the request to enter PIP happens when we are collecting for - // another transition, such as TRANSIT_CHANGE (display rotation). - if (info.getType() == TRANSIT_CHANGE) { - return true; - } + if (isEnteringPip(change, info.getType())) return true; + } + return false; + } - // Please file a bug to handle the unexpected transition type. - throw new IllegalStateException("Entering PIP with unexpected transition type=" - + transitTypeToString(info.getType())); + /** Whether a particular change is a window that is entering pip. */ + @Override + public boolean isEnteringPip(@NonNull TransitionInfo.Change change, + @WindowManager.TransitionType int transitType) { + if (change.getTaskInfo() != null + && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED + && !change.getContainer().equals(mCurrentPipTaskToken)) { + // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps + // that enter PiP instantly on opening, mostly from CTS/Flicker tests) + if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN) { + return true; + } + // This can happen if the request to enter PIP happens when we are collecting for + // another transition, such as TRANSIT_CHANGE (display rotation). + if (transitType == TRANSIT_CHANGE) { + return true; } + + // Please file a bug to handle the unexpected transition type. + throw new IllegalStateException("Entering PIP with unexpected transition type=" + + transitTypeToString(transitType)); } return false; } - private boolean startEnterAnimation(@NonNull TransitionInfo info, + private void startEnterAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - // Search for an Enter PiP transition (along with a show wallpaper one) + // Search for an Enter PiP transition TransitionInfo.Change enterPip = null; - TransitionInfo.Change wallpaper = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getTaskInfo() != null && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) { enterPip = change; - } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { - wallpaper = change; } } if (enterPip == null) { - return false; + throw new IllegalStateException("Trying to start PiP animation without a pip" + + "participant"); } - // Keep track of the PIP task. - mCurrentPipTaskToken = enterPip.getContainer(); - mHasFadeOut = false; - if (mFinishCallback != null) { - callFinishCallback(null /* wct */); - mFinishTransaction = null; - throw new RuntimeException("Previous callback not called, aborting entering PIP."); - } - - // Show the wallpaper if there is a wallpaper change. - if (wallpaper != null) { - startTransaction.show(wallpaper.getLeash()); - startTransaction.setAlpha(wallpaper.getLeash(), 1.f); - } // Make sure other open changes are visible as entering PIP. Some may be hidden in // Transitions#setupStartState because the transition type is OPEN (such as auto-enter). for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - if (change == enterPip || change == wallpaper) { - continue; - } + if (change == enterPip) continue; if (isOpeningType(change.getMode())) { final SurfaceControl leash = change.getLeash(); startTransaction.show(leash).setAlpha(leash, 1.f); } } + startEnterAnimation(enterPip, startTransaction, finishTransaction, finishCallback); + } + + @Override + public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange, + @NonNull final SurfaceControl.Transaction startTransaction, + @NonNull final SurfaceControl.Transaction finishTransaction, + @NonNull final Transitions.TransitionFinishCallback finishCallback) { + if (mFinishCallback != null) { + callFinishCallback(null /* wct */); + mFinishTransaction = null; + throw new RuntimeException("Previous callback not called, aborting entering PIP."); + } + + // Keep track of the PIP task and animation. + mCurrentPipTaskToken = pipChange.getContainer(); + mHasFadeOut = false; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; mFinishTransaction = finishTransaction; - final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation(); - return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), - startTransaction, finishTransaction, enterPip.getStartRotation(), - endRotation); - } - private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, - final SurfaceControl.Transaction startTransaction, - final SurfaceControl.Transaction finishTransaction, - final int startRotation, final int endRotation) { + final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo(); + final SurfaceControl leash = pipChange.getLeash(); + final int startRotation = pipChange.getStartRotation(); + final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation(); + setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams, taskInfo.topActivityInfo); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); @@ -657,7 +669,6 @@ public class PipTransition extends PipTransitionController { computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, destinationBounds, sourceHintRect); } - PipAnimationController.PipTransitionAnimator animator; // Set corner radius for entering pip. mSurfaceTransactionHelper .crop(finishTransaction, leash, destinationBounds) @@ -694,7 +705,7 @@ public class PipTransition extends PipTransitionController { null /* callback */, false /* withStartDelay */); } mPipTransitionState.setInSwipePipToHomeTransition(false); - return true; + return; } if (rotationDelta != Surface.ROTATION_0) { @@ -702,6 +713,12 @@ public class PipTransition extends PipTransitionController { tmpTransform.postRotate(rotationDelta); startTransaction.setMatrix(leash, tmpTransform, new float[9]); } + if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + startTransaction.setAlpha(leash, 0f); + } + startTransaction.apply(); + + PipAnimationController.PipTransitionAnimator animator; if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, @@ -712,7 +729,6 @@ public class PipTransition extends PipTransitionController { animator.setUseContentOverlay(mContext); } } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - startTransaction.setAlpha(leash, 0f); animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); mOneShotAnimationType = ANIM_TYPE_BOUNDS; @@ -720,7 +736,6 @@ public class PipTransition extends PipTransitionController { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } - startTransaction.apply(); animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration); @@ -731,8 +746,6 @@ public class PipTransition extends PipTransitionController { animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); } animator.start(); - - return true; } /** Computes destination bounds in old rotation and updates source hint rect if available. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 24993c621e3c..28418be40c37 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -17,6 +17,7 @@ package com.android.wm.shell.pip; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.WindowManager.TRANSIT_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; @@ -28,10 +29,16 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import androidx.annotation.NonNull; + import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.transition.Transitions; @@ -206,6 +213,30 @@ public abstract class PipTransitionController implements Transitions.TransitionH return false; } + /** @return whether the transition-request represents a pip-entry. */ + public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) { + return request.getType() == TRANSIT_PIP; + } + + /** Whether a particular change is a window that is entering pip. */ + public boolean isEnteringPip(@NonNull TransitionInfo.Change change, + @WindowManager.TransitionType int transitType) { + return false; + } + + /** Add PiP-related changes to `outWCT` for the given request. */ + public void augmentRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { + throw new IllegalStateException("Request isn't entering PiP"); + } + + /** Play a transition animation for entering PiP on a specific PiP change. */ + public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange, + @NonNull final SurfaceControl.Transaction startTransaction, + @NonNull final SurfaceControl.Transaction finishTransaction, + @NonNull final Transitions.TransitionFinishCallback finishCallback) { + } + /** * Callback interface for PiP transitions (both from and to PiP mode) */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index ac7b9033b2b9..a2ff97247189 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -54,6 +54,7 @@ import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -250,6 +251,10 @@ public class PipTouchHandler { }); } + public PipTransitionController getTransitionHandler() { + return mPipTaskOrganizer.getTransitionController(); + } + private void reloadResources() { final Resources res = mContext.getResources(); mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 10dfdc30d0d6..6c0bed038650 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -108,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, static final int EXIT_REASON_ROOT_TASK_VANISHED = 6; static final int EXIT_REASON_SCREEN_LOCKED = 7; static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8; - static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; + public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; @IntDef(value = { EXIT_REASON_UNKNOWN, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, @@ -198,6 +198,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.isSplitScreenVisible(); } + public StageCoordinator getTransitionHandler() { + return mStageCoordinator; + } + @Nullable public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) { if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index cd121ed41fdd..4ff9d48a3707 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; @@ -58,9 +57,6 @@ import java.util.ArrayList; class SplitScreenTransitions { private static final String TAG = "SplitScreenTransitions"; - /** Flag applied to a transition change to identify it as a divider bar for animation. */ - public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; - private final TransactionPool mTransactionPool; private final Transitions mTransitions; private final Runnable mOnFinish; @@ -187,27 +183,28 @@ class SplitScreenTransitions { } /** Starts a transition to dismiss split. */ - IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct, + IBinder startDismissTransition(WindowContainerTransaction wct, Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop, @SplitScreenController.ExitReason int reason) { final int type = reason == EXIT_REASON_DRAG_DIVIDER ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS; - if (transition == null) { - transition = mTransitions.startTransition(type, wct, handler); - } + IBinder transition = mTransitions.startTransition(type, wct, handler); + setDismissTransition(transition, dismissTop, reason); + return transition; + } + + /** Sets a transition to dismiss split. */ + void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop, + @SplitScreenController.ExitReason int reason) { mPendingDismiss = new DismissTransition(transition, reason, dismissTop); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Dismiss due to %s. toTop=%s", exitReasonToString(reason), stageTypeToString(dismissTop)); - return transition; } - IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct, - Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) { - if (transition == null) { - transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler); - } + void setRecentTransition(@NonNull IBinder transition, + @Nullable RemoteTransition remoteTransition) { mPendingRecent = transition; if (remoteTransition != null) { @@ -219,7 +216,6 @@ class SplitScreenTransitions { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Enter recent panel"); - return transition; } void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 9d6e34ddd9d7..bb434e26bb0a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -28,6 +28,7 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; +import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; @@ -47,7 +48,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN; import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; -import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; @@ -127,12 +127,15 @@ import javax.inject.Provider; * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} */ -class StageCoordinator implements SplitLayout.SplitLayoutHandler, +public class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler, ShellTaskOrganizer.TaskListener { private static final String TAG = StageCoordinator.class.getSimpleName(); + /** Flag applied to a transition change to identify it as a divider bar for animation. */ + public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; + private final SurfaceSession mSurfaceSession = new SurfaceSession(); private final MainStage mMainStage; @@ -636,7 +639,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); - mSplitTransitions.startDismissTransition(null /* transition */, wct, this, + mSplitTransitions.startDismissTransition(wct, this, mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); } else { exitSplitScreen( @@ -666,8 +669,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); - mSplitTransitions.startDismissTransition(null /* transition */, wct, this, - dismissTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); + mSplitTransitions.startDismissTransition(wct, this, dismissTop, + EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); } } } @@ -729,10 +732,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, .setWindowCrop(mSideStage.mRootLeash, null); }); - // Hide divider and reset its position. - mSplitLayout.resetDividerPosition(); - mSplitLayout.release(); - mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; + onTransitionAnimationComplete(); Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason)); // Log the exit if (childrenToTop != null) { @@ -1128,8 +1128,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); - mSplitTransitions.startDismissTransition( - null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER); + mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER); } @Override @@ -1318,7 +1317,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable TransitionRequestInfo request) { final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); if (triggerTask == null) { - if (mMainStage.isActive()) { + if (isSplitActive()) { + // Check if the display is rotating. final TransitionRequestInfo.DisplayChange displayChange = request.getDisplayChange(); if (request.getType() == TRANSIT_CHANGE && displayChange != null @@ -1345,7 +1345,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId)); } - if (mMainStage.isActive()) { + if (isSplitActive()) { // Try to handle everything while in split-screen, so return a WCT even if it's empty. ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split" + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d" @@ -1360,7 +1360,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; prepareExitSplitScreen(dismissTop, out); - mSplitTransitions.startDismissTransition(transition, out, this, dismissTop, + mSplitTransitions.setDismissTransition(transition, dismissTop, EXIT_REASON_APP_FINISHED); } } else if (isOpening && inFullscreen) { @@ -1370,12 +1370,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } else if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { // Enter overview panel, so start recent transition. - mSplitTransitions.startRecentTransition(transition, out, this, + mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition()); } else { // Occluded by the other fullscreen task, so dismiss both. prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out); - mSplitTransitions.startDismissTransition(transition, out, this, + mSplitTransitions.setDismissTransition(transition, STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN); } } @@ -1390,6 +1390,33 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return out; } + /** + * This is used for mixed scenarios. For such scenarios, just make sure to include exiting + * split or entering split when appropriate. + */ + public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request, + @NonNull WindowContainerTransaction outWCT) { + final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); + if (triggerTask != null && triggerTask.displayId != mDisplayId) { + // Skip handling task on the other display. + return; + } + final @WindowManager.TransitionType int type = request.getType(); + if (isSplitActive() && !isOpeningType(type) + && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " One of the splits became " + + "empty during a mixed transition (one not handled by split)," + + " so make sure split-screen state is cleaned-up. " + + "mainStageCount=%d sideStageCount=%d", mMainStage.getChildCount(), + mSideStage.getChildCount()); + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT); + } + } + + public boolean isSplitActive() { + return mMainStage.isActive(); + } + @Override public void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, @@ -1482,7 +1509,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - void onTransitionAnimationComplete() { + /** Called to clean-up state and do house-keeping after the animation is done. */ + public void onTransitionAnimationComplete() { // If still playing, let it finish. if (!mMainStage.isActive()) { // Update divider state after animation so that it is still around and positioned @@ -1546,8 +1574,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - private boolean startPendingDismissAnimation( - @NonNull SplitScreenTransitions.DismissTransition dismissTransition, + /** Synchronize split-screen state with transition and make appropriate preparations. */ + public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { // Make some noise if things aren't totally expected. These states shouldn't effect @@ -1580,7 +1608,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again - if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) { + if (shouldBreakPairedTaskInRecents(dismissReason) && mShouldUpdateRecents) { for (TransitionInfo.Change change : info.getChanges()) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo != null @@ -1597,30 +1625,38 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Wait until after animation to update divider // Reset crops so they don't interfere with subsequent launches - t.setWindowCrop(mMainStage.mRootLeash, null); - t.setWindowCrop(mSideStage.mRootLeash, null); + t.setCrop(mMainStage.mRootLeash, null); + t.setCrop(mSideStage.mRootLeash, null); - if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) { - logExit(dismissTransition.mReason); - // TODO: Have a proper remote for this. Until then, though, reset state and use the - // normal animation stuff (which falls back to the normal launcher remote). - mSplitLayout.release(t); - mSplitTransitions.mPendingDismiss = null; - return false; + if (toStage == STAGE_TYPE_UNDEFINED) { + logExit(dismissReason); } else { - logExitToStage(dismissTransition.mReason, - dismissTransition.mDismissTop == STAGE_TYPE_MAIN); + logExitToStage(dismissReason, toStage == STAGE_TYPE_MAIN); } addDividerBarToTransition(info, t, false /* show */); - // We're dismissing split by moving the other one to fullscreen. - // Since we don't have any animations for this yet, just use the internal example - // animations. // Hide divider and dim layer on transition finished. setDividerVisibility(false, finishT); finishT.hide(mMainStage.mDimLayer); finishT.hide(mSideStage.mDimLayer); + } + + private boolean startPendingDismissAnimation( + @NonNull SplitScreenTransitions.DismissTransition dismissTransition, + @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction finishT) { + prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info, + t, finishT); + if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) { + // TODO: Have a proper remote for this. Until then, though, reset state and use the + // normal animation stuff (which falls back to the normal launcher remote). + t.hide(mSplitLayout.getDividerLeash()); + mSplitLayout.release(t); + mSplitTransitions.mPendingDismiss = null; + return false; + } + return true; } @@ -1793,7 +1829,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - mSplitTransitions.startDismissTransition(null /* transition */, wct, + mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, STAGE_TYPE_UNDEFINED, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 9fd5d2003873..4b12eb86b568 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -268,13 +268,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); - if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { - mCallbacks.onChildTaskEnterPip(taskId); - } if (ENABLE_SHELL_TRANSITIONS) { // Status is managed/synchronized by the transition lifecycle. return; } + if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { + mCallbacks.onChildTaskEnterPip(taskId); + } sendStatusChanged(); } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java new file mode 100644 index 000000000000..1ffe26df729f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2022 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.wm.shell.transition; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; + +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; +import static com.android.wm.shell.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.splitscreen.StageCoordinator; + +import java.util.ArrayList; + +/** + * A handler for dealing with transitions involving multiple other handlers. For example: an + * activity in split-screen going into PiP. + */ +public class DefaultMixedHandler implements Transitions.TransitionHandler { + + private final Transitions mPlayer; + private final PipTransitionController mPipHandler; + private final StageCoordinator mSplitHandler; + + private static class MixedTransition { + static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; + + final int mType; + final IBinder mTransition; + + Transitions.TransitionFinishCallback mFinishCallback = null; + + /** + * Mixed transitions are made up of multiple "parts". This keeps track of how many + * parts are currently animating. + */ + int mInFlightSubAnimations = 0; + + MixedTransition(int type, IBinder transition) { + mType = type; + mTransition = transition; + } + } + private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>(); + + public DefaultMixedHandler(@NonNull Transitions player, + @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) { + mPlayer = player; + mPipHandler = pipHandler; + mSplitHandler = splitHandler; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while " + + "Split-Screen is active, so treat it as Mixed."); + if (request.getRemoteTransition() != null) { + throw new IllegalStateException("Unexpected remote transition in" + + "pip-enter-from-split request"); + } + mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, + transition)); + + WindowContainerTransaction out = new WindowContainerTransaction(); + mPipHandler.augmentRequest(transition, request, out); + mSplitHandler.addEnterOrExitIfNeeded(request, out); + return out; + } + return null; + } + + private TransitionInfo subCopy(@NonNull TransitionInfo info, + @WindowManager.TransitionType int newType) { + final TransitionInfo out = new TransitionInfo(newType, info.getFlags()); + for (int i = 0; i < info.getChanges().size(); ++i) { + out.getChanges().add(info.getChanges().get(i)); + } + out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y); + out.setAnimationOptions(info.getAnimationOptions()); + return out; + } + + private boolean isHomeOpening(@NonNull TransitionInfo.Change change) { + return change.getTaskInfo() != null + && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME; + } + + private boolean isWallpaper(@NonNull TransitionInfo.Change change) { + return (change.getFlags() & FLAG_IS_WALLPAPER) != 0; + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + MixedTransition mixed = null; + for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { + if (mActiveTransitions.get(i).mTransition != transition) continue; + mixed = mActiveTransitions.remove(i); + break; + } + if (mixed == null) return false; + + if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { + return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction, + finishCallback); + } else { + throw new IllegalStateException("Starting mixed animation without a known mixed type? " + + mixed.mType); + } + } + + private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + + "entering PIP while Split-Screen is active."); + TransitionInfo.Change pipChange = null; + TransitionInfo.Change wallpaper = null; + final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK); + boolean homeIsOpening = false; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + // going backwards, so remove-by-index is fine. + everythingElse.getChanges().remove(i); + } else if (isHomeOpening(change)) { + homeIsOpening = true; + } else if (isWallpaper(change)) { + wallpaper = change; + } + } + if (pipChange == null) { + // um, something probably went wrong. + return false; + } + final boolean isGoingHome = homeIsOpening; + mixed.mFinishCallback = finishCallback; + Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + --mixed.mInFlightSubAnimations; + if (mixed.mInFlightSubAnimations > 0) return; + if (isGoingHome) { + mSplitHandler.onTransitionAnimationComplete(); + } + mixed.mFinishCallback.onTransitionFinished(wct, wctCB); + }; + if (isGoingHome) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed " + + "since entering-PiP caused us to leave split and return home."); + // We need to split the transition into 2 parts: the pip part (animated by pip) + // and the dismiss-part (animated by launcher). + mixed.mInFlightSubAnimations = 2; + // immediately make the wallpaper visible (so that we don't see it pop-in during + // the time it takes to start recents animation (which is remote). + if (wallpaper != null) { + startTransaction.show(wallpaper.getLeash()).setAlpha(wallpaper.getLeash(), 1.f); + } + // make a new startTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + // Let split update internal state for dismiss. + mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED, + EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT, + finishTransaction); + + // We are trying to accommodate launcher's close animation which can't handle the + // divider-bar, so if split-handler is closing the divider-bar, just hide it and remove + // from transition info. + for (int i = everythingElse.getChanges().size() - 1; i >= 0; --i) { + if ((everythingElse.getChanges().get(i).getFlags() & FLAG_IS_DIVIDER_BAR) != 0) { + everythingElse.getChanges().remove(i); + break; + } + } + + mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, + finishCB); + // Dispatch the rest of the transition normally. This will most-likely be taken by + // recents or default handler. + mPlayer.dispatchTransition(mixed.mTransition, everythingElse, otherStartT, + finishTransaction, finishCB, this); + } else { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just " + + "forward animation to Pip-Handler."); + // This happens if the pip-ing activity is in a multi-activity task (and thus a + // new pip task is spawned). In this case, we don't actually exit split so we can + // just let pip transition handle the animation verbatim. + mixed.mInFlightSubAnimations = 1; + mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction, + finishCB); + } + return true; + } + + @Override + public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + } + + @Override + public void onTransitionMerged(@NonNull IBinder transition) { + MixedTransition mixed = null; + for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { + if (mActiveTransitions.get(i).mTransition != transition) continue; + mixed = mActiveTransitions.remove(i); + break; + } + if (mixed == null) return; + if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { + mPipHandler.onTransitionMerged(transition); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 435d67087f34..e4c94264be2c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -435,33 +435,42 @@ public class Transitions implements RemoteCallable<Transitions> { playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb)); } - boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) { - return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT, - (wct, cb) -> onFinish(active.mToken, wct, cb)); - } - - void playTransition(@NonNull ActiveTransition active) { + private void playTransition(@NonNull ActiveTransition active) { setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT); // If a handler already chose to run this animation, try delegating to it first. if (active.mHandler != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s", active.mHandler); - if (startAnimation(active, active.mHandler)) { + boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo, + active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb)); + if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); return; } } - // Otherwise give every other handler a chance (in order) + // Otherwise give every other handler a chance + active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT, + active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb), active.mHandler); + } + + /** + * Gives every handler (in order) a chance to animate until one consumes the transition. + * @return the handler which consumed the transition. + */ + TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, + @NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) { for (int i = mHandlers.size() - 1; i >= 0; --i) { - if (mHandlers.get(i) == active.mHandler) continue; + if (mHandlers.get(i) == skip) continue; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s", mHandlers.get(i)); - if (startAnimation(active, mHandlers.get(i))) { + boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT, + finishCB); + if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s", mHandlers.get(i)); - active.mHandler = mHandlers.get(i); - return; + return mHandlers.get(i); } } throw new IllegalStateException( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java index 9faf454261d3..86ca292399cb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java @@ -79,7 +79,7 @@ public class UnfoldBackgroundController { } private float[] getBackgroundColor(Context context) { - int colorInt = context.getResources().getColor(R.color.unfold_transition_background); + int colorInt = context.getResources().getColor(R.color.taskbar_background); return new float[]{ (float) red(colorInt) / 255.0F, (float) green(colorInt) / 255.0F, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 639603941c18..af78f2d7d870 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -34,15 +34,20 @@ import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; import com.android.wm.shell.transition.Transitions.TransitionHandler; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; +import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Executor; +/** + * Transition handler that is responsible for animating app surfaces when unfolding of foldable + * devices. It does not handle the folding animation, which is done in + * {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. + */ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener { private final ShellUnfoldProgressProvider mUnfoldProgressProvider; private final Transitions mTransitions; + private final UnfoldBackgroundController mUnfoldBackgroundController; private final Executor mExecutor; private final TransactionPool mTransactionPool; @@ -51,17 +56,22 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Nullable private IBinder mTransition; - private final List<TransitionInfo.Change> mAnimatedFullscreenTasks = new ArrayList<>(); + private final FullscreenUnfoldTaskAnimator mFullscreenAnimator; public UnfoldTransitionHandler(ShellUnfoldProgressProvider unfoldProgressProvider, - TransactionPool transactionPool, Executor executor, Transitions transitions) { + FullscreenUnfoldTaskAnimator animator, TransactionPool transactionPool, + UnfoldBackgroundController unfoldBackgroundController, + Executor executor, Transitions transitions) { mUnfoldProgressProvider = unfoldProgressProvider; + mFullscreenAnimator = animator; mTransactionPool = transactionPool; + mUnfoldBackgroundController = unfoldBackgroundController; mExecutor = executor; mTransitions = transitions; } public void init() { + mFullscreenAnimator.init(); mTransitions.addHandler(this); mUnfoldProgressProvider.addListener(mExecutor, this); } @@ -71,40 +81,36 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { - if (transition != mTransition) return false; + mUnfoldBackgroundController.ensureBackground(startTransaction); startTransaction.apply(); - mAnimatedFullscreenTasks.clear(); + mFullscreenAnimator.clearTasks(); info.getChanges().forEach(change -> { final boolean allowedToAnimate = change.getTaskInfo() != null + && change.getTaskInfo().isVisible() && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FULLSCREEN && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME && change.getMode() == TRANSIT_CHANGE; if (allowedToAnimate) { - mAnimatedFullscreenTasks.add(change); + mFullscreenAnimator.addTask(change.getTaskInfo(), change.getLeash()); } }); + mFullscreenAnimator.resetAllSurfaces(finishTransaction); + mUnfoldBackgroundController.removeBackground(finishTransaction); mFinishCallback = finishCallback; - mTransition = null; return true; } @Override public void onStateChangeProgress(float progress) { - mAnimatedFullscreenTasks.forEach(change -> { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - - // TODO: this is a placeholder animation, replace with a spec version in the next CLs - final float testScale = 0.8f + 0.2f * progress; - transaction.setScale(change.getLeash(), testScale, testScale); - - transaction.apply(); - mTransactionPool.release(transaction); - }); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + mFullscreenAnimator.applyAnimationProgress(progress, transaction); + transaction.apply(); + mTransactionPool.release(transaction); } @Override @@ -112,7 +118,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null, null); mFinishCallback = null; - mAnimatedFullscreenTasks.clear(); + mTransition = null; + mFullscreenAnimator.clearTasks(); } } @@ -120,11 +127,14 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null - && request.getDisplayChange().isPhysicalDisplayChanged()) { + if (request.getType() == TRANSIT_CHANGE && request.getDisplayChange() != null) { mTransition = transition; return new WindowContainerTransaction(); } return null; } + + public boolean willHandleTransition() { + return mTransition != null; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java new file mode 100644 index 000000000000..6ec5512c22ec --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 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.wm.shell.unfold.animation; + +import static android.util.MathUtils.lerp; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.animation.RectEvaluator; +import android.animation.TypeEvaluator; +import android.app.TaskInfo; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.util.SparseArray; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; + +import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.wm.shell.common.DisplayInsetsController; + +/** + * This helper class contains logic that calculates scaling and cropping parameters + * for the folding/unfolding animation. As an input it receives TaskInfo objects and + * surfaces leashes and as an output it could fill surface transactions with required + * transformations. + * + * This class is used by + * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and + * {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. They use independent + * instances of FullscreenUnfoldTaskAnimator. + */ +public class FullscreenUnfoldTaskAnimator implements + DisplayInsetsController.OnInsetsChangedListener { + + private static final float[] FLOAT_9 = new float[9]; + private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); + + private static final float HORIZONTAL_START_MARGIN = 0.08f; + private static final float VERTICAL_START_MARGIN = 0.03f; + private static final float END_SCALE = 1f; + private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2; + + private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); + private final int mExpandedTaskBarHeight; + private final float mWindowCornerRadiusPx; + private final DisplayInsetsController mDisplayInsetsController; + + private InsetsSource mTaskbarInsetsSource; + + public FullscreenUnfoldTaskAnimator(Context context, + DisplayInsetsController displayInsetsController) { + mDisplayInsetsController = displayInsetsController; + mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.taskbar_frame_height); + mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); + } + + public void init() { + mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); + } + + @Override + public void insetsChanged(InsetsState insetsState) { + mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + AnimationContext context = mAnimationContextByTaskId.valueAt(i); + context.update(mTaskbarInsetsSource, context.mTaskInfo); + } + } + + public boolean hasActiveTasks() { + return mAnimationContextByTaskId.size() > 0; + } + + public void addTask(TaskInfo taskInfo, SurfaceControl leash) { + AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource, + taskInfo); + mAnimationContextByTaskId.put(taskInfo.taskId, animationContext); + } + + public void onTaskInfoChanged(TaskInfo taskInfo) { + AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); + if (animationContext != null) { + animationContext.update(mTaskbarInsetsSource, taskInfo); + } + } + + public void removeTask(TaskInfo taskInfo) { + mAnimationContextByTaskId.remove(taskInfo.taskId); + } + + public void clearTasks() { + mAnimationContextByTaskId.clear(); + } + + public void resetSurface(TaskInfo taskInfo, Transaction transaction) { + final AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId); + if (context != null) { + resetSurface(context, transaction); + } + } + + public void applyAnimationProgress(float progress, Transaction transaction) { + if (mAnimationContextByTaskId.size() == 0) return; + + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + + context.mCurrentCropRect.set(RECT_EVALUATOR + .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); + + float scale = lerp(START_SCALE, END_SCALE, progress); + context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(), + context.mCurrentCropRect.exactCenterY()); + + transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) + .setMatrix(context.mLeash, context.mMatrix, FLOAT_9) + .setCornerRadius(context.mLeash, mWindowCornerRadiusPx) + .show(context.mLeash) + ; + } + } + + public void resetAllSurfaces(Transaction transaction) { + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + resetSurface(context, transaction); + } + } + + private void resetSurface(AnimationContext context, Transaction transaction) { + transaction + .setWindowCrop(context.mLeash, null) + .setCornerRadius(context.mLeash, 0.0F) + .setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F) + .setPosition(context.mLeash, + (float) context.mTaskInfo.positionInParent.x, + (float) context.mTaskInfo.positionInParent.y); + } + + private class AnimationContext { + final SurfaceControl mLeash; + final Rect mStartCropRect = new Rect(); + final Rect mEndCropRect = new Rect(); + final Rect mCurrentCropRect = new Rect(); + final Matrix mMatrix = new Matrix(); + + TaskInfo mTaskInfo; + + private AnimationContext(SurfaceControl leash, InsetsSource taskBarInsetsSource, + TaskInfo taskInfo) { + mLeash = leash; + update(taskBarInsetsSource, taskInfo); + } + + private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) { + mTaskInfo = taskInfo; + mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds()); + + if (taskBarInsetsSource != null) { + // Only insets the cropping window with task bar when it's expanded + if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { + mStartCropRect.inset(taskBarInsetsSource + .calculateVisibleInsets(mStartCropRect)); + } + } + + mEndCropRect.set(mStartCropRect); + + int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN); + mStartCropRect.left = mEndCropRect.left + horizontalMargin; + mStartCropRect.right = mEndCropRect.right - horizontalMargin; + int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN); + mStartCropRect.top = mEndCropRect.top + verticalMargin; + mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin; + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index a55f737f2f25..0e39527a9e5f 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -334,7 +334,7 @@ public class SplitTransitionTests extends ShellTestCase { TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0); info.addChange(mainChange); info.addChange(sideChange); - IBinder transition = mSplitScreenTransitions.startDismissTransition(null, + IBinder transition = mSplitScreenTransitions.startDismissTransition( new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE); boolean accepted = mStageCoordinator.startAnimation(transition, info, @@ -356,7 +356,7 @@ public class SplitTransitionTests extends ShellTestCase { TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0); info.addChange(mainChange); info.addChange(sideChange); - IBinder transition = mSplitScreenTransitions.startDismissTransition(null, + IBinder transition = mSplitScreenTransitions.startDismissTransition( new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER, STAGE_TYPE_SIDE); mMainStage.onTaskVanished(mMainChild); diff --git a/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml new file mode 100644 index 000000000000..538f3284a301 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml @@ -0,0 +1,725 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="7.062" + android:valueTo="8.578" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateX" + android:startOffset="28" + android:valueFrom="8.578" + android:valueTo="-1.789" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateX" + android:startOffset="248" + android:valueFrom="-1.789" + android:valueTo="-7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="3.312" + android:valueTo="2.016" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateY" + android:startOffset="28" + android:valueFrom="2.016" + android:valueTo="-8.789" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateY" + android:startOffset="248" + android:valueFrom="-8.789" + android:valueTo="-3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="180" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="166" + android:propertyName="rotation" + android:startOffset="28" + android:valueFrom="90" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="rotation" + android:startOffset="193" + android:valueFrom="90" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_N_1_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-135" + android:valueTo="-180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="-7" + android:valueTo="-8.859" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateX" + android:startOffset="28" + android:valueFrom="-8.859" + android:valueTo="1.69" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateX" + android:startOffset="248" + android:valueFrom="1.69" + android:valueTo="7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="-3.594" + android:valueTo="-1.922" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateY" + android:startOffset="28" + android:valueFrom="-1.922" + android:valueTo="8.627" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateY" + android:startOffset="248" + android:valueFrom="8.627" + android:valueTo="3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="360" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="166" + android:propertyName="rotation" + android:startOffset="28" + android:valueFrom="270" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="rotation" + android:startOffset="193" + android:valueFrom="270" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="rotation" + android:startOffset="248" + android:valueFrom="180" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_N_1_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-135" + android:valueTo="-180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="66" + android:propertyName="pathData" + android:startOffset="28" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="72" + android:propertyName="pathData" + android:startOffset="94" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="166" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="pathData" + android:startOffset="193" + android:valueFrom="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="290" + android:valueFrom="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueTo="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="83" + android:propertyName="pathData" + android:startOffset="317" + android:valueFrom="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueTo="M5.94 -2.52 C5.94,-2.52 5.94,-2.51 5.94,-2.51 C5.94,-2.51 5.93,-2.52 5.93,-2.52 C5.93,-2.52 4.53,-1.12 4.53,-1.12 C4.53,-1.12 4.55,-1.11 4.55,-1.11 C4.55,-1.11 4.53,-1.13 4.53,-1.13 C4.53,-1.13 4.55,-1.09 4.55,-1.09 C4.55,-1.09 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 5.94,-2.52 5.94,-2.52 C5.94,-2.52 5.94,-2.52 5.94,-2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="66" + android:propertyName="pathData" + android:startOffset="28" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="72" + android:propertyName="pathData" + android:startOffset="94" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="166" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="pathData" + android:startOffset="193" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="290" + android:valueFrom="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueTo="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="83" + android:propertyName="pathData" + android:startOffset="317" + android:valueFrom="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueTo="M-5.94 2.52 C-5.94,2.52 -5.94,2.51 -5.94,2.51 C-5.94,2.51 -5.93,2.52 -5.93,2.52 C-5.93,2.52 -4.53,1.12 -4.53,1.12 C-4.53,1.12 -4.55,1.11 -4.55,1.11 C-4.55,1.11 -4.53,1.13 -4.53,1.13 C-4.53,1.13 -4.55,1.09 -4.55,1.09 C-4.55,1.09 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -5.94,2.52 -5.94,2.52 C-5.94,2.52 -5.94,2.52 -5.94,2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="61" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="61" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="143" + android:propertyName="pathData" + android:startOffset="88" + android:valueFrom="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueTo="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="232" + android:valueFrom="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueTo="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueTo="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -8.52,-3.53 -8.52,-3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_3_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="61" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="61" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="143" + android:propertyName="pathData" + android:startOffset="88" + android:valueFrom="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueTo="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="232" + android:valueFrom="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueTo="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueTo="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 8.52,3.53 8.52,3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-135" + android:valueTo="-180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="683" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_2_G_N_1_T_0" + android:rotation="-135" + android:translateX="12.008" + android:translateY="11.992"> + <group + android:name="_R_G_L_2_G_T_1" + android:rotation="180" + android:translateX="7.062" + android:translateY="3.312"> + <group + android:name="_R_G_L_2_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_1_G_N_1_T_0" + android:rotation="-135" + android:translateX="12.008" + android:translateY="11.992"> + <group + android:name="_R_G_L_1_G_T_1" + android:rotation="360" + android:translateX="-7" + android:translateY="-3.594"> + <group + android:name="_R_G_L_1_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_0_G" + android:rotation="-135" + android:translateX="12.008" + android:translateY="11.992"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " /> + <path + android:name="_R_G_L_0_G_D_3_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml new file mode 100644 index 000000000000..bd67d9f8dbaa --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml @@ -0,0 +1,781 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="fillAlpha" + android:startOffset="367" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="17" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="45" + android:propertyName="pathData" + android:startOffset="59" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="103" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="pathData" + android:startOffset="121" + android:valueFrom="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="26" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="181" + android:valueFrom="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueTo="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="52" + android:propertyName="pathData" + android:startOffset="198" + android:valueFrom="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueTo="M5.94 -2.52 C5.94,-2.52 5.94,-2.51 5.94,-2.51 C5.94,-2.51 5.93,-2.52 5.93,-2.52 C5.93,-2.52 4.53,-1.12 4.53,-1.12 C4.53,-1.12 4.55,-1.11 4.55,-1.11 C4.55,-1.11 4.53,-1.13 4.53,-1.13 C4.53,-1.13 4.55,-1.09 4.55,-1.09 C4.55,-1.09 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 5.94,-2.52 5.94,-2.52 C5.94,-2.52 5.94,-2.52 5.94,-2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="fillAlpha" + android:startOffset="367" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="17" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="45" + android:propertyName="pathData" + android:startOffset="59" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="103" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="pathData" + android:startOffset="121" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="26" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="181" + android:valueFrom="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueTo="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="52" + android:propertyName="pathData" + android:startOffset="198" + android:valueFrom="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueTo="M-5.94 2.52 C-5.94,2.52 -5.94,2.51 -5.94,2.51 C-5.94,2.51 -5.93,2.52 -5.93,2.52 C-5.93,2.52 -4.53,1.12 -4.53,1.12 C-4.53,1.12 -4.55,1.11 -4.55,1.11 C-4.55,1.11 -4.53,1.13 -4.53,1.13 C-4.53,1.13 -4.55,1.09 -4.55,1.09 C-4.55,1.09 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -5.94,2.52 -5.94,2.52 C-5.94,2.52 -5.94,2.52 -5.94,2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="38" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="38" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="90" + android:propertyName="pathData" + android:startOffset="55" + android:valueFrom="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueTo="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="10" + android:propertyName="pathData" + android:startOffset="145" + android:valueFrom="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueTo="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueTo="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -8.52,-3.53 -8.52,-3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_3_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="38" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="38" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="90" + android:propertyName="pathData" + android:startOffset="55" + android:valueFrom="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueTo="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="10" + android:propertyName="pathData" + android:startOffset="145" + android:valueFrom="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueTo="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueTo="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 8.52,3.53 8.52,3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="-135" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="-7" + android:valueTo="-8.859" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateX" + android:startOffset="17" + android:valueFrom="-8.859" + android:valueTo="1.734" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateX" + android:startOffset="155" + android:valueFrom="1.734" + android:valueTo="7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="-3.594" + android:valueTo="-1.922" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateY" + android:startOffset="17" + android:valueFrom="-1.922" + android:valueTo="8.671" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateY" + android:startOffset="155" + android:valueFrom="8.671" + android:valueTo="3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="360" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="103" + android:propertyName="rotation" + android:startOffset="17" + android:valueFrom="270" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="rotation" + android:startOffset="121" + android:valueFrom="270" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="rotation" + android:startOffset="155" + android:valueFrom="180" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_N_3_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="-135" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="7.062" + android:valueTo="8.578" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateX" + android:startOffset="17" + android:valueFrom="8.578" + android:valueTo="-1.656" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateX" + android:startOffset="155" + android:valueFrom="-1.656" + android:valueTo="-7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="3.312" + android:valueTo="2.016" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateY" + android:startOffset="17" + android:valueFrom="2.016" + android:valueTo="-8.656" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateY" + android:startOffset="155" + android:valueFrom="-8.656" + android:valueTo="-3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="180" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="103" + android:propertyName="rotation" + android:startOffset="17" + android:valueFrom="90" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="rotation" + android:startOffset="121" + android:valueFrom="90" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_N_3_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="-135" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="433" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_2_G" + android:rotation="0" + android:translateX="12" + android:translateY="12"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " /> + <path + android:name="_R_G_L_2_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " /> + <path + android:name="_R_G_L_2_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " /> + <path + android:name="_R_G_L_2_G_D_3_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " /> + </group> + <group + android:name="_R_G_L_1_G_N_3_T_0" + android:rotation="0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_1_G_T_1" + android:rotation="360" + android:translateX="-7" + android:translateY="-3.594"> + <group + android:name="_R_G_L_1_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_0_G_N_3_T_0" + android:rotation="0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_0_G_T_1" + android:rotation="180" + android:translateX="7.062" + android:translateY="3.312"> + <group + android:name="_R_G_L_0_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml new file mode 100644 index 000000000000..17a76116db25 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:height="24dp" + android:width="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c "/> + </group> + <group + android:name="_R_G_L_1_G" + android:translateX="14.125" + android:translateY="12" + android:pivotX="-9.109" + android:scaleX="1" + android:scaleY="1"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-9.09 -1.5 C-8.26,-1.5 -7.59,-0.83 -7.59,0 C-7.59,0.83 -8.26,1.5 -9.09,1.5 C-9.91,1.5 -10.59,0.83 -10.59,0 C-10.59,-0.83 -9.91,-1.5 -9.09,-1.5c "/> + </group> + <group + android:name="_R_G_L_0_G" + android:translateX="14.125" + android:translateY="12" + android:pivotX="4.875" + android:scaleX="1" + android:scaleY="1"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M4.92 -1.5 C5.75,-1.5 6.42,-0.83 6.42,0 C6.42,0.83 5.75,1.5 4.92,1.5 C4.09,1.5 3.42,0.83 3.42,0 C3.42,-0.83 4.09,-1.5 4.92,-1.5c "/> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_1_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:propertyName="scaleX" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:propertyName="scaleY" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr + name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:propertyName="scaleX" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:propertyName="scaleY" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:propertyName="translateX" + android:duration="150" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml new file mode 100644 index 000000000000..2dba48cf155d --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_1_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="scaleX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="250" + android:propertyName="scaleY" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="scaleX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="250" + android:propertyName="scaleY" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="267" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_2_G" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c " /> + </group> + <group + android:name="_R_G_L_1_G" + android:pivotX="-9.109" + android:scaleX="0" + android:scaleY="0" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-9.09 -1.5 C-8.26,-1.5 -7.59,-0.83 -7.59,0 C-7.59,0.83 -8.26,1.5 -9.09,1.5 C-9.91,1.5 -10.59,0.83 -10.59,0 C-10.59,-0.83 -9.91,-1.5 -9.09,-1.5c " /> + </group> + <group + android:name="_R_G_L_0_G" + android:pivotX="4.875" + android:scaleX="0" + android:scaleY="0" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M4.92 -1.5 C5.75,-1.5 6.42,-0.83 6.42,0 C6.42,0.83 5.75,1.5 4.92,1.5 C4.09,1.5 3.42,0.83 3.42,0 C3.42,-0.83 4.09,-1.5 4.92,-1.5c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml new file mode 100644 index 000000000000..3697769cfa34 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml @@ -0,0 +1,268 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_0_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="500" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_3_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_4_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="500" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="1017" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_0_G" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M4.56 2.33 C4.56,2.33 2.24,0.01 2.24,0.01 C2.24,0.01 4.57,-2.31 4.57,-2.31 C4.84,-1.59 5,-0.82 5,0 C5,0.82 4.84,1.61 4.56,2.33c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M6.27 -4.03 C6.27,-4.03 7.53,-5.29 7.53,-5.29 C8.46,-3.77 9,-1.99 9.01,-0.1 C9.01,1.85 8.44,3.67 7.47,5.21 C7.47,5.21 6.27,4.01 6.27,4.01 C6.89,2.81 7.25,1.44 7.25,-0.01 C7.25,-1.46 6.9,-2.82 6.27,-4.03c " /> + <path + android:name="_R_G_L_0_G_D_3_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-8.6 -2.33 C-8.6,-2.33 -6.28,-0.01 -6.28,-0.01 C-6.28,-0.01 -8.61,2.31 -8.61,2.31 C-8.88,1.59 -9.04,0.82 -9.04,0 C-9.04,-0.82 -8.88,-1.61 -8.6,-2.33c " /> + <path + android:name="_R_G_L_0_G_D_4_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-10.31 4.03 C-10.31,4.03 -11.57,5.29 -11.57,5.29 C-12.5,3.77 -13.04,1.99 -13.05,0.1 C-13.05,-1.85 -12.48,-3.67 -11.51,-5.21 C-11.51,-5.21 -10.31,-4.01 -10.31,-4.01 C-10.93,-2.81 -11.29,-1.44 -11.29,0.01 C-11.29,1.46 -10.94,2.82 -10.31,4.03c " /> + <path + android:name="_R_G_L_0_G_D_5_P_0" + android:fillAlpha="1" + android:fillColor="#000000" + android:fillType="nonZero" + android:pathData=" M-9.09 0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0c " /> + <path + android:name="_R_G_L_0_G_D_6_P_0" + android:fillAlpha="1" + android:fillColor="#000000" + android:fillType="nonZero" + android:pathData=" M4.92 0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index f2ddf9e645b6..fb04bfd7ca05 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -441,6 +441,9 @@ they were added. --> <integer name="config_smart_replies_in_notifications_onclick_init_delay">200</integer> + <!-- Smartspace trampoline activity that is used when the user taps smartspace. --> + <string name="config_smartspaceTrampolineActivityComponent" translatable="false">com.google.android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity</string> + <!-- Screenshot editing default activity. Must handle ACTION_EDIT image/png intents. Blank sends the user to the Chooser first. This name is in the ComponentName flattened format (package/class) --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index 9cf482fdf790..4ce110bf7c47 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -17,7 +17,6 @@ package com.android.systemui.shared.system; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; -import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -32,7 +31,6 @@ import android.app.WindowConfiguration; import android.graphics.Point; import android.graphics.Rect; import android.util.ArrayMap; -import android.util.IntArray; import android.util.SparseArray; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -142,21 +140,10 @@ public class RemoteAnimationTargetCompat { // changes should be ordered top-to-bottom in z final int mode = change.getMode(); - // Don't move anything that isn't independent within its parents - if (!TransitionInfo.isIndependent(change, info)) { - if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) { - t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y); - } - return; - } - - final boolean hasParent = change.getParent() != null; + t.reparent(leash, info.getRootLeash()); + t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, + change.getStartAbsBounds().top - info.getRootOffset().y); - if (!hasParent) { - t.reparent(leash, info.getRootLeash()); - t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, - change.getStartAbsBounds().top - info.getRootOffset().y); - } // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { if (isOpening) { @@ -196,8 +183,7 @@ public class RemoteAnimationTargetCompat { .setContainerLayer() // Initial the surface visible to respect the visibility of the original surface. .setHidden(false) - .setParent(change.getParent() == null ? info.getRootLeash() - : info.getChange(change.getParent()).getLeash()) + .setParent(info.getRootLeash()) .build(); // Copied Transitions setup code (which expects bottom-to-top order, so we swap here) setupLeash(leashSurface, change, info.getChanges().size() - order, info, t); @@ -269,58 +255,32 @@ public class RemoteAnimationTargetCompat { public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>(); - final SparseArray<RemoteAnimationTargetCompat> childTaskTargets = new SparseArray<>(); - final IntArray excludedParentTaskIds = new IntArray(); + final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>(); for (int i = 0; i < info.getChanges().size(); i++) { final TransitionInfo.Change change = info.getChanges().get(i); final boolean changeIsWallpaper = (change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0; if (wallpapers != changeIsWallpaper) continue; - final RemoteAnimationTargetCompat targetCompat = - new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t); - if (leashMap != null) { - leashMap.put(change.getLeash(), targetCompat.leash); - } - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo != null) { - // Skip wrapping excluded parent task animate target since it will animate its child - // tasks instead. - if (excludedParentTaskIds.binarySearch(taskInfo.taskId) != -1) { - continue; - } - - // Check if there's a matching child task target in cache. - RemoteAnimationTargetCompat childTaskTarget = childTaskTargets.get(taskInfo.taskId); - if (childTaskTarget != null) { - // Launcher monitors leaf task ids to perform animation, override the target - // with its child task information so Launcher can animate this parent surface - // directly with leaf task information. - targetCompat.taskInfo = childTaskTarget.taskInfo; - targetCompat.taskId = childTaskTarget.taskId; - childTaskTargets.remove(taskInfo.taskId); - } - - // Check if it has a parent task, cache its information for later use. - if (taskInfo.parentTaskId != -1 - && excludedParentTaskIds.binarySearch(taskInfo.parentTaskId) == -1) { - if (!childTaskTargets.contains(taskInfo.parentTaskId)) { - // Cache the target amd skip wrapping it info the final animation targets. - // Otherwise, the child task might get transformed multiple-times with the - // flow like RecentsView#redrawLiveTile. - childTaskTargets.put(taskInfo.parentTaskId, targetCompat); + if (!wallpapers) { + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + // Children always come before parent since changes are in top-to-bottom z-order. + if (taskInfo != null) { + if (childTaskTargets.contains(taskInfo.taskId)) { + // has children, so not a leaf. Skip. continue; } - - // There is another child task target cached with the same parent task id. - // Which means the parent having multiple child tasks in transition. Stop - // propagate child task info. - childTaskTarget = childTaskTargets.removeReturnOld(taskInfo.parentTaskId); - out.add(childTaskTarget); - excludedParentTaskIds.add(taskInfo.parentTaskId); + if (taskInfo.hasParentTask()) { + childTaskTargets.put(taskInfo.parentTaskId, change); + } } } + final RemoteAnimationTargetCompat targetCompat = + new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t); + if (leashMap != null) { + leashMap.put(change.getLeash(), targetCompat.leash); + } out.add(targetCompat); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index de35514be2cb..d7f8235d76a9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionFilter.CONTAINER_ORDER_TOP; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.annotation.NonNull; import android.annotation.Nullable; @@ -132,16 +133,17 @@ public class RemoteTransitionCompat implements Parcelable { // TODO(b/177438007): Move this set-up logic into launcher's animation impl. mToken = transition; // This transition is for opening recents, so recents is on-top. We want to draw - // the current going-away task on top of recents, though, so move it to front + // the current going-away tasks on top of recents, though, so move them to front. + // Note that we divide up the "layer space" into 3 regions each the size of + // the change count. This way we can easily move changes into above/below/between + // while maintaining their relative ordering. final ArrayList<WindowContainerToken> pausingTasks = new ArrayList<>(); WindowContainerToken pipTask = null; WindowContainerToken recentsTask = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) { - t.setLayer(leashMap.get(change.getLeash()), - info.getChanges().size() * 3 - i); + for (int i = apps.length - 1; i >= 0; --i) { + final ActivityManager.RunningTaskInfo taskInfo = apps[i].taskInfo; + if (apps[i].mode == MODE_CLOSING) { + t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); if (taskInfo == null) { continue; } @@ -154,8 +156,7 @@ public class RemoteTransitionCompat implements Parcelable { } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { // This task is for recents, keep it on top. - t.setLayer(leashMap.get(change.getLeash()), - info.getChanges().size() * 3 - i); + t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); recentsTask = taskInfo.token; } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { recentsTask = taskInfo.token; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java index f5c5a434a077..3d1bc59b433a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java @@ -18,10 +18,12 @@ package com.android.systemui.dreams.complication; import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS; import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW; +import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT; import android.app.smartspace.SmartspaceAction; import android.app.smartspace.SmartspaceTarget; import android.content.Context; +import android.content.Intent; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.text.TextUtils; @@ -31,6 +33,7 @@ import com.android.systemui.CoreStartable; import com.android.systemui.R; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.util.ViewController; @@ -132,15 +135,21 @@ public class DreamWeatherComplication implements Complication { */ static class DreamWeatherViewController extends ViewController<TextView> { private final LockscreenSmartspaceController mSmartSpaceController; + private final ActivityStarter mActivityStarter; + private final String mSmartspaceTrampolineActivityComponent; private SmartspaceTargetListener mSmartspaceTargetListener; @Inject DreamWeatherViewController( @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view, + @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) String smartspaceTrampoline, + ActivityStarter activityStarter, LockscreenSmartspaceController smartspaceController ) { super(view); + mActivityStarter = activityStarter; mSmartSpaceController = smartspaceController; + mSmartspaceTrampolineActivityComponent = smartspaceTrampoline; } @Override @@ -172,6 +181,15 @@ public class DreamWeatherComplication implements Complication { R.dimen.smart_action_button_icon_padding)); } + mView.setOnClickListener(v -> { + final Intent intent = headerAction.getIntent(); + if (intent != null && intent.getComponent() != null + && intent.getComponent().getClassName() + .equals(mSmartspaceTrampolineActivityComponent)) { + mActivityStarter.postStartActivityDismissingKeyguard( + intent, 0 /*delay*/); + } + }); } }); mSmartSpaceController.addListener(mSmartspaceTargetListener); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java index 536f3dcd2850..a1660f2b43f2 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dreams.complication.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import android.content.Context; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.TextView; @@ -76,6 +77,7 @@ public interface DreamWeatherComplicationComponent { String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view"; String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS = "weather_complication_layout_params"; + String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity"; // Order weight of insert into parent container int INSERT_ORDER_WEIGHT = 1; @@ -106,5 +108,15 @@ public interface DreamWeatherComplicationComponent { ComplicationLayoutParams.DIRECTION_END, INSERT_ORDER_WEIGHT); } + + /** + * Provides the smartspace trampoline activity component. + */ + @Provides + @DreamWeatherComplicationScope + @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) + static String provideSmartspaceTrampolineActivityComponent(Context context) { + return context.getString(R.string.config_smartspaceTrampolineActivityComponent); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java index c147fde65cf7..517fce1b16a6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -144,6 +144,8 @@ public class BouncerSwipeTouchHandler implements DreamTouchHandler { private void setPanelExpansion(float expansion) { mCurrentExpansion = expansion; + mCentralSurfaces.setBouncerShowingOverDream( + mCurrentExpansion != KeyguardBouncer.EXPANSION_HIDDEN); mStatusBarKeyguardViewManager.onPanelExpansionChanged(mCurrentExpansion, false, true); } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index afa7d5e0a9c4..94418f498d3b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -158,6 +158,8 @@ public class Flags { // 1000 - dock public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING = new BooleanFlag(1000, true); + public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, false); + // 1100 - windowing @Keep diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index cd695bbc6b69..8878c2decb87 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -317,7 +317,7 @@ public class NavigationBarView extends FrameLayout { }; private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> { - if (visible) { + if (visible && mAutoHideController != null) { mAutoHideController.touchAutoHide(); } notifyActiveTouchRegions(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index f736231bc22b..da5202bc3645 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -19,9 +19,7 @@ package com.android.systemui.qs.tiles; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; -import android.content.Context; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.UserManager; @@ -134,9 +132,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_bluetooth); state.stateDescription = ""; + if (enabled) { if (connected) { - state.icon = new BluetoothConnectedTileIcon(); + state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_on); if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) { state.label = mController.getConnectedDeviceName(); } @@ -145,21 +144,19 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { + ", " + state.secondaryLabel; } else if (state.isTransient) { state.icon = ResourceIcon.get( - com.android.internal.R.drawable.ic_bluetooth_transient_animation); + R.drawable.qs_bluetooth_icon_search); state.stateDescription = state.secondaryLabel; } else { state.icon = - ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth); + ResourceIcon.get(R.drawable.qs_bluetooth_icon_off); state.stateDescription = mContext.getString(R.string.accessibility_not_connected); } state.state = Tile.STATE_ACTIVE; } else { - state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth); + state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_off); state.state = Tile.STATE_INACTIVE; } - state.dualLabelContentDescription = mContext.getResources().getString( - R.string.accessibility_quick_settings_open_settings, getTileLabel()); state.expandedAccessibilityClassName = Switch.class.getName(); } @@ -244,22 +241,4 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { refreshState(); } }; - - /** - * Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is - * used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to - * use a context that reflects dark/light theme attributes. - */ - private class BluetoothConnectedTileIcon extends Icon { - - BluetoothConnectedTileIcon() { - // Do nothing. Default constructor to limit visibility. - } - - @Override - public Drawable getDrawable(Context context) { - // This method returns Pair<Drawable, String> - the first value is the drawable. - return context.getDrawable(R.drawable.ic_bluetooth_connected); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 177c82ed3d78..600874f0d01a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -57,6 +57,7 @@ import javax.inject.Inject; /** Quick settings tile: Rotation **/ public class RotationLockTile extends QSTileImpl<BooleanState> implements BatteryController.BatteryStateChangeCallback { + private static final String EMPTY_SECONDARY_STRING = ""; private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate); private final RotationLockController mController; @@ -144,13 +145,15 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements && mController.isCameraRotationEnabled(); state.value = !rotationLocked; state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); - state.icon = mIcon; + state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off); state.contentDescription = getAccessibilityString(rotationLocked); - if (!rotationLocked && cameraRotation) { - state.secondaryLabel = mContext.getResources().getString( - R.string.rotation_lock_camera_rotation_on); + if (!rotationLocked) { + state.secondaryLabel = cameraRotation ? mContext.getResources().getString( + R.string.rotation_lock_camera_rotation_on) + : EMPTY_SECONDARY_STRING; + state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on); } else { - state.secondaryLabel = ""; + state.secondaryLabel = EMPTY_SECONDARY_STRING; } state.stateDescription = state.secondaryLabel; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index 5e81b5da2455..a1dbf0c3f5b6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -468,6 +468,7 @@ public class CentralSurfaces extends CoreStartable implements */ protected int mState; // TODO: remove this. Just use StatusBarStateController protected boolean mBouncerShowing; + private boolean mBouncerShowingOverDream; private final PhoneStatusBarPolicy mIconPolicy; @@ -3251,9 +3252,13 @@ public class CentralSurfaces extends CoreStartable implements } public boolean onBackPressed() { - boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; - if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) { - if (isScrimmedBouncer) { + final boolean isScrimmedBouncer = + mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; + final boolean isBouncerOverDream = isBouncerShowingOverDream(); + + if (mStatusBarKeyguardViewManager.onBackPressed( + isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) { + if (isScrimmedBouncer || isBouncerOverDream) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } else { mNotificationPanelViewController.expandWithoutQs(); @@ -3275,7 +3280,8 @@ public class CentralSurfaces extends CoreStartable implements if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) { return true; } - if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { + if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED + && !isBouncerOverDream) { if (mNotificationPanelViewController.canPanelBeCollapsed()) { mShadeController.animateCollapsePanels(); } @@ -3473,6 +3479,16 @@ public class CentralSurfaces extends CoreStartable implements } /** + * Sets whether the bouncer over dream is showing. Note that the bouncer over dream is handled + * independently of the rest of the notification panel. As a result, setting this state via + * {@link #setBouncerShowing(boolean)} leads to unintended side effects from states modified + * behind the dream. + */ + public void setBouncerShowingOverDream(boolean bouncerShowingOverDream) { + mBouncerShowingOverDream = bouncerShowingOverDream; + } + + /** * Propagate the bouncer state to status bar components. * * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and @@ -4145,7 +4161,7 @@ public class CentralSurfaces extends CoreStartable implements } public boolean isBouncerShowingOverDream() { - return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive(); + return mBouncerShowingOverDream; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 4ba8441fc9a6..711b7e08a7ba 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -423,6 +423,8 @@ public abstract class PanelViewController { // situations, such keeping your finger down while swiping to unlock to an app // that is locked in landscape (the rotation will cancel the touch event). expand = false; + } else if (mCentralSurfaces.isBouncerShowingOverDream()) { + expand = false; } else { // If we get a cancel, put the shade back to the state it was in when the // gesture started diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java index d3c6e9aa0da9..313d56febb75 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java @@ -18,6 +18,7 @@ package com.android.systemui.util.condition; import android.util.Log; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.CallbackController; import org.jetbrains.annotations.NotNull; @@ -60,21 +61,13 @@ public class Monitor implements CallbackController<Monitor.Callback> { }; @Inject - public Monitor(Executor executor, Set<Condition> conditions, Set<Callback> callbacks) { + public Monitor(@Main Executor executor, Set<Condition> conditions) { mConditions = new HashSet<>(); mExecutor = executor; if (conditions != null) { mConditions.addAll(conditions); } - - if (callbacks == null) { - return; - } - - for (Callback callback : callbacks) { - addCallbackLocked(callback); - } } private void updateConditionMetState() { diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java index fc67973fe278..8e739d61b4f2 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java +++ b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java @@ -34,8 +34,7 @@ public interface MonitorComponent { */ @Subcomponent.Factory interface Factory { - MonitorComponent create(@BindsInstance Set<Condition> conditions, - @BindsInstance Set<Monitor.Callback> callbacks); + MonitorComponent create(@BindsInstance Set<Condition> conditions); } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index e175af7a037d..535023fa08c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -420,7 +420,31 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE); } + /** + * Ensures {@link CentralSurfaces} + */ + @Test + public void testInformBouncerShowingOnExpand() { + swipeToPosition(1f, Direction.UP, 0); + verify(mCentralSurfaces).setBouncerShowingOverDream(true); + } + + /** + * Ensures {@link CentralSurfaces} + */ + @Test + public void testInformBouncerHidingOnCollapse() { + // Must swipe up to set initial state. + swipeToPosition(1f, Direction.UP, 0); + Mockito.clearInvocations(mCentralSurfaces); + + swipeToPosition(0f, Direction.DOWN, 0); + verify(mCentralSurfaces).setBouncerShowingOverDream(false); + } + + private void swipeToPosition(float percent, Direction direction, float velocityY) { + Mockito.clearInvocations(mTouchSession); mTouchHandler.onSessionStart(mTouchSession); ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt index cc472481ad8a..d65901777a73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt @@ -10,6 +10,7 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter @@ -18,6 +19,7 @@ import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSTileHost import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BluetoothController import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -25,6 +27,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -84,6 +87,53 @@ class BluetoothTileTest : SysuiTestCase() { assertThat(tile.restrictionChecked).isEqualTo(UserManager.DISALLOW_BLUETOOTH) } + @Test + fun testIcon_whenDisabled_isOffState() { + val state = QSTile.BooleanState() + disableBluetooth() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off)) + } + + @Test + fun testIcon_whenDisconnected_isOffState() { + val state = QSTile.BooleanState() + enableBluetooth() + setBluetoothDisconnected() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off)) + } + + @Test + fun testIcon_whenConnected_isOnState() { + val state = QSTile.BooleanState() + enableBluetooth() + setBluetoothConnected() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on)) + } + + @Test + fun testIcon_whenConnecting_isSearchState() { + val state = QSTile.BooleanState() + enableBluetooth() + setBluetoothConnecting() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search)) + } + private class FakeBluetoothTile( qsTileHost: QSTileHost, backgroundLooper: Looper, @@ -114,4 +164,27 @@ class BluetoothTileTest : SysuiTestCase() { restrictionChecked = userRestriction } } + + fun enableBluetooth() { + `when`(bluetoothController.isBluetoothEnabled).thenReturn(true) + } + + fun disableBluetooth() { + `when`(bluetoothController.isBluetoothEnabled).thenReturn(false) + } + + fun setBluetoothDisconnected() { + `when`(bluetoothController.isBluetoothConnecting).thenReturn(false) + `when`(bluetoothController.isBluetoothConnected).thenReturn(false) + } + + fun setBluetoothConnected() { + `when`(bluetoothController.isBluetoothConnecting).thenReturn(false) + `when`(bluetoothController.isBluetoothConnected).thenReturn(true) + } + + fun setBluetoothConnecting() { + `when`(bluetoothController.isBluetoothConnected).thenReturn(false) + `when`(bluetoothController.isBluetoothConnecting).thenReturn(true) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index 55c51b255ac7..e9dfd3ed182b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -36,9 +36,11 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController; import com.android.systemui.statusbar.policy.RotationLockController; @@ -194,6 +196,26 @@ public class RotationLockTileTest extends SysuiTestCase { assertEquals("", mLockTile.getState().secondaryLabel.toString()); } + @Test + public void testIcon_whenDisabled_isOffState() { + QSTile.BooleanState state = new QSTile.BooleanState(); + disableAutoRotation(); + + mLockTile.handleUpdateState(state, /* arg= */ null); + + assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off)); + } + + @Test + public void testIcon_whenEnabled_isOnState() { + QSTile.BooleanState state = new QSTile.BooleanState(); + enableAutoRotation(); + + mLockTile.handleUpdateState(state, /* arg= */ null); + + assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on)); + } + private void enableAutoRotation() { when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java index 5118637ea710..758961658f2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java @@ -65,7 +65,7 @@ public class ConditionMonitorTest extends SysuiTestCase { mCondition3 = spy(new FakeCondition()); mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3)); - mConditionMonitor = new Monitor(mExecutor, mConditions, null /*callbacks*/); + mConditionMonitor = new Monitor(mExecutor, mConditions); } @Test @@ -76,8 +76,10 @@ public class ConditionMonitorTest extends SysuiTestCase { final Monitor monitor = new Monitor( mExecutor, - new HashSet<>(Arrays.asList(overridingCondition, regularCondition)), - new HashSet<>(Arrays.asList(callback))); + new HashSet<>(Arrays.asList(overridingCondition, regularCondition))); + + monitor.addCallback(callback); + mExecutor.runAllReady(); when(overridingCondition.isOverridingCondition()).thenReturn(true); when(overridingCondition.isConditionMet()).thenReturn(true); @@ -123,8 +125,9 @@ public class ConditionMonitorTest extends SysuiTestCase { final Monitor monitor = new Monitor( mExecutor, new HashSet<>(Arrays.asList(overridingCondition, overridingCondition2, - regularCondition)), - new HashSet<>(Arrays.asList(callback))); + regularCondition))); + monitor.addCallback(callback); + mExecutor.runAllReady(); when(overridingCondition.isOverridingCondition()).thenReturn(true); when(overridingCondition.isConditionMet()).thenReturn(true); @@ -174,8 +177,8 @@ public class ConditionMonitorTest extends SysuiTestCase { mock(Monitor.Callback.class); final Condition condition = mock(Condition.class); when(condition.isConditionMet()).thenReturn(true); - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)), - new HashSet<>(Arrays.asList(callback1))); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition))); + monitor.addCallback(callback1); final Monitor.Callback callback2 = mock(Monitor.Callback.class); @@ -186,7 +189,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void addCallback_noConditions_reportAllConditionsMet() { - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>()); final Monitor.Callback callback = mock(Monitor.Callback.class); monitor.addCallback(callback); @@ -196,7 +199,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void addCallback_withMultipleInstancesOfTheSameCallback_registerOnlyOne() { - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>()); final Monitor.Callback callback = mock(Monitor.Callback.class); // Adds the same instance multiple times. @@ -212,8 +215,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void removeCallback_shouldNoLongerReceiveUpdate() { final Condition condition = mock(Condition.class); - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)), - null); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition))); final Monitor.Callback callback = mock(Monitor.Callback.class); monitor.addCallback(callback); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 35f7e064e358..aa2f7c13bc77 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2281,13 +2281,9 @@ public class ActivityManagerService extends IActivityManager.Stub return mAppOpsManager; } - /** - * Provides the basic functionality for activity task related tests when a handler thread is - * given to initialize the dependency members. - */ + /** Provides the basic functionality for unit tests. */ @VisibleForTesting - public ActivityManagerService(Injector injector, ServiceThread handlerThread) { - final boolean hasHandlerThread = handlerThread != null; + ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread) { mInjector = injector; mContext = mInjector.getContext(); mUiContext = null; @@ -2295,33 +2291,27 @@ public class ActivityManagerService extends IActivityManager.Stub mPackageWatchdog = null; mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */); mBatteryStatsService = null; - mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null; + mHandler = new MainHandler(handlerThread.getLooper()); mHandlerThread = handlerThread; - mConstants = hasHandlerThread - ? new ActivityManagerConstants(mContext, this, mHandler) : null; + mConstants = new ActivityManagerConstants(mContext, this, mHandler); final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); mPlatformCompat = null; mProcessList = injector.getProcessList(this); mProcessList.init(this, activeUids, mPlatformCompat); mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null); mPhantomProcessList = new PhantomProcessList(this); - mOomAdjuster = hasHandlerThread - ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null; + mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread); - mIntentFirewall = hasHandlerThread - ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null; + mIntentFirewall = null; mProcessStats = null; mCpHelper = new ContentProviderHelper(this, false); - // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from - // {@link ActivityTaskSupervisor#cleanUpRemovedTaskLocked}. - mServices = hasHandlerThread ? new ActiveServices(this) : null; + mServices = null; mSystemThread = null; mUiHandler = injector.getUiHandler(null /* service */); mUidObserverController = new UidObserverController(mUiHandler); - mUserController = hasHandlerThread ? new UserController(this) : null; - mPendingIntentController = hasHandlerThread - ? new PendingIntentController(handlerThread.getLooper(), mUserController, - mConstants) : null; + mUserController = new UserController(this); + mPendingIntentController = + new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants); mAppRestrictionController = new AppRestrictionController(mContext, this); mProcStartHandlerThread = null; mProcStartHandler = null; @@ -2329,7 +2319,7 @@ public class ActivityManagerService extends IActivityManager.Stub mFactoryTest = FACTORY_TEST_OFF; mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mInternal = new LocalService(); - mPendingStartActivityUids = new PendingStartActivityUids(mContext); + mPendingStartActivityUids = new PendingStartActivityUids(); mUseFifoUiScheduling = false; mEnableOffloadQueue = false; mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue = @@ -2464,7 +2454,7 @@ public class ActivityManagerService extends IActivityManager.Stub } mInternal = new LocalService(); - mPendingStartActivityUids = new PendingStartActivityUids(mContext); + mPendingStartActivityUids = new PendingStartActivityUids(); mTraceErrorLogger = new TraceErrorLogger(); mComponentAliasResolver = new ComponentAliasResolver(this); } @@ -17283,7 +17273,7 @@ public class ActivityManagerService extends IActivityManager.Stub // next top activity on time. This race will fail the following binder transactions WM // sends to the activity. After this race issue between WM/ATMS and AMS is solved, this // workaround can be removed. (b/213288355) - if (isNewPending && mOomAdjuster != null) { // It can be null in unit test. + if (isNewPending) { mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(pid); } // We need to update the network rules for the app coming to the top state so that diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java index bd600571f0a3..da09317add90 100644 --- a/services/core/java/com/android/server/am/PendingStartActivityUids.java +++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java @@ -16,7 +16,6 @@ package com.android.server.am; -import android.content.Context; import android.os.SystemClock; import android.util.Pair; import android.util.Slog; @@ -40,11 +39,6 @@ final class PendingStartActivityUids { // Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the // uid is added. private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray(); - private Context mContext; - - PendingStartActivityUids(Context context) { - mContext = context; - } /** Returns {@code true} if the uid is put to the pending array. Otherwise it existed. */ synchronized boolean add(int uid, int pid) { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index e6bc79679a8f..5a40b30551b6 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -1447,6 +1447,16 @@ abstract public class ManagedServices { } } + @VisibleForTesting + void reregisterService(final ComponentName cn, final int userId) { + // If rebinding a package that died, ensure it still has permission + // after the rebind delay + if (isPackageOrComponentAllowed(cn.getPackageName(), userId) + || isPackageOrComponentAllowed(cn.flattenToString(), userId)) { + registerService(cn, userId); + } + } + /** * Inject a system service into the management list. */ @@ -1545,12 +1555,9 @@ abstract public class ManagedServices { unbindService(this, name, userid); if (!mServicesRebinding.contains(servicesBindingTag)) { mServicesRebinding.add(servicesBindingTag); - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - registerService(name, userid); - } - }, ON_BINDING_DIED_REBIND_DELAY_MS); + mHandler.postDelayed(() -> + reregisterService(name, userid), + ON_BINDING_DIED_REBIND_DELAY_MS); } else { Slog.v(TAG, getCaption() + " not rebinding in user " + userid + " as a previous rebind attempt was made: " + name); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index dc4e1174edf3..c5251296b91e 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -6300,7 +6300,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // starting window is drawn, the transition can start earlier. Exclude finishing and bubble // because it may be a trampoline. if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble - && mVisibleRequested && !mDisplayContent.mAppTransition.isReady() + && !mDisplayContent.mAppTransition.isReady() && !mDisplayContent.mAppTransition.isRunning() && mDisplayContent.isNextTransitionForward()) { // The pending transition state will be cleared after the transition is started, so diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index 3f300bce54c4..67eb9c8911a7 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -221,12 +221,18 @@ class BLASTSyncEngine { private void onTimeout() { if (!mActiveSyncs.contains(mSyncId)) return; + boolean allFinished = true; for (int i = mRootMembers.size() - 1; i >= 0; --i) { final WindowContainer<?> wc = mRootMembers.valueAt(i); if (!wc.isSyncFinished()) { + allFinished = false; Slog.i(TAG, "Unfinished container: " + wc); } } + if (allFinished && !mReady) { + Slog.w(TAG, "Sync group " + mSyncId + " timed-out because not ready. If you see " + + "this, please file a bug."); + } finishNow(); } } diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 6f69e0324b0b..1e1cbaf35b61 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1977,23 +1977,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason) { - mService.deferWindowLayout(); final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); + final Task task = r.getTask(); + final Task rootTask; - try { - final Task task = r.getTask(); - - // Create a transition now to collect the current pinned Task dismiss. Only do the - // create here as the Task (trigger) to enter PIP is not ready yet. - final TransitionController transitionController = task.mTransitionController; - Transition newTransition = null; - if (transitionController.isCollecting()) { - transitionController.setReady(task, false /* ready */); - } else if (transitionController.getTransitionPlayer() != null) { - newTransition = transitionController.createTransition(TRANSIT_PIP); - } + Transition newTransition = null; + // Create a transition now to collect the current pinned Task dismiss. Only do the + // create here as the Task (trigger) to enter PIP is not ready yet. + final TransitionController transitionController = task.mTransitionController; + if (!transitionController.isCollecting() + && transitionController.getTransitionPlayer() != null) { + newTransition = transitionController.createTransition(TRANSIT_PIP); + } + transitionController.deferTransitionReady(); + mService.deferWindowLayout(); + try { // This will change the root pinned task's windowing mode to its original mode, ensuring // we only have one root task that is in pinned mode. final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); @@ -2009,7 +2009,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final TaskFragment organizedTf = r.getOrganizedTaskFragment(); // TODO: Does it make sense to only count non-finishing activities? final boolean singleActivity = task.getActivityCount() == 1; - final Task rootTask; if (singleActivity) { rootTask = task; @@ -2075,6 +2074,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> oldTopActivity.mRequestForceTransition = true; } } + + transitionController.collect(rootTask); + // The intermediate windowing mode to be set on the ActivityRecord later. // This needs to happen before the re-parenting, otherwise we will always set the // ActivityRecord to be fullscreen. @@ -2085,13 +2087,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> rootTask.reparent(taskDisplayArea, true /* onTop */); } - // The new PIP Task is ready, start the transition before updating the windowing mode. - if (newTransition != null) { - transitionController.requestStartTransition(newTransition, rootTask, - null /* remoteTransition */, null /* displayChange */); - } - transitionController.collect(rootTask); - // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating // TODO(task-org): Figure-out more structured way to do this long term. @@ -2125,9 +2120,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } finally { mService.continueWindowLayout(); + try { + ensureActivitiesVisible(null, 0, false /* preserveWindows */); + } finally { + transitionController.continueTransitionReady(); + } + } + + if (newTransition != null) { + // Request at end since we want task-organizer events from ensureActivitiesVisible + // to be recognized. + transitionController.requestStartTransition(newTransition, rootTask, + null /* remoteTransition */, null /* displayChange */); + // A new transition was created just for this operations. Since the operation is + // complete, mark it as ready. + newTransition.setReady(rootTask, true /* ready */); } - ensureActivitiesVisible(null, 0, false /* preserveWindows */); resumeFocusedTasksTopActivities(); notifyActivityPipModeChanged(r.getTask(), r); diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index c44f08cbd1eb..0bdc00b8b74d 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -713,6 +713,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = mParticipants.size() - 1; i >= 0; --i) { final WindowContainer wc = mParticipants.valueAt(i); if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue; + // don't include transient launches, though, since those are only temporarily visible. + if (mTransientLaunches != null && wc.asActivityRecord() != null + && mTransientLaunches.containsKey(wc.asActivityRecord())) continue; mVisibleAtTransitionEndTokens.add(wc.asWindowToken()); } @@ -1595,6 +1598,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } /** + * This transition will be considered not-ready until a corresponding call to + * {@link #continueTransitionReady} + */ + void deferTransitionReady() { + ++mReadyTracker.mDeferReadyDepth; + } + + /** This undoes one call to {@link #deferTransitionReady}. */ + void continueTransitionReady() { + --mReadyTracker.mDeferReadyDepth; + } + + /** * The transition sync mechanism has 2 parts: * 1. Whether all WM operations for a particular transition are "ready" (eg. did the app * launch or stop or get a new configuration?). @@ -1624,6 +1640,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private boolean mReadyOverride = false; /** + * When non-zero, this transition is forced not-ready (even over setAllReady()). Use this + * (via deferTransitionReady/continueTransitionReady) for situations where we want to do + * bulk operations which could trigger surface-placement but the existing ready-state + * isn't known. + */ + private int mDeferReadyDepth = 0; + + /** * Adds a ready-group. Any setReady calls in this subtree will be tracked together. For * now these are only DisplayContents. */ @@ -1664,7 +1688,13 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe boolean allReady() { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b " + "override=%b states=[%s]", mUsed, mReadyOverride, groupsToString()); + // If the readiness has never been touched, mUsed will be false. We never want to + // consider a transition ready if nothing has been reported on it. if (!mUsed) return false; + // If we are deferring readiness, we never report ready. This is usually temporary. + if (mDeferReadyDepth > 0) return false; + // Next check all the ready groups to see if they are ready. We can short-cut this if + // ready-override is set (which is treated as "everything is marked ready"). if (mReadyOverride) return true; for (int i = mReadyGroups.size() - 1; i >= 0; --i) { final WindowContainer wc = mReadyGroups.keyAt(i); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index c1c390eea932..417e6ed6a07e 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -455,6 +455,24 @@ class TransitionController { setReady(wc, true); } + /** @see Transition#deferTransitionReady */ + void deferTransitionReady() { + if (!isShellTransitionsEnabled()) return; + if (mCollectingTransition == null) { + throw new IllegalStateException("No collecting transition to defer readiness for."); + } + mCollectingTransition.deferTransitionReady(); + } + + /** @see Transition#continueTransitionReady */ + void continueTransitionReady() { + if (!isShellTransitionsEnabled()) return; + if (mCollectingTransition == null) { + throw new IllegalStateException("No collecting transition to defer readiness for."); + } + mCollectingTransition.continueTransitionReady(); + } + /** @see Transition#finishTransition */ void finishTransition(@NonNull IBinder token) { // It is usually a no-op but make sure that the metric consumer is removed. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 2f4dc515fd11..3bc6dbdbf1dd 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1062,7 +1062,6 @@ public class WindowManagerService extends IWindowManager.Stub Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory; Supplier<SurfaceControl.Transaction> mTransactionFactory; - final Supplier<Surface> mSurfaceFactory; private final SurfaceControl.Transaction mTransaction; @@ -1145,7 +1144,7 @@ public class WindowManagerService extends IWindowManager.Stub final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm) { return main(context, im, showBootMsgs, onlyCore, policy, atm, - new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new, + new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, SurfaceControl.Builder::new); } @@ -1158,12 +1157,11 @@ public class WindowManagerService extends IWindowManager.Stub final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, - Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { final WindowManagerService[] wms = new WindowManagerService[1]; DisplayThread.getHandler().runWithScissors(() -> wms[0] = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, - atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory, + atm, displayWindowSettingsProvider, transactionFactory, surfaceControlFactory), 0); return wms[0]; } @@ -1188,7 +1186,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, - Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { installLock(this, INDEX_WINDOW); mGlobalLock = atm.getGlobalLock(); @@ -1228,7 +1225,6 @@ public class WindowManagerService extends IWindowManager.Stub mSurfaceControlFactory = surfaceControlFactory; mTransactionFactory = transactionFactory; - mSurfaceFactory = surfaceFactory; mTransaction = mTransactionFactory.get(); mPolicy = policy; diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 2f054b004d42..8ba9af0d85d0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -840,6 +840,156 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void reregisterService_checksAppIsApproved_pkg() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_PACKAGE); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a", 0, true); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsApproved_pkg_secondary() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_PACKAGE); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a", 0, false); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsApproved_cn() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a/a", 0, true); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsApproved_cn_secondary() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a/a", 0, false); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsNotApproved_cn_secondary() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("b/b", 0, false); + + service.reregisterService(cn, 0); + + assertFalse(service.isBound(cn, 0)); + } + + @Test public void unbindOtherUserServices() throws PackageManager.NameNotFoundException { Context context = mock(Context.class); PackageManager pm = mock(PackageManager.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index 716612c70aef..75ecfd870eb2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; @@ -34,6 +35,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -284,10 +286,14 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { .setCreateActivity(true).build().getTopMostActivity(); activity2.getTask().setResumedActivity(activity2, "test"); - mAtm.mAmInternal.deletePendingTopUid(activity1.getUid(), Long.MAX_VALUE); + final int[] pendingTopUid = new int[1]; + doAnswer(invocation -> { + pendingTopUid[0] = invocation.getArgument(0); + return null; + }).when(mAtm.mAmInternal).addPendingTopUid(anyInt(), anyInt(), any()); clearInvocations(mAtm); activity1.moveFocusableActivityToTop("test"); - assertTrue(mAtm.mAmInternal.isPendingTopUid(activity1.getUid())); + assertEquals(activity1.getUid(), pendingTopUid[0]); verify(mAtm).updateOomAdj(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 8ef9ada8995f..a3b881c8b270 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -657,7 +657,7 @@ public class RootWindowContainerTests extends WindowTestsBase { doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); - mAtm.setBooted(true); + setBooted(mAtm); // Trigger resume on all displays mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -685,7 +685,7 @@ public class RootWindowContainerTests extends WindowTestsBase { doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); - mAtm.setBooted(true); + setBooted(mAtm); // Trigger resume on all displays mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -771,17 +771,10 @@ public class RootWindowContainerTests extends WindowTestsBase { @Test public void testNotStartHomeBeforeBoot() { final int displayId = 1; - final boolean isBooting = mAtm.mAmInternal.isBooting(); - final boolean isBooted = mAtm.mAmInternal.isBooted(); - try { - mAtm.mAmInternal.setBooting(false); - mAtm.mAmInternal.setBooted(false); - mRootWindowContainer.onDisplayAdded(displayId); - verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); - } finally { - mAtm.mAmInternal.setBooting(isBooting); - mAtm.mAmInternal.setBooted(isBooted); - } + doReturn(false).when(mAtm).isBooting(); + doReturn(false).when(mAtm).isBooted(); + mRootWindowContainer.onDisplayAdded(displayId); + verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index dc9a62554fdb..18c4eb964a3a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -66,7 +66,6 @@ import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.Log; import android.view.InputChannel; -import android.view.Surface; import android.view.SurfaceControl; import com.android.dx.mockito.inline.extended.StaticMockitoSession; @@ -77,7 +76,6 @@ import com.android.server.LockGuard; import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService; -import com.android.server.appop.AppOpsService; import com.android.server.display.color.ColorDisplayService; import com.android.server.firewall.IntentFirewall; import com.android.server.input.InputManagerService; @@ -94,10 +92,8 @@ import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.quality.Strictness; -import java.io.File; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; /** * JUnit test rule to correctly setting up system services like {@link WindowManagerService} @@ -125,13 +121,11 @@ public class SystemServicesTestRule implements TestRule { private Description mDescription; private Context mContext; private StaticMockitoSession mMockitoSession; - private ActivityManagerService mAmService; private ActivityTaskManagerService mAtmService; private WindowManagerService mWmService; private WindowState.PowerManagerWrapper mPowerManagerWrapper; private InputManagerService mImService; private InputChannel mInputChannel; - private Supplier<Surface> mSurfaceFactory = () -> mock(Surface.class); /** * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls. */ @@ -289,29 +283,9 @@ public class SystemServicesTestRule implements TestRule { } private void setUpActivityTaskManagerService() { - // ActivityManagerService - mAmService = new ActivityManagerService(new AMTestInjector(mContext), null /* thread */); - spyOn(mAmService); - doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager(); - doNothing().when(mAmService).grantImplicitAccess( - anyInt(), any(), anyInt(), anyInt()); - // ActivityManagerInternal - final ActivityManagerInternal amInternal = mAmService.mInternal; - spyOn(amInternal); - doNothing().when(amInternal).trimApplications(); - doNothing().when(amInternal).scheduleAppGcs(); - doNothing().when(amInternal).updateCpuStats(); - doNothing().when(amInternal).updateOomAdj(); - doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); - doNothing().when(amInternal).updateActivityUsageStats( - any(), anyInt(), anyInt(), any(), any()); - doNothing().when(amInternal).startProcess( - any(), any(), anyBoolean(), anyBoolean(), any(), any()); - doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt()); - doNothing().when(amInternal).broadcastGlobalConfigurationChanged(anyInt(), anyBoolean()); - doNothing().when(amInternal).cleanUpServices(anyInt(), any(), any()); - doNothing().when(amInternal).reportCurKeyguardUsageEvent(anyBoolean()); + final ActivityManagerInternal amInternal = + mock(ActivityManagerInternal.class, withSettings().stubOnly()); doReturn(UserHandle.USER_SYSTEM).when(amInternal).getCurrentUserId(); doReturn(TEST_USER_PROFILE_IDS).when(amInternal).getCurrentProfileIds(); doReturn(true).when(amInternal).isUserRunning(anyInt(), anyInt()); @@ -320,7 +294,9 @@ public class SystemServicesTestRule implements TestRule { doReturn(false).when(amInternal).isActivityStartsLoggingEnabled(); LocalServices.addService(ActivityManagerInternal.class, amInternal); - mAtmService = new TestActivityTaskManagerService(mContext, mAmService); + final ActivityManagerService amService = + mock(ActivityManagerService.class, withSettings().stubOnly()); + mAtmService = new TestActivityTaskManagerService(mContext, amService); LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal()); } @@ -334,7 +310,7 @@ public class SystemServicesTestRule implements TestRule { mWmService = WindowManagerService.main( mContext, mImService, false, false, wmPolicy, mAtmService, testDisplayWindowSettingsProvider, StubTransaction::new, - () -> mSurfaceFactory.get(), (unused) -> new MockSurfaceControlBuilder()); + (unused) -> new MockSurfaceControlBuilder()); spyOn(mWmService); spyOn(mWmService.mRoot); // Invoked during {@link ActivityStack} creation. @@ -355,7 +331,7 @@ public class SystemServicesTestRule implements TestRule { null, null, mTransaction, mWmService.mPowerManagerInternal); mWmService.onInitReady(); - mAmService.setWindowManager(mWmService); + mAtmService.setWindowManager(mWmService); mWmService.mDisplayEnabled = true; mWmService.mDisplayReady = true; // Set configuration for default display @@ -454,10 +430,6 @@ public class SystemServicesTestRule implements TestRule { .spiedInstance(sWakeLock).stubOnly()); } - void setSurfaceFactory(Supplier<Surface> factory) { - mSurfaceFactory = factory; - } - void cleanupWindowManagerHandlers() { final WindowManagerService wm = getWindowManagerService(); if (wm == null) { @@ -618,32 +590,4 @@ public class SystemServicesTestRule implements TestRule { doReturn(true).when(controller).checkKeyguardVisibility(any()); } } - - // TODO: Can we just mock this? - private static class AMTestInjector extends ActivityManagerService.Injector { - - AMTestInjector(Context context) { - super(context); - } - - @Override - public Context getContext() { - return getInstrumentation().getTargetContext(); - } - - @Override - public AppOpsService getAppOpsService(File file, Handler handler) { - return null; - } - - @Override - public Handler getUiHandler(ActivityManagerService service) { - return UiThread.getHandler(); - } - - @Override - public boolean isNetworkRestrictedForUid(int uid) { - return false; - } - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 6c1c0865446e..9dedc850defc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -955,6 +955,29 @@ public class TransitionTests extends WindowTestsBase { verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false)); } + @Test + public void testNotReadyPushPop() { + final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); + final TransitionController controller = new TransitionController(mAtm, snapshotController); + final ITransitionPlayer player = new ITransitionPlayer.Default(); + controller.registerTransitionPlayer(player, null /* appThread */); + final Transition openTransition = controller.createTransition(TRANSIT_OPEN); + + // Start out with task2 visible and set up a transition that closes task2 and opens task1 + final Task task1 = createTask(mDisplayContent); + openTransition.collectExistenceChange(task1); + + assertFalse(openTransition.allReady()); + + openTransition.setAllReady(); + + openTransition.deferTransitionReady(); + assertFalse(openTransition.allReady()); + + openTransition.continueTransitionReady(); + assertTrue(openTransition.allReady()); + } + private static void makeTaskOrganized(Task... tasks) { final ITaskOrganizer organizer = mock(ITaskOrganizer.class); for (Task t : tasks) { |