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