diff options
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt | 83 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt | 159 |
2 files changed, 181 insertions, 61 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt index fe540445793e..b5934ec680d3 100644 --- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt @@ -19,6 +19,7 @@ import android.content.ContentResolver import android.database.ContentObserver import android.net.Uri import android.provider.Settings.SettingNotFoundException +import androidx.annotation.AnyThread import androidx.annotation.WorkerThread import com.android.app.tracing.TraceUtils.trace import kotlinx.coroutines.CoroutineDispatcher @@ -57,7 +58,7 @@ interface SettingsProxy { * @param name to look up in the table * @return the corresponding content URI, or null if not present */ - fun getUriFor(name: String): Uri + @AnyThread fun getUriFor(name: String): Uri /** * Registers listener for a given content observer <b>while blocking the current thread</b>. @@ -89,12 +90,31 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ + @AnyThread fun registerContentObserverAsync(name: String, settingsObserver: ContentObserver) = CoroutineScope(backgroundDispatcher).launch { registerContentObserverSync(getUriFor(name), settingsObserver) } /** + * Convenience wrapper around [ContentResolver.registerContentObserver].' + * + * API corresponding to [registerContentObserver] for Java usage. After registration is + * complete, the callback block is called on the <b>background thread</b> to allow for update of + * value. + */ + @AnyThread + fun registerContentObserverAsync( + name: String, + settingsObserver: ContentObserver, + @WorkerThread registered: Runnable + ) = + CoroutineScope(backgroundDispatcher).launch { + registerContentObserverSync(getUriFor(name), settingsObserver) + registered.run() + } + + /** * Registers listener for a given content observer <b>while blocking the current thread</b>. * * This should not be called from the main thread, use [registerContentObserver] or @@ -120,6 +140,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ + @AnyThread fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = CoroutineScope(backgroundDispatcher).launch { registerContentObserverSync(uri, settingsObserver) @@ -128,8 +149,27 @@ interface SettingsProxy { /** * Convenience wrapper around [ContentResolver.registerContentObserver].' * + * API corresponding to [registerContentObserver] for Java usage. After registration is + * complete, the callback block is called on the <b>background thread</b> to allow for update of + * value. + */ + @AnyThread + fun registerContentObserverAsync( + uri: Uri, + settingsObserver: ContentObserver, + @WorkerThread registered: Runnable + ) = + CoroutineScope(backgroundDispatcher).launch { + registerContentObserverSync(uri, settingsObserver) + registered.run() + } + + /** + * Convenience wrapper around [ContentResolver.registerContentObserver].' + * * Implicitly calls [getUriFor] on the passed in name. */ + @WorkerThread fun registerContentObserverSync( name: String, notifyForDescendants: Boolean, @@ -158,6 +198,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ + @AnyThread fun registerContentObserverAsync( name: String, notifyForDescendants: Boolean, @@ -168,6 +209,25 @@ interface SettingsProxy { } /** + * Convenience wrapper around [ContentResolver.registerContentObserver].' + * + * API corresponding to [registerContentObserver] for Java usage. After registration is + * complete, the callback block is called on the <b>background thread</b> to allow for update of + * value. + */ + @AnyThread + fun registerContentObserverAsync( + name: String, + notifyForDescendants: Boolean, + settingsObserver: ContentObserver, + @WorkerThread registered: Runnable + ) = + CoroutineScope(backgroundDispatcher).launch { + registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver) + registered.run() + } + + /** * Registers listener for a given content observer <b>while blocking the current thread</b>. * * This should not be called from the main thread, use [registerContentObserver] or @@ -207,6 +267,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ + @AnyThread fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, @@ -217,6 +278,25 @@ interface SettingsProxy { } /** + * Convenience wrapper around [ContentResolver.registerContentObserver].' + * + * API corresponding to [registerContentObserver] for Java usage. After registration is + * complete, the callback block is called on the <b>background thread</b> to allow for update of + * value. + */ + @AnyThread + fun registerContentObserverAsync( + uri: Uri, + notifyForDescendants: Boolean, + settingsObserver: ContentObserver, + @WorkerThread registered: Runnable + ) = + CoroutineScope(backgroundDispatcher).launch { + registerContentObserverSync(uri, notifyForDescendants, settingsObserver) + registered.run() + } + + /** * Unregisters the given content observer <b>while blocking the current thread</b>. * * This should not be called from the main thread, use [unregisterContentObserver] or @@ -246,6 +326,7 @@ interface SettingsProxy { * API corresponding to [unregisterContentObserver] for Java usage to ensure that * [ContentObserver] registration happens on a worker thread. */ + @AnyThread fun unregisterContentObserverAsync(settingsObserver: ContentObserver) = CoroutineScope(backgroundDispatcher).launch { unregisterContentObserver(settingsObserver) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt index dd791e764e01..5ac61102fa99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt @@ -28,9 +28,10 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.launch +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Assert.assertThrows import org.junit.Before @@ -44,6 +45,7 @@ import org.mockito.kotlin.eq @RunWith(AndroidJUnit4::class) @SmallTest @TestableLooper.RunWithLooper +@OptIn(ExperimentalCoroutinesApi::class) class SettingsProxyTest : SysuiTestCase() { private val testDispatcher = StandardTestDispatcher() @@ -60,11 +62,12 @@ class SettingsProxyTest : SysuiTestCase() { } @Test - fun registerContentObserver_inputString_success() { - mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver) - verify(mSettings.getContentResolver()) - .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) - } + fun registerContentObserver_inputString_success() = + testScope.runTest { + mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver) + verify(mSettings.getContentResolver()) + .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) + } @Test fun registerContentObserverSuspend_inputString_success() = @@ -75,24 +78,25 @@ class SettingsProxyTest : SysuiTestCase() { } @Test - fun registerContentObserverAsync_inputString_success() { - mSettings.registerContentObserverAsync(TEST_SETTING, mContentObserver) - testScope.launch { + fun registerContentObserverAsync_inputString_success() = + testScope.runTest { + mSettings.registerContentObserverAsync(TEST_SETTING, mContentObserver) + testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) } - } @Test - fun registerContentObserver_inputString_notifyForDescendants_true() { - mSettings.registerContentObserverSync( - TEST_SETTING, - notifyForDescendants = true, - mContentObserver - ) - verify(mSettings.getContentResolver()) - .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) - } + fun registerContentObserver_inputString_notifyForDescendants_true() = + testScope.runTest { + mSettings.registerContentObserverSync( + TEST_SETTING, + notifyForDescendants = true, + mContentObserver + ) + verify(mSettings.getContentResolver()) + .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) + } @Test fun registerContentObserverSuspend_inputString_notifyForDescendants_true() = @@ -107,24 +111,25 @@ class SettingsProxyTest : SysuiTestCase() { } @Test - fun registerContentObserverAsync_inputString_notifyForDescendants_true() { - mSettings.registerContentObserverAsync( - TEST_SETTING, - notifyForDescendants = true, - mContentObserver - ) - testScope.launch { + fun registerContentObserverAsync_inputString_notifyForDescendants_true() = + testScope.runTest { + mSettings.registerContentObserverAsync( + TEST_SETTING, + notifyForDescendants = true, + mContentObserver + ) + testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) } - } @Test - fun registerContentObserver_inputUri_success() { - mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver) - verify(mSettings.getContentResolver()) - .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) - } + fun registerContentObserver_inputUri_success() = + testScope.runTest { + mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver) + verify(mSettings.getContentResolver()) + .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) + } @Test fun registerContentObserverSuspend_inputUri_success() = @@ -135,24 +140,25 @@ class SettingsProxyTest : SysuiTestCase() { } @Test - fun registerContentObserverAsync_inputUri_success() { - mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver) - testScope.launch { + fun registerContentObserverAsync_inputUri_success() = + testScope.runTest { + mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver) + testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) } - } @Test - fun registerContentObserver_inputUri_notifyForDescendants_true() { - mSettings.registerContentObserverSync( - TEST_SETTING_URI, - notifyForDescendants = true, - mContentObserver - ) - verify(mSettings.getContentResolver()) - .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) - } + fun registerContentObserver_inputUri_notifyForDescendants_true() = + testScope.runTest { + mSettings.registerContentObserverSync( + TEST_SETTING_URI, + notifyForDescendants = true, + mContentObserver + ) + verify(mSettings.getContentResolver()) + .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) + } @Test fun registerContentObserverSuspend_inputUri_notifyForDescendants_true() = @@ -167,25 +173,58 @@ class SettingsProxyTest : SysuiTestCase() { } @Test - fun registerContentObserverAsync_inputUri_notifyForDescendants_true() { - mSettings.registerContentObserverAsync( - TEST_SETTING_URI, - notifyForDescendants = true, - mContentObserver - ) - testScope.launch { + fun registerContentObserverAsync_inputUri_notifyForDescendants_true() = + testScope.runTest { + mSettings.registerContentObserverAsync( + TEST_SETTING_URI, + notifyForDescendants = true, + mContentObserver + ) + testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) } - } @Test - fun unregisterContentObserverSync() { - mSettings.unregisterContentObserverSync(mContentObserver) - verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) + fun registerContentObserverAsync_registeredLambdaPassed_callsCallback() = + testScope.runTest { + verifyRegisteredCallbackForRegistration { + mSettings.registerContentObserverAsync(TEST_SETTING, mContentObserver, it) + } + verifyRegisteredCallbackForRegistration { + mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver, it) + } + verifyRegisteredCallbackForRegistration { + mSettings.registerContentObserverAsync(TEST_SETTING, false, mContentObserver, it) + } + verifyRegisteredCallbackForRegistration { + mSettings.registerContentObserverAsync( + TEST_SETTING_URI, + false, + mContentObserver, + it + ) + } + } + + private fun verifyRegisteredCallbackForRegistration( + call: (registeredRunnable: Runnable) -> Unit + ) { + var callbackCalled = false + val runnable = { callbackCalled = true } + call(runnable) + testScope.advanceUntilIdle() + assertThat(callbackCalled).isTrue() } @Test + fun unregisterContentObserverSync() = + testScope.runTest { + mSettings.unregisterContentObserverSync(mContentObserver) + verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) + } + + @Test fun unregisterContentObserverSuspend_inputString_success() = testScope.runTest { mSettings.unregisterContentObserver(mContentObserver) @@ -193,12 +232,12 @@ class SettingsProxyTest : SysuiTestCase() { } @Test - fun unregisterContentObserverAsync_inputString_success() { - mSettings.unregisterContentObserverAsync(mContentObserver) - testScope.launch { + fun unregisterContentObserverAsync_inputString_success() = + testScope.runTest { + mSettings.unregisterContentObserverAsync(mContentObserver) + testScope.advanceUntilIdle() verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) } - } @Test fun getString_keyPresent_returnValidValue() { |