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

Commit ef855558 authored by Devarshi Bhatt's avatar Devarshi Bhatt
Browse files

Convert SettingsProxy and UserSettingsProxy to Kotlin.

Conversion to Kotlin is the first step towards migrating
register/unregister content observer APIs to background thread.

Test: atest / manual flash
Bug: 330299944
Flag: NONE Java to Kotlin migration, flag not needed.
Change-Id: I793d4bd9ad3d6ac9e39c3e97b7e4edc5b794ba84
parent 9db036da
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -766,6 +766,7 @@ android_robolectric_test {
    ],
    static_libs: [
        "RoboTestLibraries",
        "mockito-kotlin2",
    ],
    libs: [
        "android.test.runner",
+385 −0

File changed and moved.

Preview size limit exceeded, changes collapsed.

+0 −283
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.util.settings;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.UserHandle;
import android.provider.Settings;

import com.android.app.tracing.TraceUtils;
import com.android.systemui.settings.UserTracker;

import kotlin.Unit;

/**
 * Used to interact with per-user Settings.Secure and Settings.System settings (but not
 * Settings.Global, since those do not vary per-user)
 * <p>
 * This interface can be implemented to give instance method (instead of static method) versions
 * of Settings.Secure and Settings.System. It can be injected into class constructors and then
 * faked or mocked as needed in tests.
 * <p>
 * You can ask for {@link SecureSettings} or {@link SystemSettings} to be injected as needed.
 * <p>
 * This class also provides {@link #registerContentObserver(String, ContentObserver)} methods,
 * normally found on {@link ContentResolver} instances, unifying setting related actions in one
 * place.
 */
public interface UserSettingsProxy extends SettingsProxy {

    /**
     * Returns that {@link UserTracker} this instance was constructed with.
     */
    UserTracker getUserTracker();

    /**
     * Returns the user id for the associated {@link ContentResolver}.
     */
    default int getUserId() {
        return getContentResolver().getUserId();
    }

    /**
     * Returns the actual current user handle when querying with the current user. Otherwise,
     * returns the passed in user id.
     */
    default int getRealUserHandle(int userHandle) {
        if (userHandle != UserHandle.USER_CURRENT) {
            return userHandle;
        }
        return getUserTracker().getUserId();
    }

    @Override
    default void registerContentObserver(Uri uri, ContentObserver settingsObserver) {
        registerContentObserverForUser(uri, settingsObserver, getUserId());
    }

    /**
     * Convenience wrapper around
     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)}.'
     */
    @Override
    default void registerContentObserver(Uri uri, boolean notifyForDescendants,
            ContentObserver settingsObserver) {
        registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, getUserId());
    }

    /**
     * Convenience wrapper around
     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
     *
     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
     */
    default void registerContentObserverForUser(
            String name, ContentObserver settingsObserver, int userHandle) {
        registerContentObserverForUser(
                getUriFor(name), settingsObserver, userHandle);
    }

    /**
     * Convenience wrapper around
     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
     */
    default void registerContentObserverForUser(
            Uri uri, ContentObserver settingsObserver, int userHandle) {
        registerContentObserverForUser(
                uri, false, settingsObserver, userHandle);
    }

    /**
     * Convenience wrapper around
     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
     *
     * Implicitly calls {@link #getUriFor(String)} on the passed in name.
     */
    default void registerContentObserverForUser(
            String name, boolean notifyForDescendants, ContentObserver settingsObserver,
            int userHandle) {
        registerContentObserverForUser(
                getUriFor(name), notifyForDescendants, settingsObserver, userHandle);
    }

    /**
     * Convenience wrapper around
     * {@link ContentResolver#registerContentObserver(Uri, boolean, ContentObserver, int)}
     */
    default void registerContentObserverForUser(
            Uri uri, boolean notifyForDescendants, ContentObserver settingsObserver,
            int userHandle) {
        TraceUtils.trace(
                () -> {
                    // The limit for trace tags length is 127 chars, which leaves us 90 for Uri.
                    return "USP#registerObserver#[" + uri.toString() + "]";
                }, () -> {
                    getContentResolver().registerContentObserver(
                            uri, notifyForDescendants, settingsObserver,
                            getRealUserHandle(userHandle));
                    return Unit.INSTANCE;
                });
    }

    /**
     * Look up a name in the database.
     * @param name to look up in the table
     * @return the corresponding value, or null if not present
     */
    @Override
    default String getString(String name) {
        return getStringForUser(name, getUserId());
    }

    /**See {@link #getString(String)}. */
    String getStringForUser(String name, int userHandle);

    /**
     * Store a name/value pair into the database. Values written by this method will be
     * overridden if a restore happens in the future.
     *
     * @param name to store
     * @param value to associate with the name
     * @return true if the value was set, false on database errors
     */
    boolean putString(String name, String value, boolean overrideableByRestore);

    @Override
    default boolean putString(String name, String value) {
        return putStringForUser(name, value, getUserId());
    }

    /** See {@link #putString(String, String)}. */
    boolean putStringForUser(String name, String value, int userHandle);

    /** See {@link #putString(String, String)}. */
    boolean putStringForUser(@NonNull String name, @Nullable String value, @Nullable String tag,
            boolean makeDefault, @UserIdInt int userHandle, boolean overrideableByRestore);

    @Override
    default int getInt(String name, int def) {
        return getIntForUser(name, def, getUserId());
    }

    /** See {@link #getInt(String, int)}. */
    default int getIntForUser(String name, int def, int userHandle) {
        String v = getStringForUser(name, userHandle);
        try {
            return v != null ? Integer.parseInt(v) : def;
        } catch (NumberFormatException e) {
            return def;
        }
    }

    @Override
    default int getInt(String name) throws Settings.SettingNotFoundException {
        return getIntForUser(name, getUserId());
    }

    /** See {@link #getInt(String)}. */
    default int getIntForUser(String name, int userHandle)
            throws Settings.SettingNotFoundException {
        String v = getStringForUser(name, userHandle);
        try {
            return Integer.parseInt(v);
        } catch (NumberFormatException e) {
            throw new Settings.SettingNotFoundException(name);
        }
    }

    @Override
    default boolean putInt(String name, int value) {
        return putIntForUser(name, value, getUserId());
    }

    /** See {@link #putInt(String, int)}. */
    default boolean putIntForUser(String name, int value, int userHandle) {
        return putStringForUser(name, Integer.toString(value), userHandle);
    }

    @Override
    default boolean getBool(String name, boolean def) {
        return getBoolForUser(name, def, getUserId());
    }

    /** See {@link #getBool(String, boolean)}. */
    default boolean getBoolForUser(String name, boolean def, int userHandle) {
        return getIntForUser(name, def ? 1 : 0, userHandle) != 0;
    }

    @Override
    default boolean getBool(String name) throws Settings.SettingNotFoundException {
        return getBoolForUser(name, getUserId());
    }

    /** See {@link #getBool(String)}. */
    default boolean getBoolForUser(String name, int userHandle)
            throws Settings.SettingNotFoundException {
        return getIntForUser(name, userHandle) != 0;
    }

    @Override
    default boolean putBool(String name, boolean value) {
        return putBoolForUser(name, value, getUserId());
    }

    /** See {@link #putBool(String, boolean)}. */
    default boolean putBoolForUser(String name, boolean value, int userHandle) {
        return putIntForUser(name, value ? 1 : 0, userHandle);
    }

    /** See {@link #getLong(String, long)}. */
    default long getLongForUser(String name, long def, int userHandle) {
        String valString = getStringForUser(name, userHandle);
        return SettingsProxy.parseLongOrUseDefault(valString, def);
    }

    /** See {@link #getLong(String)}. */
    default long getLongForUser(String name, int userHandle)
            throws Settings.SettingNotFoundException {
        String valString = getStringForUser(name, userHandle);
        return SettingsProxy.parseLongOrThrow(name, valString);
    }

    /** See {@link #putLong(String, long)}. */
    default boolean putLongForUser(String name, long value, int userHandle) {
        return putStringForUser(name, Long.toString(value), userHandle);
    }

    /** See {@link #getFloat(String)}. */
    default float getFloatForUser(String name, float def, int userHandle) {
        String v = getStringForUser(name, userHandle);
        return SettingsProxy.parseFloat(v, def);
    }

    /** See {@link #getFloat(String, float)}. */
    default float getFloatForUser(String name, int userHandle)
            throws Settings.SettingNotFoundException {
        String v = getStringForUser(name, userHandle);
        return SettingsProxy.parseFloatOrThrow(name, v);
    }

    /** See {@link #putFloat(String, float)} */
    default boolean putFloatForUser(String name, float value, int userHandle) {
        return putStringForUser(name, Float.toString(value), userHandle);
    }
}
+269 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.systemui.util.settings

import android.annotation.UserIdInt
import android.database.ContentObserver
import android.net.Uri
import android.os.UserHandle
import android.provider.Settings.SettingNotFoundException
import com.android.app.tracing.TraceUtils.trace
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloat
import com.android.systemui.util.settings.SettingsProxy.Companion.parseFloatOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrThrow
import com.android.systemui.util.settings.SettingsProxy.Companion.parseLongOrUseDefault

/**
 * Used to interact with per-user Settings.Secure and Settings.System settings (but not
 * Settings.Global, since those do not vary per-user)
 *
 * This interface can be implemented to give instance method (instead of static method) versions of
 * Settings.Secure and Settings.System. It can be injected into class constructors and then faked or
 * mocked as needed in tests.
 *
 * You can ask for [SecureSettings] or [SystemSettings] to be injected as needed.
 *
 * This class also provides [.registerContentObserver] methods, normally found on [ContentResolver]
 * instances, unifying setting related actions in one place.
 */
interface UserSettingsProxy : SettingsProxy {

    /** Returns that [UserTracker] this instance was constructed with. */
    val userTracker: UserTracker

    /** Returns the user id for the associated [ContentResolver]. */
    var userId: Int
        get() = getContentResolver().userId
        set(_) {
            throw UnsupportedOperationException(
                "userId cannot be set in interface, use setter from an implementation instead."
            )
        }

    /**
     * Returns the actual current user handle when querying with the current user. Otherwise,
     * returns the passed in user id.
     */
    fun getRealUserHandle(userHandle: Int): Int {
        return if (userHandle != UserHandle.USER_CURRENT) {
            userHandle
        } else userTracker.userId
    }

    override fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
        registerContentObserverForUser(uri, settingsObserver, userId)
    }

    /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
    override fun registerContentObserver(
        uri: Uri,
        notifyForDescendants: Boolean,
        settingsObserver: ContentObserver
    ) {
        registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, userId)
    }

    /**
     * Convenience wrapper around [ContentResolver.registerContentObserver]
     *
     * Implicitly calls [getUriFor] on the passed in name.
     */
    fun registerContentObserverForUser(
        name: String,
        settingsObserver: ContentObserver,
        userHandle: Int
    ) {
        registerContentObserverForUser(getUriFor(name), settingsObserver, userHandle)
    }

    /** Convenience wrapper around [ContentResolver.registerContentObserver] */
    fun registerContentObserverForUser(
        uri: Uri,
        settingsObserver: ContentObserver,
        userHandle: Int
    ) {
        registerContentObserverForUser(uri, false, settingsObserver, userHandle)
    }

    /**
     * Convenience wrapper around [ContentResolver.registerContentObserver]
     *
     * Implicitly calls [getUriFor] on the passed in name.
     */
    fun registerContentObserverForUser(
        name: String,
        notifyForDescendants: Boolean,
        settingsObserver: ContentObserver,
        userHandle: Int
    ) {
        registerContentObserverForUser(
            getUriFor(name),
            notifyForDescendants,
            settingsObserver,
            userHandle
        )
    }

    /** Convenience wrapper around [ContentResolver.registerContentObserver] */
    fun registerContentObserverForUser(
        uri: Uri,
        notifyForDescendants: Boolean,
        settingsObserver: ContentObserver,
        userHandle: Int
    ) {
        trace({ "USP#registerObserver#[$uri]" }) {
            getContentResolver()
                .registerContentObserver(
                    uri,
                    notifyForDescendants,
                    settingsObserver,
                    getRealUserHandle(userHandle)
                )
            Unit
        }
    }

    /**
     * Look up a name in the database.
     *
     * @param name to look up in the table
     * @return the corresponding value, or null if not present
     */
    override fun getString(name: String): String {
        return getStringForUser(name, userId)
    }

    /** See [getString]. */
    fun getStringForUser(name: String, userHandle: Int): String

    /**
     * Store a name/value pair into the database. Values written by this method will be overridden
     * if a restore happens in the future.
     *
     * @param name to store
     * @param value to associate with the name
     * @return true if the value was set, false on database errors
     */
    fun putString(name: String, value: String, overrideableByRestore: Boolean): Boolean
    override fun putString(name: String, value: String): Boolean {
        return putStringForUser(name, value, userId)
    }

    /** Similar implementation to [putString] for the specified [userHandle]. */
    fun putStringForUser(name: String, value: String, userHandle: Int): Boolean

    /** Similar implementation to [putString] for the specified [userHandle]. */
    fun putStringForUser(
        name: String,
        value: String,
        tag: String?,
        makeDefault: Boolean,
        @UserIdInt userHandle: Int,
        overrideableByRestore: Boolean
    ): Boolean

    override fun getInt(name: String, def: Int): Int {
        return getIntForUser(name, def, userId)
    }

    /** Similar implementation to [getInt] for the specified [userHandle]. */
    fun getIntForUser(name: String, def: Int, userHandle: Int): Int {
        val v = getStringForUser(name, userHandle)
        return try {
            v.toInt()
        } catch (e: NumberFormatException) {
            def
        }
    }

    @Throws(SettingNotFoundException::class)
    override fun getInt(name: String) = getIntForUser(name, userId)

    /** Similar implementation to [getInt] for the specified [userHandle]. */
    @Throws(SettingNotFoundException::class)
    fun getIntForUser(name: String, userHandle: Int): Int {
        val v = getStringForUser(name, userHandle)
        return try {
            v.toInt()
        } catch (e: NumberFormatException) {
            throw SettingNotFoundException(name)
        }
    }

    override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)

    /** Similar implementation to [getInt] for the specified [userHandle]. */
    fun putIntForUser(name: String, value: Int, userHandle: Int) =
        putStringForUser(name, value.toString(), userHandle)

    override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)

    /** Similar implementation to [getBool] for the specified [userHandle]. */
    fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
        getIntForUser(name, if (def) 1 else 0, userHandle) != 0

    @Throws(SettingNotFoundException::class)
    override fun getBool(name: String) = getBoolForUser(name, userId)

    /** Similar implementation to [getBool] for the specified [userHandle]. */
    @Throws(SettingNotFoundException::class)
    fun getBoolForUser(name: String, userHandle: Int): Boolean {
        return getIntForUser(name, userHandle) != 0
    }

    override fun putBool(name: String, value: Boolean): Boolean {
        return putBoolForUser(name, value, userId)
    }

    /** Similar implementation to [putBool] for the specified [userHandle]. */
    fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
        putIntForUser(name, if (value) 1 else 0, userHandle)

    /** Similar implementation to [getLong] for the specified [userHandle]. */
    fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
        val valString = getStringForUser(name, userHandle)
        return parseLongOrUseDefault(valString, def)
    }

    /** Similar implementation to [getLong] for the specified [userHandle]. */
    @Throws(SettingNotFoundException::class)
    fun getLongForUser(name: String, userHandle: Int): Long {
        val valString = getStringForUser(name, userHandle)
        return parseLongOrThrow(name, valString)
    }

    /** Similar implementation to [putLong] for the specified [userHandle]. */
    fun putLongForUser(name: String, value: Long, userHandle: Int) =
        putStringForUser(name, value.toString(), userHandle)

    /** Similar implementation to [getFloat] for the specified [userHandle]. */
    fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
        val v = getStringForUser(name, userHandle)
        return parseFloat(v, def)
    }

    /** Similar implementation to [getFloat] for the specified [userHandle]. */
    @Throws(SettingNotFoundException::class)
    fun getFloatForUser(name: String, userHandle: Int): Float {
        val v = getStringForUser(name, userHandle)
        return parseFloatOrThrow(name, v)
    }

    /** Similar implementation to [putFloat] for the specified [userHandle]. */
    fun putFloatForUser(name: String, value: Float, userHandle: Int) =
        putStringForUser(name, value.toString(), userHandle)
}
+1 −1
Original line number Diff line number Diff line
@@ -36,12 +36,12 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.kotlin.any

private const val USER_ID = 8

Loading