1 /*
2  * Copyright (C) 2022 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.settings.brightness
18 
19 import android.content.Intent
20 import android.graphics.Rect
21 import android.testing.AndroidTestingRunner
22 import android.testing.TestableLooper
23 import android.view.View
24 import android.view.ViewGroup
25 import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
26 import androidx.test.filters.FlakyTest
27 import androidx.test.filters.SmallTest
28 import androidx.test.rule.ActivityTestRule
29 import com.android.systemui.SysuiTestCase
30 import com.android.systemui.activity.SingleActivityFactory
31 import com.android.systemui.res.R
32 import com.android.systemui.shade.domain.interactor.ShadeInteractor
33 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
34 import com.android.systemui.util.concurrency.DelayableExecutor
35 import com.android.systemui.util.concurrency.FakeExecutor
36 import com.android.systemui.util.mockito.any
37 import com.android.systemui.util.mockito.whenever
38 import com.android.systemui.util.time.FakeSystemClock
39 import com.google.common.truth.Truth.assertThat
40 import kotlin.time.Duration.Companion.milliseconds
41 import kotlinx.coroutines.FlowPreview
42 import kotlinx.coroutines.flow.MutableStateFlow
43 import kotlinx.coroutines.flow.takeWhile
44 import kotlinx.coroutines.flow.timeout
45 import kotlinx.coroutines.test.runTest
46 import org.junit.After
47 import org.junit.Before
48 import org.junit.Rule
49 import org.junit.Test
50 import org.junit.runner.RunWith
51 import org.mockito.Mock
52 import org.mockito.Mockito.anyInt
53 import org.mockito.Mockito.eq
54 import org.mockito.Mockito.`when`
55 import org.mockito.MockitoAnnotations
56 
57 @SmallTest
58 @RunWith(AndroidTestingRunner::class)
59 @TestableLooper.RunWithLooper
60 class BrightnessDialogTest : SysuiTestCase() {
61 
62     @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory
63     @Mock private lateinit var brightnessSliderController: BrightnessSliderController
64     @Mock private lateinit var brightnessControllerFactory: BrightnessController.Factory
65     @Mock private lateinit var brightnessController: BrightnessController
66     @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
67     @Mock private lateinit var shadeInteractor: ShadeInteractor
68 
69     private val clock = FakeSystemClock()
70     private val mainExecutor = FakeExecutor(clock)
71 
72     @Rule
73     @JvmField
74     var activityRule =
75         ActivityTestRule(
<lambda>null76             /* activityFactory= */ SingleActivityFactory {
77                 TestDialog(
78                     brightnessSliderControllerFactory,
79                     brightnessControllerFactory,
80                     mainExecutor,
81                     accessibilityMgr,
82                     shadeInteractor
83                 )
84             },
85             /* initialTouchMode= */ false,
86             /* launchActivity= */ false,
87         )
88 
89     @Before
setUpnull90     fun setUp() {
91         MockitoAnnotations.initMocks(this)
92         `when`(brightnessSliderControllerFactory.create(any(), any()))
93             .thenReturn(brightnessSliderController)
94         `when`(brightnessSliderController.rootView).thenReturn(View(context))
95         `when`(brightnessControllerFactory.create(any())).thenReturn(brightnessController)
96         whenever(shadeInteractor.isQsExpanded).thenReturn(MutableStateFlow(false))
97     }
98 
99     @After
tearDownnull100     fun tearDown() {
101         activityRule.finishActivity()
102     }
103 
104     @Test
testGestureExclusionnull105     fun testGestureExclusion() {
106         activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
107         val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container)
108 
109         val lp = frame.layoutParams as ViewGroup.MarginLayoutParams
110         val horizontalMargin =
111             activityRule.activity.resources.getDimensionPixelSize(
112                 R.dimen.notification_side_paddings
113             )
114         assertThat(lp.leftMargin).isEqualTo(horizontalMargin)
115         assertThat(lp.rightMargin).isEqualTo(horizontalMargin)
116 
117         assertThat(frame.systemGestureExclusionRects.size).isEqualTo(1)
118         val exclusion = frame.systemGestureExclusionRects[0]
119         assertThat(exclusion)
120             .isEqualTo(Rect(-horizontalMargin, 0, frame.width + horizontalMargin, frame.height))
121     }
122 
123     @Test
testTimeoutnull124     fun testTimeout() {
125         `when`(
126                 accessibilityMgr.getRecommendedTimeoutMillis(
127                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
128                     anyInt()
129                 )
130             )
131             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
132         val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)
133         intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true)
134         activityRule.launchActivity(intent)
135 
136         assertThat(activityRule.activity.isFinishing()).isFalse()
137 
138         clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong())
139         assertThat(activityRule.activity.isFinishing()).isTrue()
140     }
141 
142     @Test
testRestartTimeoutnull143     fun testRestartTimeout() {
144         `when`(
145                 accessibilityMgr.getRecommendedTimeoutMillis(
146                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
147                     anyInt()
148                 )
149             )
150             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
151         val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)
152         intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true)
153         activityRule.launchActivity(intent)
154 
155         assertThat(activityRule.activity.isFinishing()).isFalse()
156 
157         clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2)
158         // Restart the timeout
159         activityRule.activity.onResume()
160 
161         clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2)
162         // The dialog should not have disappeared yet
163         assertThat(activityRule.activity.isFinishing()).isFalse()
164 
165         clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2)
166         assertThat(activityRule.activity.isFinishing()).isTrue()
167     }
168 
169     @Test
testNoTimeoutIfNotStartedByBrightnessKeynull170     fun testNoTimeoutIfNotStartedByBrightnessKey() {
171         `when`(
172                 accessibilityMgr.getRecommendedTimeoutMillis(
173                     eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
174                     anyInt()
175                 )
176             )
177             .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
178         activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
179 
180         assertThat(activityRule.activity.isFinishing()).isFalse()
181 
182         clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong())
183         assertThat(activityRule.activity.isFinishing()).isFalse()
184     }
185 
186     @OptIn(FlowPreview::class)
187     @FlakyTest(bugId = 326186573)
188     @Test
<lambda>null189     fun testFinishOnQSExpanded() = runTest {
190         val isQSExpanded = MutableStateFlow(false)
191         `when`(shadeInteractor.isQsExpanded).thenReturn(isQSExpanded)
192         activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
193 
194         assertThat(activityRule.activity.isFinishing()).isFalse()
195 
196         isQSExpanded.value = true
197         // Observe the activity's state until is it finishing or the timeout is reached, whatever
198         // comes first. This fixes the flakiness seen when using advanceUntilIdle().
199         activityRule.activity.finishing.timeout(100.milliseconds).takeWhile { !it }.collect {}
200         assertThat(activityRule.activity.isFinishing()).isTrue()
201     }
202 
203     class TestDialog(
204         brightnessSliderControllerFactory: BrightnessSliderController.Factory,
205         brightnessControllerFactory: BrightnessController.Factory,
206         mainExecutor: DelayableExecutor,
207         accessibilityMgr: AccessibilityManagerWrapper,
208         shadeInteractor: ShadeInteractor
209     ) :
210         BrightnessDialog(
211             brightnessSliderControllerFactory,
212             brightnessControllerFactory,
213             mainExecutor,
214             accessibilityMgr,
215             shadeInteractor
216         ) {
217         var finishing = MutableStateFlow(false)
218 
isFinishingnull219         override fun isFinishing(): Boolean {
220             return finishing.value
221         }
222 
requestFinishnull223         override fun requestFinish() {
224             finishing.value = true
225         }
226     }
227 }
228