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