1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.shared.system; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 20 import static android.view.WindowManager.TRANSIT_CLOSE; 21 import static android.view.WindowManager.TRANSIT_OLD_NONE; 22 import static android.view.WindowManager.TRANSIT_OPEN; 23 import static android.view.WindowManager.TRANSIT_TO_BACK; 24 import static android.view.WindowManager.TRANSIT_TO_FRONT; 25 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; 26 27 import android.os.IBinder; 28 import android.os.RemoteException; 29 import android.util.ArrayMap; 30 import android.util.Log; 31 import android.view.IRemoteAnimationFinishedCallback; 32 import android.view.IRemoteAnimationRunner; 33 import android.view.RemoteAnimationTarget; 34 import android.view.SurfaceControl; 35 import android.view.WindowManager; 36 import android.view.WindowManager.TransitionOldType; 37 import android.window.IRemoteTransition; 38 import android.window.IRemoteTransitionFinishedCallback; 39 import android.window.TransitionInfo; 40 41 import com.android.wm.shell.util.CounterRotator; 42 43 public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner.Stub { 44 private static final String TAG = "RemoteAnimRunnerCompat"; 45 onAnimationStart(@indowManager.TransitionOldType int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, Runnable finishedCallback)46 public abstract void onAnimationStart(@WindowManager.TransitionOldType int transit, 47 RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, 48 RemoteAnimationTarget[] nonApps, Runnable finishedCallback); 49 50 @Override onAnimationStart(@ransitionOldType int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, final IRemoteAnimationFinishedCallback finishedCallback)51 public final void onAnimationStart(@TransitionOldType int transit, 52 RemoteAnimationTarget[] apps, 53 RemoteAnimationTarget[] wallpapers, 54 RemoteAnimationTarget[] nonApps, 55 final IRemoteAnimationFinishedCallback finishedCallback) { 56 57 onAnimationStart(transit, apps, wallpapers, 58 nonApps, () -> { 59 try { 60 finishedCallback.onAnimationFinished(); 61 } catch (RemoteException e) { 62 Log.e(TAG, "Failed to call app controlled animation finished callback", e); 63 } 64 }); 65 } 66 toRemoteTransition()67 public IRemoteTransition toRemoteTransition() { 68 return wrap(this); 69 } 70 71 /** Wraps a remote animation runner in a remote-transition. */ wrap(IRemoteAnimationRunner runner)72 public static IRemoteTransition.Stub wrap(IRemoteAnimationRunner runner) { 73 return new IRemoteTransition.Stub() { 74 final ArrayMap<IBinder, Runnable> mFinishRunnables = new ArrayMap<>(); 75 76 @Override 77 public void startAnimation(IBinder token, TransitionInfo info, 78 SurfaceControl.Transaction t, 79 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 80 final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>(); 81 final RemoteAnimationTarget[] apps = 82 RemoteAnimationTargetCompat.wrapApps(info, t, leashMap); 83 final RemoteAnimationTarget[] wallpapers = 84 RemoteAnimationTargetCompat.wrapNonApps( 85 info, true /* wallpapers */, t, leashMap); 86 final RemoteAnimationTarget[] nonApps = 87 RemoteAnimationTargetCompat.wrapNonApps( 88 info, false /* wallpapers */, t, leashMap); 89 90 // TODO(b/177438007): Move this set-up logic into launcher's animation impl. 91 boolean isReturnToHome = false; 92 TransitionInfo.Change launcherTask = null; 93 TransitionInfo.Change wallpaper = null; 94 int launcherLayer = 0; 95 int rotateDelta = 0; 96 float displayW = 0; 97 float displayH = 0; 98 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 99 final TransitionInfo.Change change = info.getChanges().get(i); 100 // skip changes that we didn't wrap 101 if (!leashMap.containsKey(change.getLeash())) continue; 102 if (change.getTaskInfo() != null 103 && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { 104 isReturnToHome = change.getMode() == TRANSIT_OPEN 105 || change.getMode() == TRANSIT_TO_FRONT; 106 launcherTask = change; 107 launcherLayer = info.getChanges().size() - i; 108 } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { 109 wallpaper = change; 110 } 111 if (change.getParent() == null && change.getEndRotation() >= 0 112 && change.getEndRotation() != change.getStartRotation()) { 113 rotateDelta = change.getEndRotation() - change.getStartRotation(); 114 displayW = change.getEndAbsBounds().width(); 115 displayH = change.getEndAbsBounds().height(); 116 } 117 } 118 119 // Prepare for rotation if there is one 120 final CounterRotator counterLauncher = new CounterRotator(); 121 final CounterRotator counterWallpaper = new CounterRotator(); 122 if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) { 123 final TransitionInfo.Change parent = info.getChange(launcherTask.getParent()); 124 if (parent != null) { 125 counterLauncher.setup(t, parent.getLeash(), rotateDelta, displayW, 126 displayH); 127 } else { 128 Log.e(TAG, "Malformed: " + launcherTask + " has parent=" 129 + launcherTask.getParent() + " but it's not in info."); 130 } 131 if (counterLauncher.getSurface() != null) { 132 t.setLayer(counterLauncher.getSurface(), launcherLayer); 133 } 134 } 135 136 if (isReturnToHome) { 137 if (counterLauncher.getSurface() != null) { 138 t.setLayer(counterLauncher.getSurface(), info.getChanges().size() * 3); 139 } 140 // Need to "boost" the closing things since that's what launcher expects. 141 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 142 final TransitionInfo.Change change = info.getChanges().get(i); 143 final SurfaceControl leash = leashMap.get(change.getLeash()); 144 // skip changes that we didn't wrap 145 if (leash == null) continue; 146 final int mode = info.getChanges().get(i).getMode(); 147 // Only deal with independent layers 148 if (!TransitionInfo.isIndependent(change, info)) continue; 149 if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { 150 t.setLayer(leash, info.getChanges().size() * 3 - i); 151 counterLauncher.addChild(t, leash); 152 } 153 } 154 // Make wallpaper visible immediately since launcher apparently won't do this. 155 for (int i = wallpapers.length - 1; i >= 0; --i) { 156 t.show(wallpapers[i].leash); 157 t.setAlpha(wallpapers[i].leash, 1.f); 158 } 159 } else { 160 if (launcherTask != null) { 161 counterLauncher.addChild(t, leashMap.get(launcherTask.getLeash())); 162 } 163 if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) { 164 final TransitionInfo.Change parent = info.getChange(wallpaper.getParent()); 165 if (parent != null) { 166 counterWallpaper.setup(t, parent.getLeash(), rotateDelta, displayW, 167 displayH); 168 } else { 169 Log.e(TAG, "Malformed: " + wallpaper + " has parent=" 170 + wallpaper.getParent() + " but it's not in info."); 171 } 172 if (counterWallpaper.getSurface() != null) { 173 t.setLayer(counterWallpaper.getSurface(), -1); 174 counterWallpaper.addChild(t, leashMap.get(wallpaper.getLeash())); 175 } 176 } 177 } 178 t.apply(); 179 180 final Runnable animationFinishedCallback = () -> { 181 final SurfaceControl.Transaction finishTransaction = 182 new SurfaceControl.Transaction(); 183 counterLauncher.cleanUp(finishTransaction); 184 counterWallpaper.cleanUp(finishTransaction); 185 // Release surface references now. This is apparently to free GPU memory 186 // before GC would. 187 info.releaseAllSurfaces(); 188 // Don't release here since launcher might still be using them. Instead 189 // let launcher release them (eg. via RemoteAnimationTargets) 190 leashMap.clear(); 191 try { 192 finishCallback.onTransitionFinished(null /* wct */, finishTransaction); 193 finishTransaction.close(); 194 } catch (RemoteException e) { 195 Log.e(TAG, "Failed to call app controlled animation finished callback", e); 196 } 197 }; 198 synchronized (mFinishRunnables) { 199 mFinishRunnables.put(token, animationFinishedCallback); 200 } 201 // TODO(bc-unlcok): Pass correct transit type. 202 runner.onAnimationStart(TRANSIT_OLD_NONE, 203 apps, wallpapers, nonApps, new IRemoteAnimationFinishedCallback() { 204 @Override 205 public void onAnimationFinished() { 206 synchronized (mFinishRunnables) { 207 if (mFinishRunnables.remove(token) == null) return; 208 } 209 animationFinishedCallback.run(); 210 } 211 212 @Override 213 public IBinder asBinder() { 214 return null; 215 } 216 }); 217 } 218 219 @Override 220 public void mergeAnimation(IBinder token, TransitionInfo info, 221 SurfaceControl.Transaction t, IBinder mergeTarget, 222 IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { 223 // TODO: hook up merge to recents onTaskAppeared if applicable. Until then, adapt 224 // to legacy cancel. 225 final Runnable finishRunnable; 226 synchronized (mFinishRunnables) { 227 finishRunnable = mFinishRunnables.remove(mergeTarget); 228 } 229 // Since we're not actually animating, release native memory now 230 t.close(); 231 info.releaseAllSurfaces(); 232 if (finishRunnable == null) return; 233 runner.onAnimationCancelled(); 234 finishRunnable.run(); 235 } 236 }; 237 } 238 } 239