Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02c0c924 authored by Nick Chameyev's avatar Nick Chameyev Committed by Android (Google) Code Review
Browse files

Merge "Add unfold animation to launcher icons and widgets" into sc-v2-dev

parents 148b33b5 5e8dbe77
Loading
Loading
Loading
Loading
+54 −0
Original line number Diff line number Diff line
@@ -36,9 +36,13 @@ import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SY
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.hardware.SensorManager;
import android.hardware.devicestate.DeviceStateManager;
import android.view.HapticFeedbackConstants;
import android.view.View;

import androidx.annotation.Nullable;

import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
@@ -75,9 +79,14 @@ import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.LauncherUnfoldAnimationController;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.QuickstepOnboardingPrefs;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.unfold.UnfoldTransitionFactory;
import com.android.unfold.UnfoldTransitionProgressProvider;
import com.android.unfold.config.UnfoldTransitionConfig;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -97,10 +106,51 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
    private FixedContainerItems mAllAppsPredictions;
    private HotseatPredictionController mHotseatPredictionController;

    @Nullable
    private LauncherUnfoldAnimationController mLauncherUnfoldAnimationController;

    @Override
    protected void setupViews() {
        super.setupViews();
        mHotseatPredictionController = new HotseatPredictionController(this);

        final UnfoldTransitionConfig config = UnfoldTransitionFactory.createConfig(this);
        if (config.isEnabled()) {
            final UnfoldTransitionProgressProvider unfoldTransitionProgressProvider =
                    UnfoldTransitionFactory.createUnfoldTransitionProgressProvider(
                            this,
                            config,
                            ProxyScreenStatusProvider.INSTANCE,
                            getSystemService(DeviceStateManager.class),
                            getSystemService(SensorManager.class),
                            getMainThreadHandler(),
                            getMainExecutor()
                    );

            mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
                    this,
                    getWindowManager(),
                    unfoldTransitionProgressProvider
            );
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mLauncherUnfoldAnimationController != null) {
            mLauncherUnfoldAnimationController.onResume();
        }
    }

    @Override
    protected void onPause() {
        if (mLauncherUnfoldAnimationController != null) {
            mLauncherUnfoldAnimationController.onPause();
        }

        super.onPause();
    }

    @Override
@@ -231,6 +281,10 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
    public void onDestroy() {
        super.onDestroy();
        mHotseatPredictionController.destroy();

        if (mLauncherUnfoldAnimationController != null) {
            mLauncherUnfoldAnimationController.onDestroy();
        }
    }

    @Override
+7 −0
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ import com.android.quickstep.inputconsumers.TaskbarStashInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.AssistantUtilities;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.ProxyScreenStatusProvider;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.systemui.plugins.OverscrollPlugin;
import com.android.systemui.plugins.PluginListener;
@@ -268,6 +269,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
            MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb));
        }

        @BinderThread
        @Override
        public void onScreenTurnedOn() {
            MAIN_EXECUTOR.execute(ProxyScreenStatusProvider.INSTANCE::onScreenTurnedOn);
        }

        @Override
        public void onRotationProposal(int rotation, boolean isValid) {
            executeForTaskbarManager(() -> mTaskbarManager.onRotationProposal(rotation, isValid));
+122 −0
Original line number Diff line number Diff line
/*
 * 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.
 */
package com.android.quickstep.util;

import android.view.ViewTreeObserver;
import android.view.WindowManager;

import com.android.launcher3.Launcher;
import com.android.unfold.UnfoldTransitionProgressProvider;
import com.android.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener;

/**
 * Controls animations that are happening during unfolding foldable devices
 */
public class LauncherUnfoldAnimationController {

    private final Launcher mLauncher;
    private final UnfoldTransitionProgressProvider mUnfoldTransitionProgressProvider;
    private final UnfoldMoveFromCenterWorkspaceAnimator mMoveFromCenterWorkspaceAnimation;

    private final AnimationListener mAnimationListener = new AnimationListener();

    private boolean mIsTransitionRunning = false;
    private boolean mIsReadyToPlayAnimation = false;

    public LauncherUnfoldAnimationController(
            Launcher launcher,
            WindowManager windowManager,
            UnfoldTransitionProgressProvider unfoldTransitionProgressProvider) {
        mLauncher = launcher;
        mUnfoldTransitionProgressProvider = unfoldTransitionProgressProvider;
        mMoveFromCenterWorkspaceAnimation = new UnfoldMoveFromCenterWorkspaceAnimator(launcher,
                windowManager);
        mUnfoldTransitionProgressProvider.addCallback(mAnimationListener);
    }

    /**
     * Called when launcher is resumed
     */
    public void onResume() {
        final ViewTreeObserver obs = mLauncher.getWorkspace().getViewTreeObserver();
        obs.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                if (obs.isAlive()) {
                    onPreDrawAfterResume();
                    obs.removeOnPreDrawListener(this);
                }
                return true;
            }
        });
    }

    /**
     * Called when launcher activity is paused
     */
    public void onPause() {
        mIsReadyToPlayAnimation = false;

        if (mIsTransitionRunning) {
            mIsTransitionRunning = false;
            mMoveFromCenterWorkspaceAnimation.onTransitionFinished();
        }
    }

    /**
     * Called when launcher activity is destroyed
     */
    public void onDestroy() {
        mUnfoldTransitionProgressProvider.removeCallback(mAnimationListener);
    }

    /**
     * Called after performing layouting of the views after configuration change
     */
    private void onPreDrawAfterResume() {
        mIsReadyToPlayAnimation = true;

        if (mIsTransitionRunning) {
            mMoveFromCenterWorkspaceAnimation.onTransitionStarted();
        }
    }

    private class AnimationListener implements TransitionProgressListener {

        @Override
        public void onTransitionStarted() {
            mIsTransitionRunning = true;

            if (mIsReadyToPlayAnimation) {
                mMoveFromCenterWorkspaceAnimation.onTransitionStarted();
            }
        }

        @Override
        public void onTransitionFinished() {
            if (mIsReadyToPlayAnimation) {
                mMoveFromCenterWorkspaceAnimation.onTransitionFinished();
            }

            mIsTransitionRunning = false;
        }

        @Override
        public void onTransitionProgress(float progress) {
            mMoveFromCenterWorkspaceAnimation.onTransitionProgress(progress);
        }
    }
}
+51 −0
Original line number Diff line number Diff line
/*
 * 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.
 */
package com.android.quickstep.util;

import androidx.annotation.NonNull;

import com.android.unfold.updates.screen.ScreenStatusProvider;

import java.util.ArrayList;
import java.util.List;

/**
 * Screen status provider implementation that exposes methods to provide screen
 * status updates to listeners. It is used to receive screen turned on event from
 * SystemUI to Launcher.
 */
public class ProxyScreenStatusProvider implements ScreenStatusProvider {

    public static final ProxyScreenStatusProvider INSTANCE = new ProxyScreenStatusProvider();
    private final List<ScreenListener> mListeners = new ArrayList<>();

    /**
     * Called when the screen is on and ready (windows are drawn and screen blocker is removed)
     */
    public void onScreenTurnedOn() {
        mListeners.forEach(ScreenListener::onScreenTurnedOn);
    }

    @Override
    public void addCallback(@NonNull ScreenListener listener) {
        mListeners.add(listener);
    }

    @Override
    public void removeCallback(@NonNull ScreenListener listener) {
        mListeners.remove(listener);
    }
}
+140 −0
Original line number Diff line number Diff line
/*
 * 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.
 */
package com.android.quickstep.util;

import android.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;

import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.widget.NavigableAppWidgetHostView;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator;
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.TranslationApplier;
import com.android.unfold.UnfoldTransitionProgressProvider;

import java.util.HashMap;
import java.util.Map;

/**
 * Animation that moves launcher icons and widgets from center to the sides (final position)
 */
public class UnfoldMoveFromCenterWorkspaceAnimator
        implements UnfoldTransitionProgressProvider.TransitionProgressListener {

    private final Launcher mLauncher;
    private final UnfoldMoveFromCenterAnimator mMoveFromCenterAnimation;

    private final Map<ViewGroup, Boolean> mOriginalClipToPadding = new HashMap<>();
    private final Map<ViewGroup, Boolean> mOriginalClipChildren = new HashMap<>();

    public UnfoldMoveFromCenterWorkspaceAnimator(Launcher launcher, WindowManager windowManager) {
        mLauncher = launcher;
        mMoveFromCenterAnimation = new UnfoldMoveFromCenterAnimator(windowManager,
                new WorkspaceViewsTranslationApplier());
    }

    @Override
    public void onTransitionStarted() {
        mMoveFromCenterAnimation.updateDisplayProperties();

        Workspace workspace = mLauncher.getWorkspace();
        Hotseat hotseat = mLauncher.getHotseat();

        // App icons and widgets
        workspace
                .forEachVisiblePage(page -> {
                    final CellLayout cellLayout = (CellLayout) page;
                    ShortcutAndWidgetContainer itemsContainer = cellLayout
                            .getShortcutsAndWidgets();
                    disableClipping(cellLayout);

                    for (int i = 0; i < itemsContainer.getChildCount(); i++) {
                        View child = itemsContainer.getChildAt(i);
                        mMoveFromCenterAnimation.registerViewForAnimation(child);
                    }
                });

        disableClipping(workspace);

        // Hotseat icons
        ViewGroup hotseatIcons = hotseat.getShortcutsAndWidgets();
        disableClipping(hotseat);

        for (int i = 0; i < hotseatIcons.getChildCount(); i++) {
            View child = hotseatIcons.getChildAt(i);
            mMoveFromCenterAnimation.registerViewForAnimation(child);
        }

        onTransitionProgress(0f);
    }

    @Override
    public void onTransitionProgress(float progress) {
        mMoveFromCenterAnimation.onTransitionProgress(progress);
    }

    @Override
    public void onTransitionFinished() {
        mMoveFromCenterAnimation.onTransitionFinished();
        mMoveFromCenterAnimation.clearRegisteredViews();

        restoreClipping(mLauncher.getWorkspace());
        mLauncher.getWorkspace().forEachVisiblePage(page -> restoreClipping((CellLayout) page));
        restoreClipping(mLauncher.getHotseat());

        mOriginalClipChildren.clear();
        mOriginalClipToPadding.clear();
    }

    private void disableClipping(ViewGroup view) {
        mOriginalClipToPadding.put(view, view.getClipToPadding());
        mOriginalClipChildren.put(view, view.getClipChildren());
        view.setClipToPadding(false);
        view.setClipChildren(false);
    }

    private void restoreClipping(ViewGroup view) {
        final Boolean originalClipToPadding = mOriginalClipToPadding.get(view);
        if (originalClipToPadding != null) {
            view.setClipToPadding(originalClipToPadding);
        }
        final Boolean originalClipChildren = mOriginalClipChildren.get(view);
        if (originalClipChildren != null) {
            view.setClipChildren(originalClipChildren);
        }
    }

    private static class WorkspaceViewsTranslationApplier implements TranslationApplier {

        @Override
        public void apply(@NonNull View view, float x, float y) {
            if (view instanceof NavigableAppWidgetHostView) {
                ((NavigableAppWidgetHostView) view).setTranslationForMoveFromCenterAnimation(x, y);
            } else if (view instanceof BubbleTextView) {
                ((BubbleTextView) view).setTranslationForMoveFromCenterAnimation(x, y);
            } else {
                view.setTranslationX(x);
                view.setTranslationY(y);
            }
        }
    }
}
Loading