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.view.WindowManager.TransitionOldType; 26 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; 27 28 import android.annotation.SuppressLint; 29 import android.app.IApplicationThread; 30 import android.os.IBinder; 31 import android.os.RemoteException; 32 import android.util.ArrayMap; 33 import android.util.Log; 34 import android.view.IRemoteAnimationFinishedCallback; 35 import android.view.IRemoteAnimationRunner; 36 import android.view.RemoteAnimationAdapter; 37 import android.view.RemoteAnimationTarget; 38 import android.view.SurfaceControl; 39 import android.window.IRemoteTransition; 40 import android.window.IRemoteTransitionFinishedCallback; 41 import android.window.RemoteTransition; 42 import android.window.TransitionInfo; 43 44 import com.android.wm.shell.util.CounterRotator; 45 46 /** 47 * @see RemoteAnimationAdapter 48 */ 49 public class RemoteAnimationAdapterCompat { 50 51 private final RemoteAnimationAdapter mWrapped; 52 private final RemoteTransitionCompat mRemoteTransition; 53 RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration, long statusBarTransitionDelay, IApplicationThread appThread)54 public RemoteAnimationAdapterCompat(RemoteAnimationRunnerCompat runner, long duration, 55 long statusBarTransitionDelay, IApplicationThread appThread) { 56 mWrapped = new RemoteAnimationAdapter(wrapRemoteAnimationRunner(runner), duration, 57 statusBarTransitionDelay); 58 mRemoteTransition = buildRemoteTransition(runner, appThread); 59 } 60 getWrapped()61 RemoteAnimationAdapter getWrapped() { 62 return mWrapped; 63 } 64 65 /** Helper to just build a remote transition. Use this if the legacy adapter isn't needed. */ buildRemoteTransition(RemoteAnimationRunnerCompat runner, IApplicationThread appThread)66 public static RemoteTransitionCompat buildRemoteTransition(RemoteAnimationRunnerCompat runner, 67 IApplicationThread appThread) { 68 return new RemoteTransitionCompat( 69 new RemoteTransition(wrapRemoteTransition(runner), appThread)); 70 } 71 getRemoteTransition()72 public RemoteTransitionCompat getRemoteTransition() { 73 return mRemoteTransition; 74 } 75 76 /** Wraps a RemoteAnimationRunnerCompat in an IRemoteAnimationRunner. */ wrapRemoteAnimationRunner( final RemoteAnimationRunnerCompat remoteAnimationAdapter)77 public static IRemoteAnimationRunner.Stub wrapRemoteAnimationRunner( 78 final RemoteAnimationRunnerCompat remoteAnimationAdapter) { 79 return new IRemoteAnimationRunner.Stub() { 80 @Override 81 public void onAnimationStart(@TransitionOldType int transit, 82 RemoteAnimationTarget[] apps, 83 RemoteAnimationTarget[] wallpapers, 84 RemoteAnimationTarget[] nonApps, 85 final IRemoteAnimationFinishedCallback finishedCallback) { 86 final RemoteAnimationTargetCompat[] appsCompat = 87 RemoteAnimationTargetCompat.wrap(apps); 88 final RemoteAnimationTargetCompat[] wallpapersCompat = 89 RemoteAnimationTargetCompat.wrap(wallpapers); 90 final RemoteAnimationTargetCompat[] nonAppsCompat = 91 RemoteAnimationTargetCompat.wrap(nonApps); 92 final Runnable animationFinishedCallback = new Runnable() { 93 @Override 94 public void run() { 95 try { 96 finishedCallback.onAnimationFinished(); 97 } catch (RemoteException e) { 98 Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" 99 + " finished callback", e); 100 } 101 } 102 }; 103 remoteAnimationAdapter.onAnimationStart(transit, appsCompat, wallpapersCompat, 104 nonAppsCompat, animationFinishedCallback); 105 } 106 107 @Override 108 public void onAnimationCancelled() { 109 remoteAnimationAdapter.onAnimationCancelled(); 110 } 111 }; 112 } 113 114 private static IRemoteTransition.Stub wrapRemoteTransition( 115 final RemoteAnimationRunnerCompat remoteAnimationAdapter) { 116 return new IRemoteTransition.Stub() { 117 @Override 118 public void startAnimation(IBinder token, TransitionInfo info, 119 SurfaceControl.Transaction t, 120 IRemoteTransitionFinishedCallback finishCallback) { 121 final ArrayMap<SurfaceControl, SurfaceControl> leashMap = new ArrayMap<>(); 122 final RemoteAnimationTargetCompat[] appsCompat = 123 RemoteAnimationTargetCompat.wrap(info, false /* wallpapers */, t, leashMap); 124 final RemoteAnimationTargetCompat[] wallpapersCompat = 125 RemoteAnimationTargetCompat.wrap(info, true /* wallpapers */, t, leashMap); 126 // TODO(bc-unlock): Build wrapped object for non-apps target. 127 final RemoteAnimationTargetCompat[] nonAppsCompat = 128 new RemoteAnimationTargetCompat[0]; 129 130 // TODO(b/177438007): Move this set-up logic into launcher's animation impl. 131 boolean isReturnToHome = false; 132 TransitionInfo.Change launcherTask = null; 133 TransitionInfo.Change wallpaper = null; 134 int launcherLayer = 0; 135 int rotateDelta = 0; 136 float displayW = 0; 137 float displayH = 0; 138 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 139 final TransitionInfo.Change change = info.getChanges().get(i); 140 if (change.getTaskInfo() != null 141 && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { 142 isReturnToHome = change.getMode() == TRANSIT_OPEN 143 || change.getMode() == TRANSIT_TO_FRONT; 144 launcherTask = change; 145 launcherLayer = info.getChanges().size() - i; 146 } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { 147 wallpaper = change; 148 } 149 if (change.getParent() == null && change.getEndRotation() >= 0 150 && change.getEndRotation() != change.getStartRotation()) { 151 rotateDelta = change.getEndRotation() - change.getStartRotation(); 152 displayW = change.getEndAbsBounds().width(); 153 displayH = change.getEndAbsBounds().height(); 154 } 155 } 156 157 // Prepare for rotation if there is one 158 final CounterRotator counterLauncher = new CounterRotator(); 159 final CounterRotator counterWallpaper = new CounterRotator(); 160 if (launcherTask != null && rotateDelta != 0 && launcherTask.getParent() != null) { 161 counterLauncher.setup(t, info.getChange(launcherTask.getParent()).getLeash(), 162 rotateDelta, displayW, displayH); 163 if (counterLauncher.getSurface() != null) { 164 t.setLayer(counterLauncher.getSurface(), launcherLayer); 165 } 166 } 167 168 if (isReturnToHome) { 169 if (counterLauncher.getSurface() != null) { 170 t.setLayer(counterLauncher.getSurface(), info.getChanges().size() * 3); 171 } 172 // Need to "boost" the closing things since that's what launcher expects. 173 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 174 final TransitionInfo.Change change = info.getChanges().get(i); 175 final SurfaceControl leash = leashMap.get(change.getLeash()); 176 final int mode = info.getChanges().get(i).getMode(); 177 // Only deal with independent layers 178 if (!TransitionInfo.isIndependent(change, info)) continue; 179 if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { 180 t.setLayer(leash, info.getChanges().size() * 3 - i); 181 counterLauncher.addChild(t, leash); 182 } 183 } 184 // Make wallpaper visible immediately since launcher apparently won't do this. 185 for (int i = wallpapersCompat.length - 1; i >= 0; --i) { 186 t.show(wallpapersCompat[i].leash.getSurfaceControl()); 187 t.setAlpha(wallpapersCompat[i].leash.getSurfaceControl(), 1.f); 188 } 189 } else { 190 if (launcherTask != null) { 191 counterLauncher.addChild(t, leashMap.get(launcherTask.getLeash())); 192 } 193 if (wallpaper != null && rotateDelta != 0 && wallpaper.getParent() != null) { 194 counterWallpaper.setup(t, info.getChange(wallpaper.getParent()).getLeash(), 195 rotateDelta, displayW, displayH); 196 if (counterWallpaper.getSurface() != null) { 197 t.setLayer(counterWallpaper.getSurface(), -1); 198 counterWallpaper.addChild(t, leashMap.get(wallpaper.getLeash())); 199 } 200 } 201 } 202 t.apply(); 203 204 final Runnable animationFinishedCallback = new Runnable() { 205 @Override 206 @SuppressLint("NewApi") 207 public void run() { 208 try { 209 counterLauncher.cleanUp(info.getRootLeash()); 210 counterWallpaper.cleanUp(info.getRootLeash()); 211 // Release surface references now. This is apparently to free GPU 212 // memory while doing quick operations (eg. during CTS). 213 for (int i = 0; i < info.getChanges().size(); ++i) { 214 info.getChanges().get(i).getLeash().release(); 215 } 216 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 217 for (int i = 0; i < leashMap.size(); ++i) { 218 if (leashMap.keyAt(i) == leashMap.valueAt(i)) continue; 219 t.remove(leashMap.valueAt(i)); 220 } 221 t.apply(); 222 finishCallback.onTransitionFinished(null /* wct */, null /* sct */); 223 } catch (RemoteException e) { 224 Log.e("ActivityOptionsCompat", "Failed to call app controlled animation" 225 + " finished callback", e); 226 } 227 } 228 }; 229 // TODO(bc-unlcok): Pass correct transit type. 230 remoteAnimationAdapter.onAnimationStart( 231 TRANSIT_OLD_NONE, 232 appsCompat, wallpapersCompat, nonAppsCompat, 233 animationFinishedCallback); 234 } 235 236 @Override 237 public void mergeAnimation(IBinder token, TransitionInfo info, 238 SurfaceControl.Transaction t, IBinder mergeTarget, 239 IRemoteTransitionFinishedCallback finishCallback) { 240 // TODO: hook up merge to recents onTaskAppeared if applicable. Until then, ignore 241 // any incoming merges. 242 } 243 }; 244 } 245 } 246