1 /* 2 * Copyright (C) 2023 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.wm.shell.windowdecor; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.ValueAnimator; 22 import android.annotation.ColorRes; 23 import android.app.ActivityManager.RunningTaskInfo; 24 import android.content.Context; 25 import android.content.res.Configuration; 26 import android.graphics.PixelFormat; 27 import android.graphics.Rect; 28 import android.graphics.drawable.Drawable; 29 import android.view.Display; 30 import android.view.LayoutInflater; 31 import android.view.SurfaceControl; 32 import android.view.SurfaceControlViewHost; 33 import android.view.View; 34 import android.view.WindowManager; 35 import android.view.WindowlessWindowManager; 36 import android.widget.ImageView; 37 import android.window.TaskConstants; 38 39 import com.android.wm.shell.R; 40 41 import java.util.function.Supplier; 42 43 /** 44 * Creates and updates a veil that covers task contents on resize. 45 */ 46 public class ResizeVeil { 47 private static final int RESIZE_ALPHA_DURATION = 100; 48 private final Context mContext; 49 private final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; 50 private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier; 51 private final Drawable mAppIcon; 52 private SurfaceControl mParentSurface; 53 private SurfaceControl mVeilSurface; 54 private final RunningTaskInfo mTaskInfo; 55 private SurfaceControlViewHost mViewHost; 56 private final Display mDisplay; 57 ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo, Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display, Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier)58 public ResizeVeil(Context context, Drawable appIcon, RunningTaskInfo taskInfo, 59 Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, Display display, 60 Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) { 61 mContext = context; 62 mAppIcon = appIcon; 63 mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier; 64 mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier; 65 mTaskInfo = taskInfo; 66 mDisplay = display; 67 setupResizeVeil(); 68 } 69 70 /** 71 * Create the veil in its default invisible state. 72 */ setupResizeVeil()73 private void setupResizeVeil() { 74 SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 75 final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); 76 mVeilSurface = builder 77 .setName("Resize veil of Task= " + mTaskInfo.taskId) 78 .setContainerLayer() 79 .build(); 80 View v = LayoutInflater.from(mContext) 81 .inflate(R.layout.desktop_mode_resize_veil, null); 82 83 t.setPosition(mVeilSurface, 0, 0) 84 .setLayer(mVeilSurface, TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL) 85 .apply(); 86 Rect taskBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); 87 final WindowManager.LayoutParams lp = 88 new WindowManager.LayoutParams(taskBounds.width(), 89 taskBounds.height(), 90 WindowManager.LayoutParams.TYPE_APPLICATION, 91 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 92 PixelFormat.TRANSPARENT); 93 lp.setTitle("Resize veil of Task=" + mTaskInfo.taskId); 94 lp.setTrustedOverlay(); 95 WindowlessWindowManager windowManager = new WindowlessWindowManager(mTaskInfo.configuration, 96 mVeilSurface, null /* hostInputToken */); 97 mViewHost = new SurfaceControlViewHost(mContext, mDisplay, windowManager, "ResizeVeil"); 98 mViewHost.setView(v, lp); 99 100 final ImageView appIcon = mViewHost.getView().findViewById(R.id.veil_application_icon); 101 appIcon.setImageDrawable(mAppIcon); 102 } 103 104 /** 105 * Shows the veil surface/view. 106 * 107 * @param t the transaction to apply in sync with the veil draw 108 * @param parentSurface the surface that the veil should be a child of 109 * @param taskBounds the bounds of the task that owns the veil 110 * @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown 111 * immediately 112 */ showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface, Rect taskBounds, boolean fadeIn)113 public void showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface, 114 Rect taskBounds, boolean fadeIn) { 115 // Parent surface can change, ensure it is up to date. 116 if (!parentSurface.equals(mParentSurface)) { 117 t.reparent(mVeilSurface, parentSurface); 118 mParentSurface = parentSurface; 119 } 120 121 int backgroundColorId = getBackgroundColorId(); 122 mViewHost.getView().setBackgroundColor(mContext.getColor(backgroundColorId)); 123 124 relayout(taskBounds, t); 125 if (fadeIn) { 126 final ValueAnimator animator = new ValueAnimator(); 127 animator.setFloatValues(0f, 1f); 128 animator.setDuration(RESIZE_ALPHA_DURATION); 129 animator.addUpdateListener(animation -> { 130 t.setAlpha(mVeilSurface, animator.getAnimatedFraction()); 131 t.apply(); 132 }); 133 134 t.show(mVeilSurface) 135 .addTransactionCommittedListener( 136 mContext.getMainExecutor(), () -> animator.start()) 137 .setAlpha(mVeilSurface, 0); 138 } else { 139 // Show the veil immediately at full opacity. 140 t.show(mVeilSurface).setAlpha(mVeilSurface, 1); 141 } 142 mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t); 143 } 144 145 /** 146 * Animate veil's alpha to 1, fading it in. 147 */ showVeil(SurfaceControl parentSurface, Rect taskBounds)148 public void showVeil(SurfaceControl parentSurface, Rect taskBounds) { 149 SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 150 showVeil(t, parentSurface, taskBounds, true /* fadeIn */); 151 } 152 153 /** 154 * Update veil bounds to match bounds changes. 155 * @param newBounds bounds to update veil to. 156 */ relayout(Rect newBounds, SurfaceControl.Transaction t)157 private void relayout(Rect newBounds, SurfaceControl.Transaction t) { 158 mViewHost.relayout(newBounds.width(), newBounds.height()); 159 t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height()); 160 t.setPosition(mParentSurface, newBounds.left, newBounds.top); 161 t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height()); 162 } 163 164 /** 165 * Calls relayout to update task and veil bounds. 166 * @param newBounds bounds to update veil to. 167 */ updateResizeVeil(Rect newBounds)168 public void updateResizeVeil(Rect newBounds) { 169 SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 170 updateResizeVeil(t, newBounds); 171 } 172 173 /** 174 * Calls relayout to update task and veil bounds. 175 * 176 * @param t a transaction to be applied in sync with the veil draw. 177 * @param newBounds bounds to update veil to. 178 */ updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds)179 public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) { 180 relayout(newBounds, t); 181 mViewHost.getView().getViewRootImpl().applyTransactionOnDraw(t); 182 } 183 184 /** 185 * Animate veil's alpha to 0, fading it out. 186 */ hideVeil()187 public void hideVeil() { 188 final ValueAnimator animator = new ValueAnimator(); 189 animator.setFloatValues(1, 0); 190 animator.setDuration(RESIZE_ALPHA_DURATION); 191 animator.addUpdateListener(animation -> { 192 SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 193 t.setAlpha(mVeilSurface, 1 - animator.getAnimatedFraction()); 194 t.apply(); 195 }); 196 animator.addListener(new AnimatorListenerAdapter() { 197 @Override 198 public void onAnimationEnd(Animator animation) { 199 SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 200 t.hide(mVeilSurface); 201 t.apply(); 202 } 203 }); 204 animator.start(); 205 } 206 207 @ColorRes getBackgroundColorId()208 private int getBackgroundColorId() { 209 Configuration configuration = mContext.getResources().getConfiguration(); 210 if ((configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) 211 == Configuration.UI_MODE_NIGHT_YES) { 212 return R.color.desktop_mode_resize_veil_dark; 213 } else { 214 return R.color.desktop_mode_resize_veil_light; 215 } 216 } 217 218 /** 219 * Dispose of veil when it is no longer needed, likely on close of its container decor. 220 */ dispose()221 void dispose() { 222 if (mViewHost != null) { 223 mViewHost.release(); 224 mViewHost = null; 225 } 226 if (mVeilSurface != null) { 227 final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get(); 228 t.remove(mVeilSurface); 229 mVeilSurface = null; 230 t.apply(); 231 } 232 } 233 } 234