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 android.view; 18 19 import static android.view.InsetsState.INSET_SIDE_BOTTOM; 20 import static android.view.InsetsState.INSET_SIDE_LEFT; 21 import static android.view.InsetsState.INSET_SIDE_RIGHT; 22 import static android.view.InsetsState.INSET_SIDE_TOP; 23 import static android.view.InsetsState.toPublicType; 24 25 import android.annotation.Nullable; 26 import android.graphics.Insets; 27 import android.graphics.Matrix; 28 import android.graphics.Rect; 29 import android.os.UidProto.Sync; 30 import android.util.ArraySet; 31 import android.util.SparseArray; 32 import android.util.SparseIntArray; 33 import android.util.SparseSetArray; 34 import android.view.InsetsState.InsetSide; 35 import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; 36 import android.view.WindowInsets.Type.InsetType; 37 import android.view.WindowInsetsAnimationListener.InsetsAnimation; 38 import android.view.WindowManager.LayoutParams; 39 40 import com.android.internal.annotations.VisibleForTesting; 41 42 import java.util.ArrayList; 43 import java.util.function.Function; 44 import java.util.function.Supplier; 45 46 /** 47 * Implements {@link WindowInsetsAnimationController} 48 * @hide 49 */ 50 @VisibleForTesting 51 public class InsetsAnimationControlImpl implements WindowInsetsAnimationController { 52 53 private final Rect mTmpFrame = new Rect(); 54 55 private final WindowInsetsAnimationControlListener mListener; 56 private final SparseArray<InsetsSourceConsumer> mConsumers; 57 private final SparseIntArray mTypeSideMap = new SparseIntArray(); 58 private final SparseSetArray<InsetsSourceConsumer> mSideSourceMap = new SparseSetArray<>(); 59 60 /** @see WindowInsetsAnimationController#getHiddenStateInsets */ 61 private final Insets mHiddenInsets; 62 63 /** @see WindowInsetsAnimationController#getShownStateInsets */ 64 private final Insets mShownInsets; 65 private final Matrix mTmpMatrix = new Matrix(); 66 private final InsetsState mInitialInsetsState; 67 private final @InsetType int mTypes; 68 private final Supplier<SyncRtSurfaceTransactionApplier> mTransactionApplierSupplier; 69 private final InsetsController mController; 70 private final WindowInsetsAnimationListener.InsetsAnimation mAnimation; 71 private final Rect mFrame; 72 private Insets mCurrentInsets; 73 private Insets mPendingInsets; 74 private boolean mFinished; 75 private boolean mCancelled; 76 private int mFinishedShownTypes; 77 78 @VisibleForTesting InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame, InsetsState state, WindowInsetsAnimationControlListener listener, @InsetType int types, Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier, InsetsController controller)79 public InsetsAnimationControlImpl(SparseArray<InsetsSourceConsumer> consumers, Rect frame, 80 InsetsState state, WindowInsetsAnimationControlListener listener, 81 @InsetType int types, 82 Supplier<SyncRtSurfaceTransactionApplier> transactionApplierSupplier, 83 InsetsController controller) { 84 mConsumers = consumers; 85 mListener = listener; 86 mTypes = types; 87 mTransactionApplierSupplier = transactionApplierSupplier; 88 mController = controller; 89 mInitialInsetsState = new InsetsState(state, true /* copySources */); 90 mCurrentInsets = getInsetsFromState(mInitialInsetsState, frame, null /* typeSideMap */); 91 mHiddenInsets = calculateInsets(mInitialInsetsState, frame, consumers, false /* shown */, 92 null /* typeSideMap */); 93 mShownInsets = calculateInsets(mInitialInsetsState, frame, consumers, true /* shown */, 94 mTypeSideMap); 95 mFrame = new Rect(frame); 96 buildTypeSourcesMap(mTypeSideMap, mSideSourceMap, mConsumers); 97 98 // TODO: Check for controllability first and wait for IME if needed. 99 listener.onReady(this, types); 100 101 mAnimation = new WindowInsetsAnimationListener.InsetsAnimation(mTypes, mHiddenInsets, 102 mShownInsets); 103 mController.dispatchAnimationStarted(mAnimation); 104 } 105 106 @Override getHiddenStateInsets()107 public Insets getHiddenStateInsets() { 108 return mHiddenInsets; 109 } 110 111 @Override getShownStateInsets()112 public Insets getShownStateInsets() { 113 return mShownInsets; 114 } 115 116 @Override getCurrentInsets()117 public Insets getCurrentInsets() { 118 return mCurrentInsets; 119 } 120 121 @Override 122 @InsetType getTypes()123 public int getTypes() { 124 return mTypes; 125 } 126 127 @Override changeInsets(Insets insets)128 public void changeInsets(Insets insets) { 129 if (mFinished) { 130 throw new IllegalStateException( 131 "Can't change insets on an animation that is finished."); 132 } 133 if (mCancelled) { 134 throw new IllegalStateException( 135 "Can't change insets on an animation that is cancelled."); 136 } 137 mPendingInsets = sanitize(insets); 138 mController.scheduleApplyChangeInsets(); 139 } 140 141 @VisibleForTesting 142 /** 143 * @return Whether the finish callback of this animation should be invoked. 144 */ applyChangeInsets(InsetsState state)145 public boolean applyChangeInsets(InsetsState state) { 146 if (mCancelled) { 147 return false; 148 } 149 final Insets offset = Insets.subtract(mShownInsets, mPendingInsets); 150 ArrayList<SurfaceParams> params = new ArrayList<>(); 151 if (offset.left != 0) { 152 updateLeashesForSide(INSET_SIDE_LEFT, offset.left, mPendingInsets.left, params, state); 153 } 154 if (offset.top != 0) { 155 updateLeashesForSide(INSET_SIDE_TOP, offset.top, mPendingInsets.top, params, state); 156 } 157 if (offset.right != 0) { 158 updateLeashesForSide(INSET_SIDE_RIGHT, offset.right, mPendingInsets.right, params, 159 state); 160 } 161 if (offset.bottom != 0) { 162 updateLeashesForSide(INSET_SIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params, 163 state); 164 } 165 SyncRtSurfaceTransactionApplier applier = mTransactionApplierSupplier.get(); 166 applier.scheduleApply(params.toArray(new SurfaceParams[params.size()])); 167 mCurrentInsets = mPendingInsets; 168 if (mFinished) { 169 mController.notifyFinished(this, mFinishedShownTypes); 170 } 171 return mFinished; 172 } 173 174 @Override finish(int shownTypes)175 public void finish(int shownTypes) { 176 if (mCancelled) { 177 return; 178 } 179 InsetsState state = new InsetsState(mController.getState()); 180 for (int i = mConsumers.size() - 1; i >= 0; i--) { 181 InsetsSourceConsumer consumer = mConsumers.valueAt(i); 182 boolean visible = (shownTypes & toPublicType(consumer.getType())) != 0; 183 state.getSource(consumer.getType()).setVisible(visible); 184 } 185 Insets insets = getInsetsFromState(state, mFrame, null /* typeSideMap */); 186 changeInsets(insets); 187 mFinished = true; 188 mFinishedShownTypes = shownTypes; 189 } 190 191 @VisibleForTesting onCancelled()192 public void onCancelled() { 193 if (mFinished) { 194 return; 195 } 196 mCancelled = true; 197 mListener.onCancelled(); 198 } 199 getAnimation()200 InsetsAnimation getAnimation() { 201 return mAnimation; 202 } 203 calculateInsets(InsetsState state, Rect frame, SparseArray<InsetsSourceConsumer> consumers, boolean shown, @Nullable @InsetSide SparseIntArray typeSideMap)204 private Insets calculateInsets(InsetsState state, Rect frame, 205 SparseArray<InsetsSourceConsumer> consumers, boolean shown, 206 @Nullable @InsetSide SparseIntArray typeSideMap) { 207 for (int i = consumers.size() - 1; i >= 0; i--) { 208 state.getSource(consumers.valueAt(i).getType()).setVisible(shown); 209 } 210 return getInsetsFromState(state, frame, typeSideMap); 211 } 212 getInsetsFromState(InsetsState state, Rect frame, @Nullable @InsetSide SparseIntArray typeSideMap)213 private Insets getInsetsFromState(InsetsState state, Rect frame, 214 @Nullable @InsetSide SparseIntArray typeSideMap) { 215 return state.calculateInsets(frame, false /* isScreenRound */, 216 false /* alwaysConsumerNavBar */, null /* displayCutout */, 217 null /* legacyContentInsets */, null /* legacyStableInsets */, 218 LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/, typeSideMap) 219 .getInsets(mTypes); 220 } 221 sanitize(Insets insets)222 private Insets sanitize(Insets insets) { 223 return Insets.max(Insets.min(insets, mShownInsets), mHiddenInsets); 224 } 225 updateLeashesForSide(@nsetSide int side, int offset, int inset, ArrayList<SurfaceParams> surfaceParams, InsetsState state)226 private void updateLeashesForSide(@InsetSide int side, int offset, int inset, 227 ArrayList<SurfaceParams> surfaceParams, InsetsState state) { 228 ArraySet<InsetsSourceConsumer> items = mSideSourceMap.get(side); 229 // TODO: Implement behavior when inset spans over multiple types 230 for (int i = items.size() - 1; i >= 0; i--) { 231 final InsetsSourceConsumer consumer = items.valueAt(i); 232 final InsetsSource source = mInitialInsetsState.getSource(consumer.getType()); 233 final InsetsSourceControl control = consumer.getControl(); 234 final SurfaceControl leash = consumer.getControl().getLeash(); 235 236 mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y); 237 mTmpFrame.set(source.getFrame()); 238 addTranslationToMatrix(side, offset, mTmpMatrix, mTmpFrame); 239 240 state.getSource(source.getType()).setFrame(mTmpFrame); 241 surfaceParams.add(new SurfaceParams(leash, 1f, mTmpMatrix, null, 0, 0f, inset != 0)); 242 } 243 } 244 addTranslationToMatrix(@nsetSide int side, int inset, Matrix m, Rect frame)245 private void addTranslationToMatrix(@InsetSide int side, int inset, Matrix m, Rect frame) { 246 switch (side) { 247 case INSET_SIDE_LEFT: 248 m.postTranslate(-inset, 0); 249 frame.offset(-inset, 0); 250 break; 251 case INSET_SIDE_TOP: 252 m.postTranslate(0, -inset); 253 frame.offset(0, -inset); 254 break; 255 case INSET_SIDE_RIGHT: 256 m.postTranslate(inset, 0); 257 frame.offset(inset, 0); 258 break; 259 case INSET_SIDE_BOTTOM: 260 m.postTranslate(0, inset); 261 frame.offset(0, inset); 262 break; 263 } 264 } 265 buildTypeSourcesMap(SparseIntArray typeSideMap, SparseSetArray<InsetsSourceConsumer> sideSourcesMap, SparseArray<InsetsSourceConsumer> consumers)266 private static void buildTypeSourcesMap(SparseIntArray typeSideMap, 267 SparseSetArray<InsetsSourceConsumer> sideSourcesMap, 268 SparseArray<InsetsSourceConsumer> consumers) { 269 for (int i = typeSideMap.size() - 1; i >= 0; i--) { 270 int type = typeSideMap.keyAt(i); 271 int side = typeSideMap.valueAt(i); 272 sideSourcesMap.add(side, consumers.get(type)); 273 } 274 } 275 } 276