1 /*
2  * Copyright 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 androidx.compose.runtime
18 
19 /**
20  * A [MonotonicFrameClock] wrapper that can be [pause]d and [resume]d.
21  *
22  * A paused clock will not dispatch [withFrameNanos] events until it is resumed. Pausing a clock
23  * does **not** stop or change the frame times reported to [withFrameNanos] calls; the clock times
24  * reported will always remain consistent with [frameClock].
25  *
26  * [PausableMonotonicFrameClock] should be used in cases where frames should not be produced under
27  * some conditions, such as when a window hosting a UI is not currently visible. As clock times are
28  * not altered from the source [frameClock], animations in progress may be fully complete by the
29  * time the clock is resumed and a new frame is produced.
30  */
31 class PausableMonotonicFrameClock(private val frameClock: MonotonicFrameClock) :
32     MonotonicFrameClock {
33     private val latch = Latch()
34 
35     /**
36      * `true` if this clock is currently [paused][pause] or `false` if this clock is currently
37      * [resumed][resume]. A PausableMonotonicFrameClock is not paused at construction time.
38      */
39     val isPaused: Boolean
40         get() = !latch.isOpen
41 
42     /**
43      * Pause the generation of frames. Pausing a clock that is already paused has no effect. While
44      * the clock is paused any calls to [withFrameNanos] will suspend until the clock is resumed
45      * before delegating to the wrapped [frameClock]'s [withFrameNanos] method. Call [resume] to
46      * resume generating frames.
47      */
pausenull48     fun pause() {
49         latch.closeLatch()
50     }
51 
52     /**
53      * Resume the generation of frames. Any queued calls to [withFrameNanos] will resume and
54      * delegate to the wrapped [frameClock]'s [withFrameNanos] method.
55      */
resumenull56     fun resume() {
57         latch.openLatch()
58     }
59 
withFrameNanosnull60     override suspend fun <R> withFrameNanos(onFrame: (frameTimeNanos: Long) -> R): R {
61         latch.await()
62         return frameClock.withFrameNanos(onFrame)
63     }
64 }
65