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

Commit 1a49fb57 authored by Jon Miranda's avatar Jon Miranda
Browse files

Transform closing window to icon on workspace using adaptive icons.

With adaptive icons, we can have FloatingIconView match the shape
and size of the closing window, regardless of the icon shape.

FloatingIconView starts off as a rounded rect (same corners as task view)
and then morphs into the icon shape using FolderShape#createRevealAnimator
in reverse.

Decided to add FeatureFlag.ADAPTIVE_ICON_WINDOW_ANIM since there are still
some issues with folders, badges, and a visible jump when swapping the
FloatingIconView with the original icon.

Bug: 123900446

Change-Id: I94969eea6d5f4b932a84a11eb403611276042b46
parent 8d6c7da9
Loading
Loading
Loading
Loading
+4 −6
Original line number Diff line number Diff line
@@ -104,13 +104,11 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
                .sourceComponent;

        final View workspaceView = activity.getWorkspace().getFirstMatchForAppClose(component);
        final FloatingIconView floatingView = workspaceView == null ? null
                : new FloatingIconView(activity);
        final Rect iconLocation = new Rect();
        if (floatingView != null) {
            floatingView.matchPositionOf(activity, workspaceView, true /* hideOriginal */,
                    iconLocation);
        }
        final FloatingIconView floatingView = workspaceView == null ? null
                : FloatingIconView.getFloatingIconView(activity, workspaceView,
                true /* hideOriginal */, false /* useDrawableAsIs */,
                activity.getDeviceProfile().getAspectRatioWithInsets(), iconLocation, null);

        return new HomeAnimationFactory() {
            @Nullable
+16 −7
Original line number Diff line number Diff line
@@ -972,27 +972,36 @@ public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
        final RectF currentRect = new RectF();

        final View floatingView = homeAnimationFactory.getFloatingView();
        final boolean isFloatingIconView = floatingView instanceof FloatingIconView;

        ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
        if (floatingView instanceof FloatingIconView) {
        if (isFloatingIconView) {
            anim.addListener((FloatingIconView) floatingView);
        }

        // We want the window alpha to be 0 once this threshold is met, so that the
        // FolderIconView can be seen morphing into the icon shape.
        final float windowAlphaThreshold = isFloatingIconView ? 0.75f : 1f;
        anim.addUpdateListener(animation -> {
            float progress = animation.getAnimatedFraction();
            float interpolatedProgress = Interpolators.ACCEL_2.getInterpolation(progress);
            float interpolatedProgress = Interpolators.ACCEL_1_5.getInterpolation(progress);
            // Initially go towards original target (task view in recents),
            // but accelerate towards the final target.
            // TODO: This is technically not correct. Instead, motion should continue at
            // the released velocity but accelerate towards the target.
            targetRect.set(rectFEvaluator.evaluate(interpolatedProgress,
                    originalTarget, finalTarget));
            currentRect.set(rectFEvaluator.evaluate(progress, startRect, targetRect));
            float alpha = 1 - interpolatedProgress;
            mTransformParams.setCurrentRectAndTargetAlpha(currentRect, alpha)
            currentRect.set(rectFEvaluator.evaluate(interpolatedProgress, startRect, targetRect));

            float iconAlpha = Utilities.mapToRange(interpolatedProgress, 0,
                    windowAlphaThreshold, 0f, 1f, Interpolators.LINEAR);
            mTransformParams.setCurrentRectAndTargetAlpha(currentRect, 1f - iconAlpha)
                    .setSyncTransactionApplier(mSyncTransactionApplier);
            mClipAnimationHelper.applyTransform(targetSet, mTransformParams);

            if (floatingView instanceof FloatingIconView) {
                ((FloatingIconView) floatingView).update(currentRect, 1f - alpha);
            if (isFloatingIconView) {
                ((FloatingIconView) floatingView).update(currentRect, iconAlpha, progress,
                        windowAlphaThreshold);
            }
        });
        anim.addListener(new AnimationSuccessListener() {
+3 −4
Original line number Diff line number Diff line
@@ -403,9 +403,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
    private void playIconAnimators(AnimatorSet appOpenAnimator, View v, Rect windowTargetBounds,
            boolean toggleVisibility) {
        final boolean isBubbleTextView = v instanceof BubbleTextView;
        if (mFloatingView == null) {
            mFloatingView = new FloatingIconView(mLauncher);
        } else {
        if (mFloatingView != null) {
            mFloatingView.setTranslationX(0);
            mFloatingView.setTranslationY(0);
            mFloatingView.setScaleX(1);
@@ -414,7 +412,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
            mFloatingView.setBackground(null);
        }
        Rect rect = new Rect();
        mFloatingView.matchPositionOf(mLauncher, v, toggleVisibility, rect);
        mFloatingView = FloatingIconView.getFloatingIconView(mLauncher, v, toggleVisibility,
                true /* useDrawableAsIs */, -1 /* aspectRatio */, rect, mFloatingView);

        int viewLocationStart = mIsRtl ? windowTargetBounds.width() - rect.right : rect.left;
        LayoutParams lp = (LayoutParams) mFloatingView.getLayoutParams();
+6 −0
Original line number Diff line number Diff line
@@ -611,6 +611,12 @@ public class DeviceProfile {
        outBounds.right = outBounds.left + (getCellSize().x * spanX);
    }

    public float getAspectRatioWithInsets() {
        int w = widthPx - mInsets.left - mInsets.right;
        int h = heightPx - mInsets.top - mInsets.bottom;
        return ((float) Math.max(w, h)) / Math.min(w, h);
    }

    private static Context getContext(Context c, int orientation) {
        Configuration context = new Configuration(c.getResources().getConfiguration());
        context.orientation = orientation;
+62 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -37,6 +38,7 @@ import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
@@ -55,14 +57,24 @@ import android.util.TypedValue;
import android.view.View;
import android.view.animation.Interpolator;

import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.folder.FolderIcon;
import com.android.launcher3.shortcuts.DeepShortcutManager;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.shortcuts.ShortcutInfoCompat;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.PendingAddShortcutInfo;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
@@ -556,6 +568,7 @@ public final class Utilities {
    public static void getLocationBoundsForView(Launcher launcher, View v, Rect outRect) {
        final DragLayer dragLayer = launcher.getDragLayer();
        final boolean isBubbleTextView = v instanceof BubbleTextView;
        final boolean isFolderIcon = v instanceof FolderIcon;
        final Rect rect = new Rect();

        final boolean fromDeepShortcutView = v.getParent() instanceof DeepShortcutView;
@@ -563,15 +576,14 @@ public final class Utilities {
            // Deep shortcut views have their icon drawn in a separate view.
            DeepShortcutView view = (DeepShortcutView) v.getParent();
            dragLayer.getDescendantRectRelativeToSelf(view.getIconView(), rect);
        } else if (isBubbleTextView && v.getTag() instanceof ItemInfo
        } else if ((isBubbleTextView || isFolderIcon) && v.getTag() instanceof ItemInfo
                && (((ItemInfo) v.getTag()).container == CONTAINER_DESKTOP
                || ((ItemInfo) v.getTag()).container == CONTAINER_HOTSEAT)) {
            BubbleTextView btv = (BubbleTextView) v;
            CellLayout pageViewIsOn = ((CellLayout) btv.getParent().getParent());
            CellLayout pageViewIsOn = ((CellLayout) v.getParent().getParent());
            int pageNum = launcher.getWorkspace().indexOfChild(pageViewIsOn);

            DeviceProfile dp = launcher.getDeviceProfile();
            ItemInfo info = ((ItemInfo) btv.getTag());
            ItemInfo info = ((ItemInfo) v.getTag());
            dp.getItemLocation(info.cellX, info.cellY, info.spanX, info.spanY,
                    info.container, pageNum - launcher.getCurrentWorkspaceScreen(), rect);
        } else {
@@ -597,4 +609,49 @@ public final class Utilities {
            context.unregisterReceiver(receiver);
        } catch (IllegalArgumentException e) {}
    }

    /**
     * Returns the full drawable for {@param info}.
     * @param outObj this is set to the internal data associated with {@param info},
     *               eg {@link LauncherActivityInfo} or {@link ShortcutInfoCompat}.
     */
    public static Drawable getFullDrawable(Launcher launcher, ItemInfo info, int width, int height,
            Object[] outObj) {
        LauncherAppState appState = LauncherAppState.getInstance(launcher);
        if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
            LauncherActivityInfo activityInfo = LauncherAppsCompat.getInstance(launcher)
                    .resolveActivity(info.getIntent(), info.user);
            outObj[0] = activityInfo;
            return (activityInfo != null) ? appState.getIconCache()
                    .getFullResIcon(activityInfo, false) : null;
        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
            if (info instanceof PendingAddShortcutInfo) {
                ShortcutConfigActivityInfo activityInfo =
                        ((PendingAddShortcutInfo) info).activityInfo;
                outObj[0] = activityInfo;
                return activityInfo.getFullResIcon(appState.getIconCache());
            }
            ShortcutKey key = ShortcutKey.fromItemInfo(info);
            DeepShortcutManager sm = DeepShortcutManager.getInstance(launcher);
            List<ShortcutInfoCompat> si = sm.queryForFullDetails(
                    key.componentName.getPackageName(), Arrays.asList(key.getId()), key.user);
            if (si.isEmpty()) {
                return null;
            } else {
                outObj[0] = si.get(0);
                return sm.getShortcutIconDrawable(si.get(0),
                        appState.getInvariantDeviceProfile().fillResIconDpi);
            }
        } else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
            FolderAdaptiveIcon icon = FolderAdaptiveIcon.createFolderAdaptiveIcon(
                    launcher, info.id, new Point(width, height));
            if (icon == null) {
                return null;
            }
            outObj[0] = icon;
            return icon;
        } else {
            return null;
        }
    }
}
Loading