1 package com.android.systemui.controls.management
2 
3 import android.content.ComponentName
4 import android.content.Intent
5 import android.os.Bundle
6 import android.testing.TestableLooper
7 import android.view.View
8 import android.widget.Button
9 import android.window.OnBackInvokedCallback
10 import android.window.OnBackInvokedDispatcher
11 import androidx.test.ext.junit.runners.AndroidJUnit4
12 import androidx.test.filters.SmallTest
13 import androidx.test.rule.ActivityTestRule
14 import com.android.systemui.res.R
15 import com.android.systemui.SysuiTestCase
16 import com.android.systemui.activity.SingleActivityFactory
17 import com.android.systemui.controls.CustomIconCache
18 import com.android.systemui.controls.controller.ControlsControllerImpl
19 import com.android.systemui.settings.UserTracker
20 import com.android.systemui.util.concurrency.FakeExecutor
21 import com.android.systemui.util.time.FakeSystemClock
22 import com.google.common.truth.Truth.assertThat
23 import java.util.concurrent.CountDownLatch
24 import org.junit.Before
25 import org.junit.Rule
26 import org.junit.Test
27 import org.junit.runner.RunWith
28 import org.mockito.ArgumentCaptor
29 import org.mockito.ArgumentMatchers.eq
30 import org.mockito.Captor
31 import org.mockito.Mock
32 import org.mockito.Mockito.verify
33 import org.mockito.MockitoAnnotations
34 
35 @SmallTest
36 @RunWith(AndroidJUnit4::class)
37 @TestableLooper.RunWithLooper
38 class ControlsEditingActivityTest : SysuiTestCase() {
39 
40     private companion object {
41         val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
42         val TEST_STRUCTURE: CharSequence = "TestStructure"
43         val TEST_APP: CharSequence = "TestApp"
44     }
45 
46     private val uiExecutor = FakeExecutor(FakeSystemClock())
47 
48     @Mock lateinit var controller: ControlsControllerImpl
49 
50     @Mock lateinit var userTracker: UserTracker
51 
52     @Mock lateinit var customIconCache: CustomIconCache
53 
54     private var latch: CountDownLatch = CountDownLatch(1)
55 
56     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
57     @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
58 
59     @Rule
60     @JvmField
61     var activityRule =
62         ActivityTestRule(
<lambda>null63             /* activityFactory= */ SingleActivityFactory {
64                 TestableControlsEditingActivity(
65                     uiExecutor,
66                     controller,
67                     userTracker,
68                     customIconCache,
69                     mockDispatcher,
70                     latch
71                 )
72             },
73             /* initialTouchMode= */ false,
74             /* launchActivity= */ false,
75         )
76 
77     @Before
setUpnull78     fun setUp() {
79         MockitoAnnotations.initMocks(this)
80     }
81 
82     @Test
testBackCallbackRegistrationAndUnregistrationnull83     fun testBackCallbackRegistrationAndUnregistration() {
84         launchActivity()
85         // 1. ensure that launching the activity results in it registering a callback
86         verify(mockDispatcher)
87             .registerOnBackInvokedCallback(
88                 eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
89                 captureCallback.capture()
90             )
91         activityRule.finishActivity()
92         latch.await() // ensure activity is finished
93         // 2. ensure that when the activity is finished, it unregisters the same callback
94         verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
95     }
96 
97     @Test
testAddControlsButton_visiblenull98     fun testAddControlsButton_visible() {
99         with(launchActivity()) {
100             val addControlsButton = requireViewById<Button>(R.id.addControls)
101             assertThat(addControlsButton.visibility).isEqualTo(View.VISIBLE)
102             assertThat(addControlsButton.isEnabled).isTrue()
103         }
104     }
105 
106     @Test
testNotLaunchFromFavoriting_saveButton_disablednull107     fun testNotLaunchFromFavoriting_saveButton_disabled() {
108         with(launchActivity(isFromFavoriting = false)) {
109             val saveButton = requireViewById<Button>(R.id.done)
110             assertThat(saveButton.isEnabled).isFalse()
111         }
112     }
113 
114     @Test
testLaunchFromFavoriting_saveButton_enablednull115     fun testLaunchFromFavoriting_saveButton_enabled() {
116         with(launchActivity(isFromFavoriting = true)) {
117             val saveButton = requireViewById<Button>(R.id.done)
118             assertThat(saveButton.isEnabled).isTrue()
119         }
120     }
121 
122     @Test
testNotFromFavoriting_addControlsPressed_launchesFavouritingnull123     fun testNotFromFavoriting_addControlsPressed_launchesFavouriting() {
124         with(launchActivity(isFromFavoriting = false)) {
125             val addControls = requireViewById<Button>(R.id.addControls)
126 
127             activityRule.runOnUiThread { addControls.performClick() }
128 
129             with(startActivityData!!.intent) {
130                 assertThat(component)
131                     .isEqualTo(ComponentName(context, ControlsFavoritingActivity::class.java))
132                 assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE))
133                     .isEqualTo(TEST_STRUCTURE)
134                 assertThat(
135                         getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
136                     )
137                     .isEqualTo(TEST_COMPONENT)
138                 assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_APP))
139                     .isEqualTo(TEST_APP)
140                 assertThat(getByteExtra(ControlsFavoritingActivity.EXTRA_SOURCE, -1))
141                     .isEqualTo(ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING)
142             }
143         }
144     }
145 
launchActivitynull146     private fun launchActivity(
147         componentName: ComponentName = TEST_COMPONENT,
148         structure: CharSequence = TEST_STRUCTURE,
149         isFromFavoriting: Boolean = false,
150         app: CharSequence = TEST_APP,
151     ): TestableControlsEditingActivity =
152         activityRule.launchActivity(
153             Intent().apply {
154                 putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, isFromFavoriting)
155                 putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, structure)
156                 putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
157                 putExtra(ControlsEditingActivity.EXTRA_APP, app)
158             }
159         )
160 
161     class TestableControlsEditingActivity(
162         executor: FakeExecutor,
163         controller: ControlsControllerImpl,
164         userTracker: UserTracker,
165         customIconCache: CustomIconCache,
166         private val mockDispatcher: OnBackInvokedDispatcher,
167         private val latch: CountDownLatch
168     ) :
169         ControlsEditingActivity(
170             executor,
171             controller,
172             userTracker,
173             customIconCache,
174         ) {
175 
176         var startActivityData: StartActivityData? = null
177             private set
178 
getOnBackInvokedDispatchernull179         override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
180             return mockDispatcher
181         }
182 
onStopnull183         override fun onStop() {
184             super.onStop()
185             // ensures that test runner thread does not proceed until ui thread is done
186             latch.countDown()
187         }
188 
startActivitynull189         override fun startActivity(intent: Intent) {
190             startActivityData = StartActivityData(intent, null)
191         }
192 
startActivitynull193         override fun startActivity(intent: Intent, options: Bundle?) {
194             startActivityData = StartActivityData(intent, options)
195         }
196     }
197 }
198