Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 218de6a5 authored by Jernej Virag's avatar Jernej Virag Committed by Android (Google) Code Review
Browse files

Merge "Add completion callbacks to registerContentObserverAsync" into main

parents 52b23317 868be97c
Loading
Loading
Loading
Loading
+82 −1
Original line number Diff line number Diff line
@@ -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,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>.
     *
@@ -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,
@@ -158,6 +198,7 @@ interface SettingsProxy {
     *
     * API corresponding to [registerContentObserver] for Java usage.
     */
    @AnyThread
    fun registerContentObserverAsync(
        name: String,
        notifyForDescendants: Boolean,
@@ -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>.
     *
@@ -207,6 +267,7 @@ interface SettingsProxy {
     *
     * API corresponding to [registerContentObserver] for Java usage.
     */
    @AnyThread
    fun registerContentObserverAsync(
        uri: Uri,
        notifyForDescendants: Boolean,
@@ -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>.
     *
@@ -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) }

+99 −60
Original line number Diff line number Diff line
@@ -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,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))
@@ -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,
@@ -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))
@@ -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,
@@ -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))
        }
@@ -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() {