1 /*
<lambda>null2  * Copyright (C) 2023 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.dream.lowlight
17 
18 import android.animation.Animator
19 import android.animation.AnimatorListenerAdapter
20 import com.android.dream.lowlight.util.suspendCoroutineWithTimeout
21 import javax.inject.Inject
22 import javax.inject.Singleton
23 import kotlin.coroutines.resume
24 import kotlin.time.Duration
25 
26 /**
27  * Helper class that allows listening and running animations before entering or exiting low light.
28  */
29 @Singleton
30 class LowLightTransitionCoordinator @Inject constructor() {
31     /**
32      * Listener that is notified before low light entry.
33      */
34     interface LowLightEnterListener {
35         /**
36          * Callback that is notified before the device enters low light.
37          *
38          * @return an optional animator that will be waited upon before entering low light.
39          */
40         fun onBeforeEnterLowLight(): Animator?
41     }
42 
43     /**
44      * Listener that is notified before low light exit.
45      */
46     interface LowLightExitListener {
47         /**
48          * Callback that is notified before the device exits low light.
49          *
50          * @return an optional animator that will be waited upon before exiting low light.
51          */
52         fun onBeforeExitLowLight(): Animator?
53     }
54 
55     private var mLowLightEnterListener: LowLightEnterListener? = null
56     private var mLowLightExitListener: LowLightExitListener? = null
57 
58     /**
59      * Sets the listener for the low light enter event.
60      *
61      * Only one listener can be set at a time. This method will overwrite any previously set
62      * listener. Null can be used to unset the listener.
63      */
64     fun setLowLightEnterListener(lowLightEnterListener: LowLightEnterListener?) {
65         mLowLightEnterListener = lowLightEnterListener
66     }
67 
68     /**
69      * Sets the listener for the low light exit event.
70      *
71      * Only one listener can be set at a time. This method will overwrite any previously set
72      * listener. Null can be used to unset the listener.
73      */
74     fun setLowLightExitListener(lowLightExitListener: LowLightExitListener?) {
75         mLowLightExitListener = lowLightExitListener
76     }
77 
78     /**
79      * Notifies listeners that the device is about to enter or exit low light, and waits for the
80      * animation to complete. If this function is cancelled, the animation is also cancelled.
81      *
82      * @param timeout the maximum duration to wait for the transition animation. If the animation
83      * does not complete within this time period, a
84      * @param entering true if listeners should be notified before entering low light, false if this
85      * is notifying before exiting.
86      */
87     suspend fun waitForLowLightTransitionAnimation(timeout: Duration, entering: Boolean) =
88         suspendCoroutineWithTimeout(timeout) { continuation ->
89             var animator: Animator? = null
90             if (entering && mLowLightEnterListener != null) {
91                 animator = mLowLightEnterListener!!.onBeforeEnterLowLight()
92             } else if (!entering && mLowLightExitListener != null) {
93                 animator = mLowLightExitListener!!.onBeforeExitLowLight()
94             }
95 
96             if (animator == null) {
97                 continuation.resume(Unit)
98                 return@suspendCoroutineWithTimeout
99             }
100 
101             // If the listener returned an animator to indicate it was running an animation, run the
102             // callback after the animation completes, otherwise call the callback directly.
103             val listener = object : AnimatorListenerAdapter() {
104                 override fun onAnimationEnd(animator: Animator) {
105                     continuation.resume(Unit)
106                 }
107 
108                 override fun onAnimationCancel(animation: Animator) {
109                     continuation.cancel()
110                 }
111             }
112             animator.addListener(listener)
113         }
114 }
115