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

Commit b5670b76 authored by Evan Rosky's avatar Evan Rosky Committed by Android (Google) Code Review
Browse files

Merge "Move more utils into TransitionUtil" into udc-dev

parents 09eacb96 d667c693
Loading
Loading
Loading
Loading
+207 −0
Original line number Diff line number Diff line
@@ -16,18 +16,40 @@

package com.android.wm.shell.util;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
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_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;

import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.SparseBooleanArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;

import java.util.function.Predicate;

/** Various utility functions for transitions. */
public class TransitionUtil {

@@ -54,4 +76,189 @@ public class TransitionUtil {
        return false;
    }

    /** Returns `true` if `change` is a wallpaper. */
    public static boolean isWallpaper(TransitionInfo.Change change) {
        return (change.getTaskInfo() == null)
                && change.hasFlags(FLAG_IS_WALLPAPER)
                && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
    }

    /** Returns `true` if `change` is not an app window or wallpaper. */
    public static boolean isNonApp(TransitionInfo.Change change) {
        return (change.getTaskInfo() == null)
                && !change.hasFlags(FLAG_IS_WALLPAPER)
                && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
    }

    /**
     * Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
     * MUST call `test` in the same order that the changes appear in the TransitionInfo.
     */
    public static class LeafTaskFilter implements Predicate<TransitionInfo.Change> {
        private final SparseBooleanArray mChildTaskTargets = new SparseBooleanArray();

        @Override
        public boolean test(TransitionInfo.Change change) {
            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            // Children always come before parent since changes are in top-to-bottom z-order.
            if ((taskInfo == null) || mChildTaskTargets.get(taskInfo.taskId)) {
                // has children, so not a leaf. Skip.
                return false;
            }
            if (taskInfo.hasParentTask()) {
                mChildTaskTargets.put(taskInfo.parentTaskId, true);
            }
            return true;
        }
    }


    private static int newModeToLegacyMode(int newMode) {
        switch (newMode) {
            case WindowManager.TRANSIT_OPEN:
            case WindowManager.TRANSIT_TO_FRONT:
                return MODE_OPENING;
            case WindowManager.TRANSIT_CLOSE:
            case WindowManager.TRANSIT_TO_BACK:
                return MODE_CLOSING;
            default:
                return MODE_CHANGING;
        }
    }

    /**
     * Very similar to Transitions#setupAnimHierarchy but specialized for leashes.
     */
    @SuppressLint("NewApi")
    private static void setupLeash(@NonNull SurfaceControl leash,
            @NonNull TransitionInfo.Change change, int layer,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
        final boolean isOpening = TransitionUtil.isOpeningType(info.getType());
        // Put animating stuff above this line and put static stuff below it.
        int zSplitLine = info.getChanges().size();
        // changes should be ordered top-to-bottom in z
        final int mode = change.getMode();

        t.reparent(leash, info.getRootLeash());
        final Rect absBounds =
                (mode == TRANSIT_OPEN) ? change.getEndAbsBounds() : change.getStartAbsBounds();
        t.setPosition(leash, absBounds.left - info.getRootOffset().x,
                absBounds.top - info.getRootOffset().y);

        // Put all the OPEN/SHOW on top
        if (TransitionUtil.isOpeningType(mode)) {
            if (isOpening) {
                t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
                if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
                    // if transferred, it should be left visible.
                    t.setAlpha(leash, 0.f);
                }
            } else {
                // put on bottom and leave it visible
                t.setLayer(leash, zSplitLine - layer);
            }
        } else if (TransitionUtil.isClosingType(mode)) {
            if (isOpening) {
                // put on bottom and leave visible
                t.setLayer(leash, zSplitLine - layer);
            } else {
                // put on top
                t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
            }
        } else { // CHANGE
            t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
        }
    }

    @SuppressLint("NewApi")
    private static SurfaceControl createLeash(TransitionInfo info, TransitionInfo.Change change,
            int order, SurfaceControl.Transaction t) {
        // TODO: once we can properly sync transactions across process, then get rid of this leash.
        if (change.getParent() != null && (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
            // Special case for wallpaper atm. Normally these are left alone; but, a quirk of
            // making leashes means we have to handle them specially.
            return change.getLeash();
        }
        SurfaceControl leashSurface = new SurfaceControl.Builder()
                .setName(change.getLeash().toString() + "_transition-leash")
                .setContainerLayer()
                // Initial the surface visible to respect the visibility of the original surface.
                .setHidden(false)
                .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);
        t.reparent(change.getLeash(), leashSurface);
        t.setAlpha(change.getLeash(), 1.0f);
        t.show(change.getLeash());
        t.setPosition(change.getLeash(), 0, 0);
        t.setLayer(change.getLeash(), 0);
        return leashSurface;
    }

    /**
     * Creates a new RemoteAnimationTarget from the provided change info
     */
    public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
            TransitionInfo info, SurfaceControl.Transaction t,
            @Nullable ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
        final SurfaceControl leash = createLeash(info, change, order, t);
        if (leashMap != null) {
            leashMap.put(change.getLeash(), leash);
        }
        return newTarget(change, order, leash);
    }

    /**
     * Creates a new RemoteAnimationTarget from the provided change and leash
     */
    public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
            SurfaceControl leash) {
        int taskId;
        boolean isNotInRecents;
        ActivityManager.RunningTaskInfo taskInfo;
        WindowConfiguration windowConfiguration;

        taskInfo = change.getTaskInfo();
        if (taskInfo != null) {
            taskId = taskInfo.taskId;
            isNotInRecents = !taskInfo.isRunning;
            windowConfiguration = taskInfo.configuration.windowConfiguration;
        } else {
            taskId = INVALID_TASK_ID;
            isNotInRecents = true;
            windowConfiguration = new WindowConfiguration();
        }

        Rect localBounds = new Rect(change.getEndAbsBounds());
        localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);

        RemoteAnimationTarget target = new RemoteAnimationTarget(
                taskId,
                newModeToLegacyMode(change.getMode()),
                // TODO: once we can properly sync transactions across process,
                // then get rid of this leash.
                leash,
                (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0,
                null,
                // TODO(shell-transitions): we need to send content insets? evaluate how its used.
                new Rect(0, 0, 0, 0),
                order,
                null,
                localBounds,
                new Rect(change.getEndAbsBounds()),
                windowConfiguration,
                isNotInRecents,
                null,
                new Rect(change.getStartAbsBounds()),
                taskInfo,
                change.getAllowEnterPip(),
                (change.getFlags() & FLAG_IS_DIVIDER_BAR) != 0
                        ? TYPE_DOCK_DIVIDER : INVALID_WINDOW_TYPE
        );
        target.setWillShowImeOnTarget(
                (change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0);
        target.setRotationChange(change.getEndRotation() - change.getStartRotation());
        return target;
    }
}
+5 −211
Original line number Diff line number Diff line
@@ -16,30 +16,9 @@

package com.android.systemui.shared.system;

import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.view.RemoteAnimationTarget.MODE_CHANGING;
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;

import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.graphics.Rect;
import android.util.ArrayMap;
import android.util.SparseBooleanArray;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionInfo.Change;

@@ -53,156 +32,6 @@ import java.util.function.Predicate;
 */
public class RemoteAnimationTargetCompat {

    private static int newModeToLegacyMode(int newMode) {
        switch (newMode) {
            case WindowManager.TRANSIT_OPEN:
            case WindowManager.TRANSIT_TO_FRONT:
                return MODE_OPENING;
            case WindowManager.TRANSIT_CLOSE:
            case WindowManager.TRANSIT_TO_BACK:
                return MODE_CLOSING;
            default:
                return MODE_CHANGING;
        }
    }

    /**
     * Almost a copy of Transitions#setupStartState.
     * TODO: remove when there is proper cross-process transaction sync.
     */
    @SuppressLint("NewApi")
    private static void setupLeash(@NonNull SurfaceControl leash,
            @NonNull TransitionInfo.Change change, int layer,
            @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
        final boolean isOpening = TransitionUtil.isOpeningType(info.getType());
        // Put animating stuff above this line and put static stuff below it.
        int zSplitLine = info.getChanges().size();
        // changes should be ordered top-to-bottom in z
        final int mode = change.getMode();

        t.reparent(leash, info.getRootLeash());
        final Rect absBounds =
                (mode == TRANSIT_OPEN) ? change.getEndAbsBounds() : change.getStartAbsBounds();
        t.setPosition(leash, absBounds.left - info.getRootOffset().x,
                absBounds.top - info.getRootOffset().y);

        // Put all the OPEN/SHOW on top
        if (TransitionUtil.isOpeningType(mode)) {
            if (isOpening) {
                t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
                if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
                    // if transferred, it should be left visible.
                    t.setAlpha(leash, 0.f);
                }
            } else {
                // put on bottom and leave it visible
                t.setLayer(leash, zSplitLine - layer);
            }
        } else if (TransitionUtil.isClosingType(mode)) {
            if (isOpening) {
                // put on bottom and leave visible
                t.setLayer(leash, zSplitLine - layer);
            } else {
                // put on top
                t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
            }
        } else { // CHANGE
            t.setLayer(leash, zSplitLine + info.getChanges().size() - layer);
        }
    }

    @SuppressLint("NewApi")
    private static SurfaceControl createLeash(TransitionInfo info, TransitionInfo.Change change,
            int order, SurfaceControl.Transaction t) {
        // TODO: once we can properly sync transactions across process, then get rid of this leash.
        if (change.getParent() != null && (change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
            // Special case for wallpaper atm. Normally these are left alone; but, a quirk of
            // making leashes means we have to handle them specially.
            return change.getLeash();
        }
        SurfaceControl leashSurface = new SurfaceControl.Builder()
                .setName(change.getLeash().toString() + "_transition-leash")
                .setContainerLayer()
                // Initial the surface visible to respect the visibility of the original surface.
                .setHidden(false)
                .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);
        t.reparent(change.getLeash(), leashSurface);
        t.setAlpha(change.getLeash(), 1.0f);
        t.show(change.getLeash());
        t.setPosition(change.getLeash(), 0, 0);
        t.setLayer(change.getLeash(), 0);
        return leashSurface;
    }

    /**
     * Creates a new RemoteAnimationTarget from the provided change info
     */
    public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
            TransitionInfo info, SurfaceControl.Transaction t,
            @Nullable ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
        final SurfaceControl leash = createLeash(info, change, order, t);
        if (leashMap != null) {
            leashMap.put(change.getLeash(), leash);
        }
        return newTarget(change, order, leash);
    }

    /**
     * Creates a new RemoteAnimationTarget from the provided change and leash
     */
    public static RemoteAnimationTarget newTarget(TransitionInfo.Change change, int order,
            SurfaceControl leash) {
        int taskId;
        boolean isNotInRecents;
        ActivityManager.RunningTaskInfo taskInfo;
        WindowConfiguration windowConfiguration;

        taskInfo = change.getTaskInfo();
        if (taskInfo != null) {
            taskId = taskInfo.taskId;
            isNotInRecents = !taskInfo.isRunning;
            windowConfiguration = taskInfo.configuration.windowConfiguration;
        } else {
            taskId = INVALID_TASK_ID;
            isNotInRecents = true;
            windowConfiguration = new WindowConfiguration();
        }

        Rect localBounds = new Rect(change.getEndAbsBounds());
        localBounds.offsetTo(change.getEndRelOffset().x, change.getEndRelOffset().y);

        RemoteAnimationTarget target = new RemoteAnimationTarget(
                taskId,
                newModeToLegacyMode(change.getMode()),
                // TODO: once we can properly sync transactions across process,
                // then get rid of this leash.
                leash,
                (change.getFlags() & TransitionInfo.FLAG_TRANSLUCENT) != 0,
                null,
                // TODO(shell-transitions): we need to send content insets? evaluate how its used.
                new Rect(0, 0, 0, 0),
                order,
                null,
                localBounds,
                new Rect(change.getEndAbsBounds()),
                windowConfiguration,
                isNotInRecents,
                null,
                new Rect(change.getStartAbsBounds()),
                taskInfo,
                change.getAllowEnterPip(),
                (change.getFlags() & FLAG_IS_DIVIDER_BAR) != 0
                        ? TYPE_DOCK_DIVIDER : INVALID_WINDOW_TYPE
        );
        target.setWillShowImeOnTarget(
                (change.getFlags() & TransitionInfo.FLAG_WILL_IME_SHOWN) != 0);
        target.setRotationChange(change.getEndRotation() - change.getStartRotation());
        return target;
    }

    /**
     * Represents a TransitionInfo object as an array of old-style app targets
     *
@@ -211,7 +40,7 @@ public class RemoteAnimationTargetCompat {
     */
    public static RemoteAnimationTarget[] wrapApps(TransitionInfo info,
            SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
        return wrap(info, t, leashMap, new LeafTaskFilter());
        return wrap(info, t, leashMap, new TransitionUtil.LeafTaskFilter());
    }

    /**
@@ -224,8 +53,8 @@ public class RemoteAnimationTargetCompat {
     */
    public static RemoteAnimationTarget[] wrapNonApps(TransitionInfo info, boolean wallpapers,
            SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) {
        return wrap(info, t, leashMap, (change) ->
                (wallpapers ? isWallpaper(change) : isNonApp(change)));
        return wrap(info, t, leashMap, (change) -> (wallpapers
                ? TransitionUtil.isWallpaper(change) : TransitionUtil.isNonApp(change)));
    }

    private static RemoteAnimationTarget[] wrap(TransitionInfo info,
@@ -235,45 +64,10 @@ public class RemoteAnimationTargetCompat {
        for (int i = 0; i < info.getChanges().size(); i++) {
            TransitionInfo.Change change = info.getChanges().get(i);
            if (filter.test(change)) {
                out.add(newTarget(change, info.getChanges().size() - i, info, t, leashMap));
                out.add(TransitionUtil.newTarget(
                        change, info.getChanges().size() - i, info, t, leashMap));
            }
        }
        return out.toArray(new RemoteAnimationTarget[out.size()]);
    }

    /** Returns `true` if `change` is a wallpaper. */
    public static boolean isWallpaper(Change change) {
        return (change.getTaskInfo() == null)
                && change.hasFlags(FLAG_IS_WALLPAPER)
                && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
    }

    /** Returns `true` if `change` is not an app window or wallpaper. */
    public static boolean isNonApp(Change change) {
        return (change.getTaskInfo() == null)
                && !change.hasFlags(FLAG_IS_WALLPAPER)
                && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
    }

    /**
     * Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
     * MUST call `test` in the same order that the changes appear in the TransitionInfo.
     */
    public static class LeafTaskFilter implements Predicate<Change> {
        private final SparseBooleanArray mChildTaskTargets = new SparseBooleanArray();

        @Override
        public boolean test(Change change) {
            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            // Children always come before parent since changes are in top-to-bottom z-order.
            if ((taskInfo == null) || mChildTaskTargets.get(taskInfo.taskId)) {
                // has children, so not a leaf. Skip.
                return false;
            }
            if (taskInfo.hasParentTask()) {
                mChildTaskTargets.put(taskInfo.parentTaskId, true);
            }
            return true;
        }
    }
}
+9 −11
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED;

import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.newTarget;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -160,16 +158,15 @@ public class RemoteTransitionCompat {

            final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>();
            final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>();
            RemoteAnimationTargetCompat.LeafTaskFilter leafTaskFilter =
                    new RemoteAnimationTargetCompat.LeafTaskFilter();
            TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter();
            // About layering: we divide up the "layer space" into 3 regions (each the size of
            // the change count). This lets us categorize things into above/below/between
            // while maintaining their relative ordering.
            for (int i = 0; i < info.getChanges().size(); ++i) {
                final TransitionInfo.Change change = info.getChanges().get(i);
                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
                if (RemoteAnimationTargetCompat.isWallpaper(change)) {
                    final RemoteAnimationTarget target = newTarget(change,
                if (TransitionUtil.isWallpaper(change)) {
                    final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
                            // wallpapers go into the "below" layer space
                            info.getChanges().size() - i, info, t, mLeashMap);
                    wallpapers.add(target);
@@ -177,7 +174,7 @@ public class RemoteTransitionCompat {
                    t.setAlpha(target.leash, 1);
                } else if (leafTaskFilter.test(change)) {
                    // start by putting everything into the "below" layer space.
                    final RemoteAnimationTarget target = newTarget(change,
                    final RemoteAnimationTarget target = TransitionUtil.newTarget(change,
                            info.getChanges().size() - i, info, t, mLeashMap);
                    apps.add(target);
                    if (TransitionUtil.isClosingType(change.getMode())) {
@@ -217,8 +214,8 @@ public class RemoteTransitionCompat {
            TransitionInfo.Change recentsOpening = null;
            boolean foundRecentsClosing = false;
            boolean hasChangingApp = false;
            final RemoteAnimationTargetCompat.LeafTaskFilter leafTaskFilter =
                    new RemoteAnimationTargetCompat.LeafTaskFilter();
            final TransitionUtil.LeafTaskFilter leafTaskFilter =
                    new TransitionUtil.LeafTaskFilter();
            for (int i = 0; i < info.getChanges().size(); ++i) {
                final TransitionInfo.Change change = info.getChanges().get(i);
                final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
@@ -308,7 +305,8 @@ public class RemoteTransitionCompat {
                    int pausingIdx = TaskState.indexOf(mPausingTasks, change);
                    if (pausingIdx >= 0) {
                        // Something is showing/opening a previously-pausing app.
                        targets[i] = newTarget(change, layer, mPausingTasks.get(pausingIdx).mLeash);
                        targets[i] = TransitionUtil.newTarget(change, layer,
                                mPausingTasks.get(pausingIdx).mLeash);
                        mOpeningTasks.add(mPausingTasks.remove(pausingIdx));
                        // Setup hides opening tasks initially, so make it visible again (since we
                        // are already showing it).
@@ -316,7 +314,7 @@ public class RemoteTransitionCompat {
                        t.setAlpha(change.getLeash(), 1.f);
                    } else {
                        // We are receiving new opening tasks, so convert to onTasksAppeared.
                        targets[i] = newTarget(change, layer, info, t, mLeashMap);
                        targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap);
                        t.reparent(targets[i].leash, mInfo.getRootLeash());
                        t.setLayer(targets[i].leash, layer);
                        mOpeningTasks.add(new TaskState(change, targets[i].leash));
+2 −1

File changed.

Preview size limit exceeded, changes collapsed.