Merge "Multiuser support in ClockRegistry" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
c38b9fdba4
@@ -13,11 +13,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.systemui.shared.clocks
|
package com.android.systemui.shared.clocks
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.app.UserSwitchObserver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.ContentObserver
|
import android.database.ContentObserver
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Handler
|
import android.os.UserHandle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.OpenForTesting
|
import androidx.annotation.OpenForTesting
|
||||||
@@ -29,17 +31,23 @@ import com.android.systemui.plugins.ClockProviderPlugin
|
|||||||
import com.android.systemui.plugins.ClockSettings
|
import com.android.systemui.plugins.ClockSettings
|
||||||
import com.android.systemui.plugins.PluginListener
|
import com.android.systemui.plugins.PluginListener
|
||||||
import com.android.systemui.plugins.PluginManager
|
import com.android.systemui.plugins.PluginManager
|
||||||
|
import com.android.systemui.util.Assert
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
private val TAG = ClockRegistry::class.simpleName
|
private val TAG = ClockRegistry::class.simpleName!!
|
||||||
private const val DEBUG = true
|
private const val DEBUG = true
|
||||||
|
|
||||||
/** ClockRegistry aggregates providers and plugins */
|
/** ClockRegistry aggregates providers and plugins */
|
||||||
open class ClockRegistry(
|
open class ClockRegistry(
|
||||||
val context: Context,
|
val context: Context,
|
||||||
val pluginManager: PluginManager,
|
val pluginManager: PluginManager,
|
||||||
val handler: Handler,
|
val scope: CoroutineScope,
|
||||||
|
val mainDispatcher: CoroutineDispatcher,
|
||||||
|
val bgDispatcher: CoroutineDispatcher,
|
||||||
val isEnabled: Boolean,
|
val isEnabled: Boolean,
|
||||||
userHandle: Int,
|
val handleAllUsers: Boolean,
|
||||||
defaultClockProvider: ClockProvider,
|
defaultClockProvider: ClockProvider,
|
||||||
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
|
val fallbackClockId: ClockId = DEFAULT_CLOCK_ID,
|
||||||
) {
|
) {
|
||||||
@@ -50,66 +58,132 @@ open class ClockRegistry(
|
|||||||
|
|
||||||
private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
|
private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
|
||||||
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
|
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
|
||||||
private val settingObserver = object : ContentObserver(handler) {
|
private val settingObserver =
|
||||||
override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) =
|
object : ContentObserver(null) {
|
||||||
clockChangeListeners.forEach { it.onClockChanged() }
|
override fun onChange(
|
||||||
}
|
selfChange: Boolean,
|
||||||
|
uris: Collection<Uri>,
|
||||||
|
flags: Int,
|
||||||
|
userId: Int
|
||||||
|
) {
|
||||||
|
scope.launch(bgDispatcher) { querySettings() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private val pluginListener = object : PluginListener<ClockProviderPlugin> {
|
private val pluginListener =
|
||||||
override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) =
|
object : PluginListener<ClockProviderPlugin> {
|
||||||
connectClocks(plugin)
|
override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) =
|
||||||
|
connectClocks(plugin)
|
||||||
|
|
||||||
override fun onPluginDisconnected(plugin: ClockProviderPlugin) =
|
override fun onPluginDisconnected(plugin: ClockProviderPlugin) =
|
||||||
disconnectClocks(plugin)
|
disconnectClocks(plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
open var settings: ClockSettings?
|
private val userSwitchObserver =
|
||||||
get() {
|
object : UserSwitchObserver() {
|
||||||
|
override fun onUserSwitchComplete(newUserId: Int) {
|
||||||
|
scope.launch(bgDispatcher) { querySettings() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(b/267372164): Migrate to flows
|
||||||
|
var settings: ClockSettings? = null
|
||||||
|
get() = field
|
||||||
|
protected set(value) {
|
||||||
|
if (field != value) {
|
||||||
|
field = value
|
||||||
|
scope.launch(mainDispatcher) { onClockChanged() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isRegistered: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
|
@OpenForTesting
|
||||||
|
open fun querySettings() {
|
||||||
|
assertNotMainThread()
|
||||||
|
val result =
|
||||||
try {
|
try {
|
||||||
val json = Settings.Secure.getString(
|
val json =
|
||||||
context.contentResolver,
|
if (handleAllUsers) {
|
||||||
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
|
Settings.Secure.getStringForUser(
|
||||||
)
|
context.contentResolver,
|
||||||
if (json == null || json.isEmpty()) {
|
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
|
||||||
return null
|
ActivityManager.getCurrentUser()
|
||||||
}
|
)
|
||||||
return ClockSettings.deserialize(json)
|
} else {
|
||||||
|
Settings.Secure.getString(
|
||||||
|
context.contentResolver,
|
||||||
|
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ClockSettings.deserialize(json)
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Log.e(TAG, "Failed to parse clock settings", ex)
|
Log.e(TAG, "Failed to parse clock settings", ex)
|
||||||
return null
|
null
|
||||||
}
|
}
|
||||||
}
|
settings = result
|
||||||
protected set(value) {
|
}
|
||||||
try {
|
|
||||||
val json = if (value != null) {
|
|
||||||
value._applied_timestamp = System.currentTimeMillis()
|
|
||||||
ClockSettings.serialize(value)
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@OpenForTesting
|
||||||
|
open fun applySettings(value: ClockSettings?) {
|
||||||
|
assertNotMainThread()
|
||||||
|
|
||||||
|
try {
|
||||||
|
value?._applied_timestamp = System.currentTimeMillis()
|
||||||
|
val json = ClockSettings.serialize(value)
|
||||||
|
|
||||||
|
if (handleAllUsers) {
|
||||||
|
Settings.Secure.putStringForUser(
|
||||||
|
context.contentResolver,
|
||||||
|
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
|
||||||
|
json,
|
||||||
|
ActivityManager.getCurrentUser()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
Settings.Secure.putString(
|
Settings.Secure.putString(
|
||||||
context.contentResolver,
|
context.contentResolver,
|
||||||
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json
|
Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE,
|
||||||
|
json
|
||||||
)
|
)
|
||||||
} catch (ex: Exception) {
|
|
||||||
Log.e(TAG, "Failed to set clock settings", ex)
|
|
||||||
}
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Log.e(TAG, "Failed to set clock settings", ex)
|
||||||
}
|
}
|
||||||
|
settings = value
|
||||||
|
}
|
||||||
|
|
||||||
private fun mutateSetting(mutator: (ClockSettings) -> Unit) {
|
@OpenForTesting
|
||||||
val settings = this.settings ?: ClockSettings()
|
protected open fun assertMainThread() {
|
||||||
mutator(settings)
|
Assert.isMainThread()
|
||||||
this.settings = settings
|
}
|
||||||
|
|
||||||
|
@OpenForTesting
|
||||||
|
protected open fun assertNotMainThread() {
|
||||||
|
Assert.isNotMainThread()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onClockChanged() {
|
||||||
|
assertMainThread()
|
||||||
|
clockChangeListeners.forEach { it.onClockChanged() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) {
|
||||||
|
scope.launch(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentClockId: ClockId
|
var currentClockId: ClockId
|
||||||
get() = settings?.clockId ?: fallbackClockId
|
get() = settings?.clockId ?: fallbackClockId
|
||||||
set(value) { mutateSetting { it.clockId = value } }
|
set(value) {
|
||||||
|
mutateSetting { it.copy(clockId = value) }
|
||||||
|
}
|
||||||
|
|
||||||
var seedColor: Int?
|
var seedColor: Int?
|
||||||
get() = settings?.seedColor
|
get() = settings?.seedColor
|
||||||
set(value) { mutateSetting { it.seedColor = value } }
|
set(value) {
|
||||||
|
mutateSetting { it.copy(seedColor = value) }
|
||||||
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
connectClocks(defaultClockProvider)
|
connectClocks(defaultClockProvider)
|
||||||
@@ -118,19 +192,51 @@ open class ClockRegistry(
|
|||||||
"$defaultClockProvider did not register clock at $DEFAULT_CLOCK_ID"
|
"$defaultClockProvider did not register clock at $DEFAULT_CLOCK_ID"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isEnabled) {
|
fun registerListeners() {
|
||||||
pluginManager.addPluginListener(
|
if (!isEnabled || isRegistered) {
|
||||||
pluginListener,
|
return
|
||||||
ClockProviderPlugin::class.java,
|
}
|
||||||
/*allowMultiple=*/ true
|
|
||||||
)
|
isRegistered = true
|
||||||
|
|
||||||
|
pluginManager.addPluginListener(
|
||||||
|
pluginListener,
|
||||||
|
ClockProviderPlugin::class.java,
|
||||||
|
/*allowMultiple=*/ true
|
||||||
|
)
|
||||||
|
|
||||||
|
scope.launch(bgDispatcher) { querySettings() }
|
||||||
|
if (handleAllUsers) {
|
||||||
context.contentResolver.registerContentObserver(
|
context.contentResolver.registerContentObserver(
|
||||||
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
|
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
|
||||||
/*notifyForDescendants=*/ false,
|
/*notifyForDescendants=*/ false,
|
||||||
settingObserver,
|
settingObserver,
|
||||||
userHandle
|
UserHandle.USER_ALL
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ActivityManager.getService().registerUserSwitchObserver(userSwitchObserver, TAG)
|
||||||
|
} else {
|
||||||
|
context.contentResolver.registerContentObserver(
|
||||||
|
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
|
||||||
|
/*notifyForDescendants=*/ false,
|
||||||
|
settingObserver
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unregisterListeners() {
|
||||||
|
if (!isRegistered) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isRegistered = false
|
||||||
|
|
||||||
|
pluginManager.removePluginListener(pluginListener)
|
||||||
|
context.contentResolver.unregisterContentObserver(settingObserver)
|
||||||
|
if (handleAllUsers) {
|
||||||
|
ActivityManager.getService().unregisterUserSwitchObserver(userSwitchObserver)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +263,7 @@ open class ClockRegistry(
|
|||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.i(TAG, "Current clock ($currentId) was connected")
|
Log.i(TAG, "Current clock ($currentId) was connected")
|
||||||
}
|
}
|
||||||
clockChangeListeners.forEach { it.onClockChanged() }
|
onClockChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -172,13 +278,12 @@ open class ClockRegistry(
|
|||||||
|
|
||||||
if (currentId == clock.clockId) {
|
if (currentId == clock.clockId) {
|
||||||
Log.w(TAG, "Current clock ($currentId) was disconnected")
|
Log.w(TAG, "Current clock ($currentId) was disconnected")
|
||||||
clockChangeListeners.forEach { it.onClockChanged() }
|
onClockChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OpenForTesting
|
fun getClocks(): List<ClockMetadata> {
|
||||||
open fun getClocks(): List<ClockMetadata> {
|
|
||||||
if (!isEnabled) {
|
if (!isEnabled) {
|
||||||
return listOf(availableClocks[DEFAULT_CLOCK_ID]!!.metadata)
|
return listOf(availableClocks[DEFAULT_CLOCK_ID]!!.metadata)
|
||||||
}
|
}
|
||||||
@@ -213,16 +318,16 @@ open class ClockRegistry(
|
|||||||
return createClock(DEFAULT_CLOCK_ID)!!
|
return createClock(DEFAULT_CLOCK_ID)!!
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createClock(clockId: ClockId): ClockController? {
|
private fun createClock(targetClockId: ClockId): ClockController? {
|
||||||
val settings = this.settings ?: ClockSettings()
|
var settings = this.settings ?: ClockSettings()
|
||||||
if (clockId != settings.clockId) {
|
if (targetClockId != settings.clockId) {
|
||||||
settings.clockId = clockId
|
settings = settings.copy(clockId = targetClockId)
|
||||||
}
|
}
|
||||||
return availableClocks[clockId]?.provider?.createClock(settings)
|
return availableClocks[targetClockId]?.provider?.createClock(settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class ClockInfo(
|
private data class ClockInfo(
|
||||||
val metadata: ClockMetadata,
|
val metadata: ClockMetadata,
|
||||||
val provider: ClockProvider
|
val provider: ClockProvider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ interface ClockProvider {
|
|||||||
/** Initializes and returns the target clock design */
|
/** Initializes and returns the target clock design */
|
||||||
@Deprecated("Use overload with ClockSettings")
|
@Deprecated("Use overload with ClockSettings")
|
||||||
fun createClock(id: ClockId): ClockController {
|
fun createClock(id: ClockId): ClockController {
|
||||||
return createClock(ClockSettings(id, null, null))
|
return createClock(ClockSettings(id, null))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Initializes and returns the target clock design */
|
/** Initializes and returns the target clock design */
|
||||||
@@ -186,16 +186,21 @@ data class ClockMetadata(
|
|||||||
/** Structure for keeping clock-specific settings */
|
/** Structure for keeping clock-specific settings */
|
||||||
@Keep
|
@Keep
|
||||||
data class ClockSettings(
|
data class ClockSettings(
|
||||||
var clockId: ClockId? = null,
|
val clockId: ClockId? = null,
|
||||||
var seedColor: Int? = null,
|
val seedColor: Int? = null,
|
||||||
var _applied_timestamp: Long? = null,
|
|
||||||
) {
|
) {
|
||||||
|
var _applied_timestamp: Long? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val KEY_CLOCK_ID = "clockId"
|
private val KEY_CLOCK_ID = "clockId"
|
||||||
private val KEY_SEED_COLOR = "seedColor"
|
private val KEY_SEED_COLOR = "seedColor"
|
||||||
private val KEY_TIMESTAMP = "_applied_timestamp"
|
private val KEY_TIMESTAMP = "_applied_timestamp"
|
||||||
|
|
||||||
fun serialize(setting: ClockSettings): String {
|
fun serialize(setting: ClockSettings?): String {
|
||||||
|
if (setting == null) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
return JSONObject()
|
return JSONObject()
|
||||||
.put(KEY_CLOCK_ID, setting.clockId)
|
.put(KEY_CLOCK_ID, setting.clockId)
|
||||||
.put(KEY_SEED_COLOR, setting.seedColor)
|
.put(KEY_SEED_COLOR, setting.seedColor)
|
||||||
@@ -203,13 +208,21 @@ data class ClockSettings(
|
|||||||
.toString()
|
.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deserialize(jsonStr: String): ClockSettings {
|
fun deserialize(jsonStr: String?): ClockSettings? {
|
||||||
|
if (jsonStr.isNullOrEmpty()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
val json = JSONObject(jsonStr)
|
val json = JSONObject(jsonStr)
|
||||||
return ClockSettings(
|
val result =
|
||||||
json.getString(KEY_CLOCK_ID),
|
ClockSettings(
|
||||||
if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null,
|
json.getString(KEY_CLOCK_ID),
|
||||||
if (!json.isNull(KEY_TIMESTAMP)) json.getLong(KEY_TIMESTAMP) else null
|
if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
|
||||||
)
|
)
|
||||||
|
if (!json.isNull(KEY_TIMESTAMP)) {
|
||||||
|
result._applied_timestamp = json.getLong(KEY_TIMESTAMP)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,12 @@ package com.android.keyguard.dagger;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
import com.android.systemui.R;
|
import com.android.systemui.R;
|
||||||
import com.android.systemui.dagger.SysUISingleton;
|
import com.android.systemui.dagger.SysUISingleton;
|
||||||
import com.android.systemui.dagger.qualifiers.Application;
|
import com.android.systemui.dagger.qualifiers.Application;
|
||||||
|
import com.android.systemui.dagger.qualifiers.Background;
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.flags.FeatureFlags;
|
import com.android.systemui.flags.FeatureFlags;
|
||||||
import com.android.systemui.flags.Flags;
|
import com.android.systemui.flags.Flags;
|
||||||
@@ -34,6 +33,8 @@ import com.android.systemui.shared.clocks.DefaultClockProvider;
|
|||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher;
|
||||||
|
import kotlinx.coroutines.CoroutineScope;
|
||||||
|
|
||||||
/** Dagger Module for clocks. */
|
/** Dagger Module for clocks. */
|
||||||
@Module
|
@Module
|
||||||
@@ -44,17 +45,23 @@ public abstract class ClockRegistryModule {
|
|||||||
public static ClockRegistry getClockRegistry(
|
public static ClockRegistry getClockRegistry(
|
||||||
@Application Context context,
|
@Application Context context,
|
||||||
PluginManager pluginManager,
|
PluginManager pluginManager,
|
||||||
@Main Handler handler,
|
@Application CoroutineScope scope,
|
||||||
|
@Main CoroutineDispatcher mainDispatcher,
|
||||||
|
@Background CoroutineDispatcher bgDispatcher,
|
||||||
FeatureFlags featureFlags,
|
FeatureFlags featureFlags,
|
||||||
@Main Resources resources,
|
@Main Resources resources,
|
||||||
LayoutInflater layoutInflater) {
|
LayoutInflater layoutInflater) {
|
||||||
return new ClockRegistry(
|
ClockRegistry registry = new ClockRegistry(
|
||||||
context,
|
context,
|
||||||
pluginManager,
|
pluginManager,
|
||||||
handler,
|
scope,
|
||||||
|
mainDispatcher,
|
||||||
|
bgDispatcher,
|
||||||
featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
|
featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
|
||||||
UserHandle.USER_ALL,
|
/* handleAllUsers= */ true,
|
||||||
new DefaultClockProvider(context, layoutInflater, resources),
|
new DefaultClockProvider(context, layoutInflater, resources),
|
||||||
context.getString(R.string.lockscreen_clock_id_fallback));
|
context.getString(R.string.lockscreen_clock_id_fallback));
|
||||||
|
registry.registerListeners();
|
||||||
|
return registry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ package com.android.systemui.shared.clocks
|
|||||||
import android.content.ContentResolver
|
import android.content.ContentResolver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Handler
|
|
||||||
import android.os.UserHandle
|
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import androidx.test.filters.SmallTest
|
import androidx.test.filters.SmallTest
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
@@ -34,6 +32,9 @@ import com.android.systemui.util.mockito.argumentCaptor
|
|||||||
import com.android.systemui.util.mockito.eq
|
import com.android.systemui.util.mockito.eq
|
||||||
import junit.framework.Assert.assertEquals
|
import junit.framework.Assert.assertEquals
|
||||||
import junit.framework.Assert.fail
|
import junit.framework.Assert.fail
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
|
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.TestScope
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
@@ -49,19 +50,19 @@ import org.mockito.junit.MockitoJUnit
|
|||||||
class ClockRegistryTest : SysuiTestCase() {
|
class ClockRegistryTest : SysuiTestCase() {
|
||||||
|
|
||||||
@JvmField @Rule val mockito = MockitoJUnit.rule()
|
@JvmField @Rule val mockito = MockitoJUnit.rule()
|
||||||
|
private lateinit var dispatcher: CoroutineDispatcher
|
||||||
|
private lateinit var scope: TestScope
|
||||||
|
|
||||||
@Mock private lateinit var mockContext: Context
|
@Mock private lateinit var mockContext: Context
|
||||||
@Mock private lateinit var mockPluginManager: PluginManager
|
@Mock private lateinit var mockPluginManager: PluginManager
|
||||||
@Mock private lateinit var mockClock: ClockController
|
@Mock private lateinit var mockClock: ClockController
|
||||||
@Mock private lateinit var mockDefaultClock: ClockController
|
@Mock private lateinit var mockDefaultClock: ClockController
|
||||||
@Mock private lateinit var mockThumbnail: Drawable
|
@Mock private lateinit var mockThumbnail: Drawable
|
||||||
@Mock private lateinit var mockHandler: Handler
|
|
||||||
@Mock private lateinit var mockContentResolver: ContentResolver
|
@Mock private lateinit var mockContentResolver: ContentResolver
|
||||||
private lateinit var fakeDefaultProvider: FakeClockPlugin
|
private lateinit var fakeDefaultProvider: FakeClockPlugin
|
||||||
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
|
private lateinit var pluginListener: PluginListener<ClockProviderPlugin>
|
||||||
private lateinit var registry: ClockRegistry
|
private lateinit var registry: ClockRegistry
|
||||||
|
|
||||||
private var settingValue: ClockSettings? = null
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private fun failFactory(): ClockController {
|
private fun failFactory(): ClockController {
|
||||||
fail("Unexpected call to createClock")
|
fail("Unexpected call to createClock")
|
||||||
@@ -99,6 +100,9 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
|
dispatcher = StandardTestDispatcher()
|
||||||
|
scope = TestScope(dispatcher)
|
||||||
|
|
||||||
fakeDefaultProvider = FakeClockPlugin()
|
fakeDefaultProvider = FakeClockPlugin()
|
||||||
.addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail })
|
.addClock(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME, { mockDefaultClock }, { mockThumbnail })
|
||||||
whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
|
whenever(mockContext.contentResolver).thenReturn(mockContentResolver)
|
||||||
@@ -107,15 +111,22 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
registry = object : ClockRegistry(
|
registry = object : ClockRegistry(
|
||||||
mockContext,
|
mockContext,
|
||||||
mockPluginManager,
|
mockPluginManager,
|
||||||
mockHandler,
|
scope = scope.backgroundScope,
|
||||||
|
mainDispatcher = dispatcher,
|
||||||
|
bgDispatcher = dispatcher,
|
||||||
isEnabled = true,
|
isEnabled = true,
|
||||||
userHandle = UserHandle.USER_ALL,
|
handleAllUsers = true,
|
||||||
defaultClockProvider = fakeDefaultProvider
|
defaultClockProvider = fakeDefaultProvider,
|
||||||
) {
|
) {
|
||||||
override var settings: ClockSettings?
|
override fun querySettings() { }
|
||||||
get() = settingValue
|
override fun applySettings(value: ClockSettings?) {
|
||||||
set(value) { settingValue = value }
|
settings = value
|
||||||
|
}
|
||||||
|
// Unit Test does not validate threading
|
||||||
|
override fun assertMainThread() {}
|
||||||
|
override fun assertNotMainThread() {}
|
||||||
}
|
}
|
||||||
|
registry.registerListeners()
|
||||||
|
|
||||||
verify(mockPluginManager)
|
verify(mockPluginManager)
|
||||||
.addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java), eq(true))
|
.addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java), eq(true))
|
||||||
@@ -187,16 +198,16 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
.addClock("clock_1", "clock 1")
|
.addClock("clock_1", "clock 1")
|
||||||
.addClock("clock_2", "clock 2")
|
.addClock("clock_2", "clock 2")
|
||||||
|
|
||||||
settingValue = ClockSettings("clock_3", null, null)
|
|
||||||
val plugin2 = FakeClockPlugin()
|
val plugin2 = FakeClockPlugin()
|
||||||
.addClock("clock_3", "clock 3", { mockClock })
|
.addClock("clock_3", "clock 3", { mockClock })
|
||||||
.addClock("clock_4", "clock 4")
|
.addClock("clock_4", "clock 4")
|
||||||
|
|
||||||
|
registry.applySettings(ClockSettings("clock_3", null))
|
||||||
pluginListener.onPluginConnected(plugin1, mockContext)
|
pluginListener.onPluginConnected(plugin1, mockContext)
|
||||||
pluginListener.onPluginConnected(plugin2, mockContext)
|
pluginListener.onPluginConnected(plugin2, mockContext)
|
||||||
|
|
||||||
val clock = registry.createCurrentClock()
|
val clock = registry.createCurrentClock()
|
||||||
assertEquals(clock, mockClock)
|
assertEquals(mockClock, clock)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -205,11 +216,11 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
.addClock("clock_1", "clock 1")
|
.addClock("clock_1", "clock 1")
|
||||||
.addClock("clock_2", "clock 2")
|
.addClock("clock_2", "clock 2")
|
||||||
|
|
||||||
settingValue = ClockSettings("clock_3", null, null)
|
|
||||||
val plugin2 = FakeClockPlugin()
|
val plugin2 = FakeClockPlugin()
|
||||||
.addClock("clock_3", "clock 3")
|
.addClock("clock_3", "clock 3")
|
||||||
.addClock("clock_4", "clock 4")
|
.addClock("clock_4", "clock 4")
|
||||||
|
|
||||||
|
registry.applySettings(ClockSettings("clock_3", null))
|
||||||
pluginListener.onPluginConnected(plugin1, mockContext)
|
pluginListener.onPluginConnected(plugin1, mockContext)
|
||||||
pluginListener.onPluginConnected(plugin2, mockContext)
|
pluginListener.onPluginConnected(plugin2, mockContext)
|
||||||
pluginListener.onPluginDisconnected(plugin2)
|
pluginListener.onPluginDisconnected(plugin2)
|
||||||
@@ -224,11 +235,11 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
.addClock("clock_1", "clock 1")
|
.addClock("clock_1", "clock 1")
|
||||||
.addClock("clock_2", "clock 2")
|
.addClock("clock_2", "clock 2")
|
||||||
|
|
||||||
settingValue = ClockSettings("clock_3", null, null)
|
|
||||||
val plugin2 = FakeClockPlugin()
|
val plugin2 = FakeClockPlugin()
|
||||||
.addClock("clock_3", "clock 3", { mockClock })
|
.addClock("clock_3", "clock 3", { mockClock })
|
||||||
.addClock("clock_4", "clock 4")
|
.addClock("clock_4", "clock 4")
|
||||||
|
|
||||||
|
registry.applySettings(ClockSettings("clock_3", null))
|
||||||
pluginListener.onPluginConnected(plugin1, mockContext)
|
pluginListener.onPluginConnected(plugin1, mockContext)
|
||||||
pluginListener.onPluginConnected(plugin2, mockContext)
|
pluginListener.onPluginConnected(plugin2, mockContext)
|
||||||
|
|
||||||
@@ -244,7 +255,7 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun jsonDeserialization_gotExpectedObject() {
|
fun jsonDeserialization_gotExpectedObject() {
|
||||||
val expected = ClockSettings("ID", null, 500)
|
val expected = ClockSettings("ID", null).apply { _applied_timestamp = 500 }
|
||||||
val actual = ClockSettings.deserialize("""{
|
val actual = ClockSettings.deserialize("""{
|
||||||
"clockId":"ID",
|
"clockId":"ID",
|
||||||
"_applied_timestamp":500
|
"_applied_timestamp":500
|
||||||
@@ -254,14 +265,14 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun jsonDeserialization_noTimestamp_gotExpectedObject() {
|
fun jsonDeserialization_noTimestamp_gotExpectedObject() {
|
||||||
val expected = ClockSettings("ID", null, null)
|
val expected = ClockSettings("ID", null)
|
||||||
val actual = ClockSettings.deserialize("{\"clockId\":\"ID\"}")
|
val actual = ClockSettings.deserialize("{\"clockId\":\"ID\"}")
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun jsonDeserialization_nullTimestamp_gotExpectedObject() {
|
fun jsonDeserialization_nullTimestamp_gotExpectedObject() {
|
||||||
val expected = ClockSettings("ID", null, null)
|
val expected = ClockSettings("ID", null)
|
||||||
val actual = ClockSettings.deserialize("""{
|
val actual = ClockSettings.deserialize("""{
|
||||||
"clockId":"ID",
|
"clockId":"ID",
|
||||||
"_applied_timestamp":null
|
"_applied_timestamp":null
|
||||||
@@ -271,7 +282,7 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test(expected = JSONException::class)
|
@Test(expected = JSONException::class)
|
||||||
fun jsonDeserialization_noId_threwException() {
|
fun jsonDeserialization_noId_threwException() {
|
||||||
val expected = ClockSettings("ID", null, 500)
|
val expected = ClockSettings(null, null).apply { _applied_timestamp = 500 }
|
||||||
val actual = ClockSettings.deserialize("{\"_applied_timestamp\":500}")
|
val actual = ClockSettings.deserialize("{\"_applied_timestamp\":500}")
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
@@ -279,14 +290,15 @@ class ClockRegistryTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun jsonSerialization_gotExpectedString() {
|
fun jsonSerialization_gotExpectedString() {
|
||||||
val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}"
|
val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}"
|
||||||
val actual = ClockSettings.serialize(ClockSettings("ID", null, 500))
|
val actual = ClockSettings.serialize(ClockSettings("ID", null)
|
||||||
|
.apply { _applied_timestamp = 500 })
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun jsonSerialization_noTimestamp_gotExpectedString() {
|
fun jsonSerialization_noTimestamp_gotExpectedString() {
|
||||||
val expected = "{\"clockId\":\"ID\"}"
|
val expected = "{\"clockId\":\"ID\"}"
|
||||||
val actual = ClockSettings.serialize(ClockSettings("ID", null, null))
|
val actual = ClockSettings.serialize(ClockSettings("ID", null))
|
||||||
assertEquals(expected, actual)
|
assertEquals(expected, actual)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user