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

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

Merge "Add one-shot remote handler and remote runner interface" into sc-dev

parents 2f7bef0b eae6c92d
Loading
Loading
Loading
Loading
+107 −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.wm.shell.transition;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;

import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;

/**
 * Handler that forwards to a RemoteTransition. It is designed for one-shot use to attach a
 * specific remote animation to a specific transition.
 */
public class OneShotRemoteHandler implements Transitions.TransitionHandler {
    private final ShellExecutor mMainExecutor;

    /** The specific transition that this handler is associated with. Just for validation. */
    private IBinder mTransition = null;

    /** The remote to delegate animation to */
    private final IRemoteTransition mRemote;

    public OneShotRemoteHandler(@NonNull ShellExecutor mainExecutor,
            @NonNull IRemoteTransition remote) {
        mMainExecutor = mainExecutor;
        mRemote = remote;
    }

    public void setTransition(@NonNull IBinder transition) {
        mTransition = transition;
    }

    @Override
    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t,
            @NonNull Transitions.TransitionFinishCallback finishCallback) {
        if (mTransition != transition) return false;
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
                + " transition %s for %s.", mRemote, transition);

        final IBinder.DeathRecipient remoteDied = () -> {
            Log.e(Transitions.TAG, "Remote transition died, finishing");
            mMainExecutor.execute(
                    () -> finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
        };
        IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
            @Override
            public void onTransitionFinished(WindowContainerTransaction wct) {
                if (mRemote.asBinder() != null) {
                    mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
                }
                mMainExecutor.execute(
                        () -> finishCallback.onTransitionFinished(wct, null /* wctCB */));
            }
        };
        try {
            if (mRemote.asBinder() != null) {
                mRemote.asBinder().linkToDeath(remoteDied, 0 /* flags */);
            }
            mRemote.startAnimation(info, t, cb);
        } catch (RemoteException e) {
            if (mRemote.asBinder() != null) {
                mRemote.asBinder().unlinkToDeath(remoteDied, 0 /* flags */);
            }
            Log.e(Transitions.TAG, "Error running remote transition.", e);
            finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
        }
        return true;
    }

    @Override
    @Nullable
    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
            @Nullable TransitionRequestInfo request) {
        IRemoteTransition remote = request.getRemoteTransition();
        if (remote != mRemote) return null;
        mTransition = transition;
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "RemoteTransition directly requested"
                + " for %s: %s", transition, remote);
        return new WindowContainerTransaction();
    }
}
+41 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
@@ -30,6 +31,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;

import static org.junit.Assert.assertEquals;
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;
@@ -295,6 +298,44 @@ public class ShellTransitionTests {
        verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any(), any());
    }

    @Test
    public void testOneShotRemoteHandler() {
        Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext,
                mMainExecutor, mAnimExecutor);
        transitions.replaceDefaultHandlerForTest(mDefaultHandler);

        final boolean[] remoteCalled = new boolean[]{false};
        final WindowContainerTransaction remoteFinishWCT = new WindowContainerTransaction();
        IRemoteTransition testRemote = new IRemoteTransition.Stub() {
            @Override
            public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
                    IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
                remoteCalled[0] = true;
                finishCallback.onTransitionFinished(remoteFinishWCT);
            }
        };

        final int transitType = TRANSIT_FIRST_CUSTOM + 1;

        OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor, testRemote);
        // Verify that it responds to the remote but not other things.
        IBinder transitToken = new Binder();
        assertNotNull(oneShot.handleRequest(transitToken,
                new TransitionRequestInfo(transitType, null, testRemote)));
        assertNull(oneShot.handleRequest(transitToken,
                new TransitionRequestInfo(transitType, null, null)));

        Transitions.TransitionFinishCallback testFinish =
                mock(Transitions.TransitionFinishCallback.class);
        // Verify that it responds to animation properly
        oneShot.setTransition(transitToken);
        IBinder anotherToken = new Binder();
        assertFalse(oneShot.startAnimation(anotherToken, new TransitionInfo(transitType, 0),
                mock(SurfaceControl.Transaction.class), testFinish));
        assertTrue(oneShot.startAnimation(transitToken, new TransitionInfo(transitType, 0),
                mock(SurfaceControl.Transaction.class), testFinish));
    }

    class TransitionInfoBuilder {
        final TransitionInfo mInfo;

+19 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
 */
@DataClass
public class RemoteTransitionCompat implements Parcelable {
    private static final String TAG = "RemoteTransitionCompat";

    @NonNull final IRemoteTransition mTransition;
    @Nullable TransitionFilter mFilter = null;

@@ -57,6 +59,23 @@ public class RemoteTransitionCompat implements Parcelable {
        mTransition = transition;
    }

    public RemoteTransitionCompat(RemoteTransitionRunner runner) {
        mTransition = new IRemoteTransition.Stub() {
            @Override
            public void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
                    IRemoteTransitionFinishedCallback finishedCallback) {
                final Runnable finishAdapter = () ->  {
                    try {
                        finishedCallback.onTransitionFinished(null /* wct */);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to call transition finished callback", e);
                    }
                };
                runner.startAnimation(info, t, finishAdapter);
            }
        };
    }

    /** Constructor specifically for recents animation */
    public RemoteTransitionCompat(RecentsAnimationListener recents,
            RecentsAnimationControllerCompat controller) {
+30 −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.systemui.shared.system;

import android.view.SurfaceControl;
import android.window.TransitionInfo;

/** Interface for something that runs a remote transition animation. */
public interface RemoteTransitionRunner {
    /**
     * Starts a transition animation. Once complete, the implementation should call
     * `finishCallback`.
     */
    void startAnimation(TransitionInfo info, SurfaceControl.Transaction t,
            Runnable finishCallback);
}