1 /* 2 * Copyright (C) 2020 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.pip; 18 19 import android.content.Context; 20 import android.graphics.Matrix; 21 import android.graphics.Rect; 22 import android.graphics.RectF; 23 import android.view.SurfaceControl; 24 25 import com.android.wm.shell.R; 26 27 /** 28 * Abstracts the common operations on {@link SurfaceControl.Transaction} for PiP transition. 29 */ 30 public class PipSurfaceTransactionHelper { 31 /** for {@link #scale(SurfaceControl.Transaction, SurfaceControl, Rect, Rect)} operation */ 32 private final Matrix mTmpTransform = new Matrix(); 33 private final float[] mTmpFloat9 = new float[9]; 34 private final RectF mTmpSourceRectF = new RectF(); 35 private final RectF mTmpDestinationRectF = new RectF(); 36 private final Rect mTmpDestinationRect = new Rect(); 37 38 private int mCornerRadius; 39 40 /** 41 * Called when display size or font size of settings changed 42 * 43 * @param context the current context 44 */ onDensityOrFontScaleChanged(Context context)45 public void onDensityOrFontScaleChanged(Context context) { 46 mCornerRadius = context.getResources().getDimensionPixelSize(R.dimen.pip_corner_radius); 47 } 48 49 /** 50 * Operates the alpha on a given transaction and leash 51 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 52 */ alpha(SurfaceControl.Transaction tx, SurfaceControl leash, float alpha)53 public PipSurfaceTransactionHelper alpha(SurfaceControl.Transaction tx, SurfaceControl leash, 54 float alpha) { 55 tx.setAlpha(leash, alpha); 56 return this; 57 } 58 59 /** 60 * Operates the crop (and position) on a given transaction and leash 61 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 62 */ crop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds)63 public PipSurfaceTransactionHelper crop(SurfaceControl.Transaction tx, SurfaceControl leash, 64 Rect destinationBounds) { 65 tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height()) 66 .setPosition(leash, destinationBounds.left, destinationBounds.top); 67 return this; 68 } 69 70 /** 71 * Operates the scale (setMatrix) on a given transaction and leash 72 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 73 */ scale(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds)74 public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash, 75 Rect sourceBounds, Rect destinationBounds) { 76 return scale(tx, leash, sourceBounds, destinationBounds, 0 /* degrees */); 77 } 78 79 /** 80 * Operates the scale (setMatrix) on a given transaction and leash, along with a rotation. 81 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 82 */ scale(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, float degrees)83 public PipSurfaceTransactionHelper scale(SurfaceControl.Transaction tx, SurfaceControl leash, 84 Rect sourceBounds, Rect destinationBounds, float degrees) { 85 mTmpSourceRectF.set(sourceBounds); 86 // We want the matrix to position the surface relative to the screen coordinates so offset 87 // the source to 0,0 88 mTmpSourceRectF.offsetTo(0, 0); 89 mTmpDestinationRectF.set(destinationBounds); 90 mTmpTransform.setRectToRect(mTmpSourceRectF, mTmpDestinationRectF, Matrix.ScaleToFit.FILL); 91 mTmpTransform.postRotate(degrees, 92 mTmpDestinationRectF.centerX(), mTmpDestinationRectF.centerY()); 93 tx.setMatrix(leash, mTmpTransform, mTmpFloat9); 94 return this; 95 } 96 97 /** 98 * Operates the scale (setMatrix) on a given transaction and leash 99 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 100 */ scaleAndCrop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets)101 public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, 102 SurfaceControl leash, 103 Rect sourceBounds, Rect destinationBounds, Rect insets) { 104 mTmpSourceRectF.set(sourceBounds); 105 mTmpDestinationRect.set(sourceBounds); 106 mTmpDestinationRect.inset(insets); 107 // Scale by the shortest edge and offset such that the top/left of the scaled inset source 108 // rect aligns with the top/left of the destination bounds 109 final float scale = sourceBounds.width() <= sourceBounds.height() 110 ? (float) destinationBounds.width() / sourceBounds.width() 111 : (float) destinationBounds.height() / sourceBounds.height(); 112 final float left = destinationBounds.left - insets.left * scale; 113 final float top = destinationBounds.top - insets.top * scale; 114 mTmpTransform.setScale(scale, scale); 115 tx.setMatrix(leash, mTmpTransform, mTmpFloat9) 116 .setWindowCrop(leash, mTmpDestinationRect) 117 .setPosition(leash, left, top); 118 return this; 119 } 120 121 /** 122 * Operates the rotation according to the given degrees and scale (setMatrix) according to the 123 * source bounds and rotated destination bounds. The crop will be the unscaled source bounds. 124 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 125 */ rotateAndScaleWithCrop(SurfaceControl.Transaction tx, SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets, float degrees, float positionX, float positionY, boolean isExpanding, boolean clockwise)126 public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx, 127 SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets, 128 float degrees, float positionX, float positionY, boolean isExpanding, 129 boolean clockwise) { 130 mTmpDestinationRect.set(sourceBounds); 131 mTmpDestinationRect.inset(insets); 132 final int srcW = mTmpDestinationRect.width(); 133 final int srcH = mTmpDestinationRect.height(); 134 final int destW = destinationBounds.width(); 135 final int destH = destinationBounds.height(); 136 // Scale by the short side so there won't be empty area if the aspect ratio of source and 137 // destination are different. 138 final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH; 139 final Rect crop = mTmpDestinationRect; 140 crop.set(0, 0, destW, destH); 141 // Inverse scale for crop to fit in screen coordinates. 142 crop.scale(1 / scale); 143 crop.offset(insets.left, insets.top); 144 if (isExpanding) { 145 // Expand bounds (shrink insets) in source orientation. 146 positionX -= insets.left * scale; 147 positionY -= insets.top * scale; 148 } else { 149 // Shrink bounds (expand insets) in destination orientation. 150 if (clockwise) { 151 positionX -= insets.top * scale; 152 positionY += insets.left * scale; 153 } else { 154 positionX += insets.top * scale; 155 positionY -= insets.left * scale; 156 } 157 } 158 mTmpTransform.setScale(scale, scale); 159 mTmpTransform.postRotate(degrees); 160 mTmpTransform.postTranslate(positionX, positionY); 161 tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setWindowCrop(leash, crop); 162 return this; 163 } 164 165 /** 166 * Resets the scale (setMatrix) on a given transaction and leash if there's any 167 * 168 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 169 */ resetScale(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds)170 public PipSurfaceTransactionHelper resetScale(SurfaceControl.Transaction tx, 171 SurfaceControl leash, 172 Rect destinationBounds) { 173 tx.setMatrix(leash, Matrix.IDENTITY_MATRIX, mTmpFloat9) 174 .setPosition(leash, destinationBounds.left, destinationBounds.top); 175 return this; 176 } 177 178 /** 179 * Operates the round corner radius on a given transaction and leash 180 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 181 */ round(SurfaceControl.Transaction tx, SurfaceControl leash, boolean applyCornerRadius)182 public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash, 183 boolean applyCornerRadius) { 184 tx.setCornerRadius(leash, applyCornerRadius ? mCornerRadius : 0); 185 return this; 186 } 187 188 /** 189 * Operates the round corner radius on a given transaction and leash, scaled by bounds 190 * @return same {@link PipSurfaceTransactionHelper} instance for method chaining 191 */ round(SurfaceControl.Transaction tx, SurfaceControl leash, Rect fromBounds, Rect toBounds)192 public PipSurfaceTransactionHelper round(SurfaceControl.Transaction tx, SurfaceControl leash, 193 Rect fromBounds, Rect toBounds) { 194 final float scale = (float) (Math.hypot(fromBounds.width(), fromBounds.height()) 195 / Math.hypot(toBounds.width(), toBounds.height())); 196 tx.setCornerRadius(leash, mCornerRadius * scale); 197 return this; 198 } 199 200 /** 201 * Re-parents the snapshot to the parent's surface control and shows it. 202 */ reparentAndShowSurfaceSnapshot( SurfaceControl.Transaction t, SurfaceControl parent, SurfaceControl snapshot)203 public PipSurfaceTransactionHelper reparentAndShowSurfaceSnapshot( 204 SurfaceControl.Transaction t, SurfaceControl parent, SurfaceControl snapshot) { 205 t.reparent(snapshot, parent); 206 t.setLayer(snapshot, Integer.MAX_VALUE); 207 t.show(snapshot); 208 t.apply(); 209 return this; 210 } 211 212 public interface SurfaceControlTransactionFactory { getTransaction()213 SurfaceControl.Transaction getTransaction(); 214 } 215 } 216