blob: ee55bf0aa8b7db840faf9446d14204ee95b790a4 [file] [log] [blame]
/*
* Copyright (C) 2018 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 static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_NONE;
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.view.WindowManager.TransitionOldType;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import android.annotation.SuppressLint;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Log;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.IRemoteTransition;
import android.window.IRemoteTransitionFinishedCallback;
import android.window.TransitionInfo;
import java.util.ArrayList;
/**
* @see RemoteAnimationAdapter
*/
public class RemoteAnimationAdapterCompat {
private final RemoteAnimationAdapter mWrapped;
private final RemoteTransitionCompat mRemoteTransition;
public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration,
long statusBarTransitionDelay) {
mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration,
statusBarTransitionDelay);
mRemoteTransition = buildRemoteTransition(runner);
}
RemoteAnimationAdapter getWrapped() {
return mWrapped;
}
/** Helper to just build a remote transition. Use this if the legacy adapter isn't needed. */
public static RemoteTransitionCompat buildRemoteTransition(RemoteAnimationRunnerCompat runner) {
return new RemoteTransitionCompat(wrapRemoteTransition(runner));
}
public RemoteTransitionCompat getRemoteTransition() {
return mRemoteTransition;
}
private static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteAnimationRunner.Stub() {
@Override
public void onAnimationStart(@TransitionOldType int transit,
RemoteAnimationTarget[] apps,
RemoteAnimationTarget[] wallpapers,
RemoteAnimationTarget[] nonApps,
final IRemoteAnimationFinishedCallback finishedCallback) {
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(apps);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(wallpapers);
final RemoteAnimationTargetCompat[] nonAppsCompat =
RemoteAnimationTargetCompat.wrap(nonApps);
final Runnable animationFinishedCallback = new Runnable() {
@Override
public void run() {
try {
finishedCallback.onAnimationFinished();
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
}
}
};
remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat,
nonAppsCompat, animationFinishedCallback);
}
@Override
public void onAnimationCancelled() {
remoteAnimationAdapter.onAnimationCancelled();
}
};
}
private static class CounterRotator {
SurfaceControl mSurface = null;
ArrayList<SurfaceControl> mRotateChildren = null;
void setup(SurfaceControl.Transaction t, SurfaceControl parent, int rotateDelta,
float displayW, float displayH) {
if (rotateDelta == 0) return;
mRotateChildren = new ArrayList<>();
// We want to counter-rotate, so subtract from 4
rotateDelta = 4 - (rotateDelta + 4) % 4;
mSurface = new SurfaceControl.Builder()
.setName("Transition Unrotate")
.setContainerLayer()
.setParent(parent)
.build();
// column-major
if (rotateDelta == 1) {
t.setMatrix(mSurface, 0, 1, -1, 0);
t.setPosition(mSurface, displayW, 0);
} else if (rotateDelta == 2) {
t.setMatrix(mSurface, -1, 0, 0, -1);
t.setPosition(mSurface, displayW, displayH);
} else if (rotateDelta == 3) {
t.setMatrix(mSurface, 0, -1, 1, 0);
t.setPosition(mSurface, 0, displayH);
}
t.show(mSurface);
}
void addChild(SurfaceControl.Transaction t, SurfaceControl child) {
if (mSurface == null) return;
t.reparent(child, mSurface);
mRotateChildren.add(child);
}
void cleanUp(SurfaceControl rootLeash) {
if (mSurface == null) return;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = mRotateChildren.size() - 1; i >= 0; --i) {
t.reparent(mRotateChildren.get(i), rootLeash);
}
t.remove(mSurface);
t.apply();
}
}
private static IRemoteTransition.Stub wrapRemoteTransition(
final RemoteAnimationRunnerCompat remoteAnimationAdapter) {
return new IRemoteTransition.Stub() {
@Override
public void startAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t,
IRemoteTransitionFinishedCallback finishCallback) {
final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>();
final RemoteAnimationTargetCompat[] appsCompat =
RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap);
final RemoteAnimationTargetCompat[] wallpapersCompat =
RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap);
// TODO(bc-unlock): Build wrapped object for non-apps target.
final RemoteAnimationTargetCompat[] nonAppsCompat =
new RemoteAnimationTargetCompat[0];
// TODO(b/177438007): Move this set-up logic into launcher's animation impl.
boolean isReturnToHome = false;
TransitionInfo.Change launcherTask = null;
TransitionInfo.Change wallpaper = null;
int launcherLayer = 0;
int rotateDelta = 0;
float displayW = 0;
float displayH = 0;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.getTaskInfo() != null
&& change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) {
isReturnToHome = change.getMode() == TRANSIT_OPEN
|| change.getMode() == TRANSIT_TO_FRONT;
launcherTask = change;
launcherLayer = info.getChanges().size() - i;
} else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) {
wallpaper = change;
}
if (change.getParent() == null && change.getEndRotation() >= 0
&& change.getEndRotation() != change.getStartRotation()) {
rotateDelta = change.getEndRotation() - change.getStartRotation();
displayW = change.getEndAbsBounds().width();
displayH = change.getEndAbsBounds().height();
}
}
// Prepare for rotation if there is one
final CounterRotator counterLauncher = new CounterRotator();
final CounterRotator counterWallpaper = new CounterRotator();
if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) {
counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(),
rotateDelta, displayW, displayH);
if (counterLauncher.mSurface != null) {
t.setLayer(counterLauncher.mSurface, launcherLayer);
}
}
if (isReturnToHome) {
if (counterLauncher.mSurface != null) {
t.setLayer(counterLauncher.mSurface, info.getChanges().size() * 3);
}
// Need to "boost" the closing things since that's what launcher expects.
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
final SurfaceControl leash = leashMap.get(change.getLeash());
final int mode = info.getChanges().get(i).getMode();
// Only deal with independent layers
if (!TransitionInfo.isIndependent(change, info)) continue;
if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
t.setLayer(leash, info.getChanges().size() * 3 - i);
counterLauncher.addChild(t, leash);
}
}
// Make wallpaper visible immediately since launcher apparently won't do this.
for (int i = wallpapersCompat.length - 1; i >= 0; --i) {
t.show(wallpapersCompat[i].leash.getSurfaceControl());
t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f);
}
} else {
if (launcherTask != null) {
counterLauncher.addChild(t, leashMap.get(launcherTask.getLeash()));
}
if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) {
counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(),
rotateDelta, displayW, displayH);
if (counterWallpaper.mSurface != null) {
t.setLayer(counterWallpaper.mSurface, -1);
counterWallpaper.addChild(t, leashMap.get(wallpaper.getLeash()));
}
}
}
t.apply();
final Runnable animationFinishedCallback = new Runnable() {
@Override
@SuppressLint("NewApi")
public void run() {
try {
counterLauncher.cleanUp(info.getRootLeash());
counterWallpaper.cleanUp(info.getRootLeash());
// Release surface references now. This is apparently to free GPU
// memory while doing quick operations (eg. during CTS).
for (int i = 0; i < info.getChanges().size(); ++i) {
info.getChanges().get(i).getLeash().release();
}
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (int i = 0; i < leashMap.size(); ++i) {
if (leashMap.keyAt(i) == leashMap.valueAt(i)) continue;
t.remove(leashMap.valueAt(i));
}
t.apply();
finishCallback.onTransitionFinished(null /* wct */);
} catch (RemoteException e) {
Log.e("ActivityOptionsCompat", "Failed to call app controlled animation"
+ " finished callback", e);
}
}
};
// TODO(bc-unlcok): Pass correct transit type.
remoteAnimationAdapter.onAnimationStart(
TRANSIT_OLD_NONE,
appsCompat, wallpapersCompat, nonAppsCompat,
animationFinishedCallback);
}
@Override
public void mergeAnimation(IBinder token, TransitionInfo info,
SurfaceControl.Transaction t, IBinder mergeTarget,
IRemoteTransitionFinishedCallback finishCallback) {
// TODO: hook up merge to recents onTaskAppeared if applicable. Until then, ignore
// any incoming merges.
}
};
}
}