Loading packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +82 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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>. Loading Loading @@ -89,11 +90,30 @@ 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>. * Loading @@ -120,16 +140,36 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ @AnyThread fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = CoroutineScope(backgroundDispatcher).launch { registerContentObserverSync(uri, 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( 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, Loading Loading @@ -158,6 +198,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ @AnyThread fun registerContentObserverAsync( name: String, notifyForDescendants: Boolean, Loading @@ -167,6 +208,25 @@ interface SettingsProxy { registerContentObserverSync(getUriFor(name), notifyForDescendants, 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, 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>. * Loading Loading @@ -207,6 +267,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ @AnyThread fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, Loading @@ -216,6 +277,25 @@ interface SettingsProxy { registerContentObserverSync(uri, notifyForDescendants, 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( 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>. * Loading Loading @@ -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) } Loading packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt +99 −60 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -60,7 +62,8 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserver_inputString_success() { fun registerContentObserver_inputString_success() = testScope.runTest { mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver) verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) Loading @@ -75,16 +78,17 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputString_success() { fun registerContentObserverAsync_inputString_success() = testScope.runTest { mSettings.registerContentObserverAsync(TEST_SETTING, mContentObserver) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) } } @Test fun registerContentObserver_inputString_notifyForDescendants_true() { fun registerContentObserver_inputString_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverSync( TEST_SETTING, notifyForDescendants = true, Loading @@ -107,20 +111,21 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputString_notifyForDescendants_true() { fun registerContentObserverAsync_inputString_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverAsync( TEST_SETTING, notifyForDescendants = true, mContentObserver ) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) } } @Test fun registerContentObserver_inputUri_success() { fun registerContentObserver_inputUri_success() = testScope.runTest { mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver) verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) Loading @@ -135,16 +140,17 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputUri_success() { fun registerContentObserverAsync_inputUri_success() = testScope.runTest { mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) } } @Test fun registerContentObserver_inputUri_notifyForDescendants_true() { fun registerContentObserver_inputUri_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverSync( TEST_SETTING_URI, notifyForDescendants = true, Loading @@ -167,20 +173,53 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputUri_notifyForDescendants_true() { fun registerContentObserverAsync_inputUri_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverAsync( TEST_SETTING_URI, notifyForDescendants = true, mContentObserver ) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) } @Test 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() { fun unregisterContentObserverSync() = testScope.runTest { mSettings.unregisterContentObserverSync(mContentObserver) verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) } Loading @@ -193,12 +232,12 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun unregisterContentObserverAsync_inputString_success() { fun unregisterContentObserverAsync_inputString_success() = testScope.runTest { mSettings.unregisterContentObserverAsync(mContentObserver) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) } } @Test fun getString_keyPresent_returnValidValue() { Loading Loading
packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt +82 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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>. Loading Loading @@ -89,11 +90,30 @@ 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>. * Loading @@ -120,16 +140,36 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ @AnyThread fun registerContentObserverAsync(uri: Uri, settingsObserver: ContentObserver) = CoroutineScope(backgroundDispatcher).launch { registerContentObserverSync(uri, 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( 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, Loading Loading @@ -158,6 +198,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ @AnyThread fun registerContentObserverAsync( name: String, notifyForDescendants: Boolean, Loading @@ -167,6 +208,25 @@ interface SettingsProxy { registerContentObserverSync(getUriFor(name), notifyForDescendants, 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, 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>. * Loading Loading @@ -207,6 +267,7 @@ interface SettingsProxy { * * API corresponding to [registerContentObserver] for Java usage. */ @AnyThread fun registerContentObserverAsync( uri: Uri, notifyForDescendants: Boolean, Loading @@ -216,6 +277,25 @@ interface SettingsProxy { registerContentObserverSync(uri, notifyForDescendants, 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( 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>. * Loading Loading @@ -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) } Loading
packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt +99 −60 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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() Loading @@ -60,7 +62,8 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserver_inputString_success() { fun registerContentObserver_inputString_success() = testScope.runTest { mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver) verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) Loading @@ -75,16 +78,17 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputString_success() { fun registerContentObserverAsync_inputString_success() = testScope.runTest { mSettings.registerContentObserverAsync(TEST_SETTING, mContentObserver) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) } } @Test fun registerContentObserver_inputString_notifyForDescendants_true() { fun registerContentObserver_inputString_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverSync( TEST_SETTING, notifyForDescendants = true, Loading @@ -107,20 +111,21 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputString_notifyForDescendants_true() { fun registerContentObserverAsync_inputString_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverAsync( TEST_SETTING, notifyForDescendants = true, mContentObserver ) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) } } @Test fun registerContentObserver_inputUri_success() { fun registerContentObserver_inputUri_success() = testScope.runTest { mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver) verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) Loading @@ -135,16 +140,17 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputUri_success() { fun registerContentObserverAsync_inputUri_success() = testScope.runTest { mSettings.registerContentObserverAsync(TEST_SETTING_URI, mContentObserver) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver)) } } @Test fun registerContentObserver_inputUri_notifyForDescendants_true() { fun registerContentObserver_inputUri_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverSync( TEST_SETTING_URI, notifyForDescendants = true, Loading @@ -167,20 +173,53 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun registerContentObserverAsync_inputUri_notifyForDescendants_true() { fun registerContentObserverAsync_inputUri_notifyForDescendants_true() = testScope.runTest { mSettings.registerContentObserverAsync( TEST_SETTING_URI, notifyForDescendants = true, mContentObserver ) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()) .registerContentObserver(eq(TEST_SETTING_URI), eq(true), eq(mContentObserver)) } @Test 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() { fun unregisterContentObserverSync() = testScope.runTest { mSettings.unregisterContentObserverSync(mContentObserver) verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) } Loading @@ -193,12 +232,12 @@ class SettingsProxyTest : SysuiTestCase() { } @Test fun unregisterContentObserverAsync_inputString_success() { fun unregisterContentObserverAsync_inputString_success() = testScope.runTest { mSettings.unregisterContentObserverAsync(mContentObserver) testScope.launch { testScope.advanceUntilIdle() verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver)) } } @Test fun getString_keyPresent_returnValidValue() { Loading