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 package com.android.quickstep.util; 17 18 import android.annotation.TargetApi; 19 import android.os.Build; 20 import android.os.Handler; 21 import android.os.Message; 22 import android.view.SurfaceControl; 23 import android.view.SurfaceControl.Transaction; 24 import android.view.View; 25 import android.view.View.OnAttachStateChangeListener; 26 import android.view.ViewRootImpl; 27 28 import com.android.quickstep.RemoteAnimationTargets.ReleaseCheck; 29 30 /** 31 * Helper class to apply surface transactions in sync with RenderThread similar to 32 * android.view.SyncRtSurfaceTransactionApplier 33 * with some Launcher specific utility methods 34 */ 35 @TargetApi(Build.VERSION_CODES.R) 36 public class SurfaceTransactionApplier extends ReleaseCheck { 37 38 private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0; 39 40 private final Handler mApplyHandler; 41 42 private boolean mInitialized; 43 private SurfaceControl mBarrierSurfaceControl; 44 private ViewRootImpl mTargetViewRootImpl; 45 46 private int mLastSequenceNumber = 0; 47 48 /** 49 * @param targetView The view in the surface that acts as synchronization anchor. 50 */ SurfaceTransactionApplier(View targetView)51 public SurfaceTransactionApplier(View targetView) { 52 if (targetView.isAttachedToWindow()) { 53 initialize(targetView); 54 } else { 55 mInitialized = false; 56 targetView.addOnAttachStateChangeListener(new OnAttachStateChangeListener() { 57 @Override 58 public void onViewAttachedToWindow(View v) { 59 if (!mInitialized) { 60 targetView.removeOnAttachStateChangeListener(this); 61 initialize(targetView); 62 } 63 } 64 65 @Override 66 public void onViewDetachedFromWindow(View v) { 67 // Do nothing 68 } 69 }); 70 } 71 mApplyHandler = new Handler(this::onApplyMessage); 72 setCanRelease(true); 73 } 74 initialize(View view)75 private void initialize(View view) { 76 mTargetViewRootImpl = view.getViewRootImpl(); 77 mBarrierSurfaceControl = mTargetViewRootImpl.getSurfaceControl(); 78 mInitialized = true; 79 } 80 onApplyMessage(Message msg)81 protected boolean onApplyMessage(Message msg) { 82 if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) { 83 setCanRelease(msg.arg1 == mLastSequenceNumber); 84 return true; 85 } 86 return false; 87 } 88 89 /** 90 * Schedules applying surface parameters on the next frame. 91 * 92 * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into 93 * this method to avoid synchronization issues. 94 */ scheduleApply(SurfaceTransaction params)95 public void scheduleApply(SurfaceTransaction params) { 96 if (!mInitialized) { 97 params.getTransaction().apply(); 98 return; 99 } 100 View view = mTargetViewRootImpl.getView(); 101 if (view == null) { 102 return; 103 } 104 Transaction t = params.getTransaction(); 105 106 mLastSequenceNumber++; 107 final int toApplySeqNo = mLastSequenceNumber; 108 setCanRelease(false); 109 mTargetViewRootImpl.registerRtFrameCallback(frame -> { 110 if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) { 111 Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) 112 .sendToTarget(); 113 return; 114 } 115 mTargetViewRootImpl.mergeWithNextTransaction(t, frame); 116 Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) 117 .sendToTarget(); 118 }); 119 120 // Make sure a frame gets scheduled. 121 view.invalidate(); 122 } 123 } 124