• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 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.systemui.common.ui.view
18 
19 import android.view.Choreographer
20 import android.view.View
21 import com.android.app.tracing.coroutines.TrackTracer
22 import kotlin.coroutines.resume
23 import kotlinx.coroutines.suspendCancellableCoroutine
24 
25 /** utilities related to [Choreographer]. */
26 interface ChoreographerUtils {
27     /**
28      * Waits until the next [view] doFrame is completed.
29      *
30      * Note that this is expected to work properly when called from any thread. If called during a
31      * doFrame, it waits for the next one to be completed.
32      *
33      * This differs from [kotlinx.coroutines.android.awaitFrame] as it uses
34      * [Handler.postAtFrontOfQueue] instead of [Handler.post] when called from a thread different
35      * than the UI thread for that view. Using [Handler.post] might lead to posting the runnable
36      * after a few frame, effectively missing the "next do frame".
37      */
38     suspend fun waitUntilNextDoFrameDone(view: View)
39 }
40 
41 object ChoreographerUtilsImpl : ChoreographerUtils {
42     private val t = TrackTracer("ChoreographerUtils")
43 
waitUntilNextDoFrameDonenull44     override suspend fun waitUntilNextDoFrameDone(view: View) {
45         t.traceAsync("waitUntilNextDoFrameDone") { waitUntilNextDoFrameDoneTraced(view) }
46     }
47 
waitUntilNextDoFrameDoneTracednull48     suspend fun waitUntilNextDoFrameDoneTraced(view: View) {
49         suspendCancellableCoroutine { cont ->
50             val frameCallback =
51                 Choreographer.FrameCallback {
52                     t.instant { "We're in doFrame, waiting for it to end." }
53                     view.handler.postAtFrontOfQueue {
54                         t.instant { "DoFrame ended." }
55                         cont.resume(Unit)
56                     }
57                 }
58             view.runOnUiThreadUrgently {
59                 t.instant { "Waiting for next doFrame" }
60                 val choreographer = Choreographer.getInstance()
61                 cont.invokeOnCancellation { choreographer.removeFrameCallback(frameCallback) }
62                 choreographer.postFrameCallback(frameCallback)
63             }
64         }
65     }
66 
67     /**
68      * Execute [r] on the view UI thread, taking priority over everything else scheduled there. Runs
69      * directly if we're already in the correct thread.
70      */
runOnUiThreadUrgentlynull71     private fun View.runOnUiThreadUrgently(r: () -> Unit) {
72         if (handler.looper.isCurrentThread) {
73             r()
74         } else {
75             handler.postAtFrontOfQueue(r)
76         }
77     }
78 }
79