Merge "Add support for flag change listeners library." into sc-v2-dev
This commit is contained in:
@@ -83,8 +83,7 @@ android_library {
|
|||||||
"src/**/*.kt",
|
"src/**/*.kt",
|
||||||
"src/**/*.java",
|
"src/**/*.java",
|
||||||
"src/**/I*.aidl",
|
"src/**/I*.aidl",
|
||||||
"src-release/**/*.kt",
|
":ReleaseJavaFiles",
|
||||||
"src-release/**/*.java",
|
|
||||||
],
|
],
|
||||||
product_variables: {
|
product_variables: {
|
||||||
debuggable: {
|
debuggable: {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
# SystemUI Plugins
|
# SystemUI Plugins
|
||||||
|
|
||||||
Plugins provide an easy way to rapidly prototype SystemUI features. Plugins are APKs that will be installable only on Build.IS_DEBUGGABLE (dogfood) builds, that can change the behavior of SystemUI at runtime. This is done by creating a basic set of interfaces that the plugins can expect to be in SysUI, then the portion of code controlled by the interface can be iterated on faster than currently.
|
Plugins provide an easy way to rapidly prototype SystemUI features. Plugins are APKs that will be installable only on Build.IS_DEBUGGABLE (dogfood) builds, that can change the behavior of SystemUI at runtime. This is done by creating a basic set of interfaces that the plugins can expect to be in SysUI, then the portion of code controlled by the interface can be iterated on faster than currently.
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ java_library {
|
|||||||
static_kotlin_stdlib: false,
|
static_kotlin_stdlib: false,
|
||||||
libs: [
|
libs: [
|
||||||
"androidx.concurrent_concurrent-futures",
|
"androidx.concurrent_concurrent-futures",
|
||||||
"SystemUI-flags",
|
|
||||||
],
|
],
|
||||||
static_libs: [
|
static_libs: [
|
||||||
"SystemUI-flag-types",
|
"SystemUI-flag-types",
|
||||||
|
"SystemUI-flags",
|
||||||
],
|
],
|
||||||
java_version: "1.8",
|
java_version: "1.8",
|
||||||
min_sdk_version: "current",
|
min_sdk_version: "current",
|
||||||
|
|||||||
@@ -18,13 +18,19 @@ package com.android.systemui.flags
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.database.ContentObserver
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Handler
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.concurrent.futures.CallbackToFutureAdapter
|
import androidx.concurrent.futures.CallbackToFutureAdapter
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
|
|
||||||
class FlagManager constructor(val context: Context) : FlagReader {
|
class FlagManager constructor(
|
||||||
|
private val context: Context,
|
||||||
|
private val handler: Handler
|
||||||
|
) : FlagReader {
|
||||||
companion object {
|
companion object {
|
||||||
const val RECEIVING_PACKAGE = "com.android.systemui"
|
const val RECEIVING_PACKAGE = "com.android.systemui"
|
||||||
const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
|
const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
|
||||||
@@ -36,6 +42,9 @@ class FlagManager constructor(val context: Context) : FlagReader {
|
|||||||
private const val SETTINGS_PREFIX = "systemui/flags"
|
private const val SETTINGS_PREFIX = "systemui/flags"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val listeners: MutableSet<FlagReader.Listener> = mutableSetOf()
|
||||||
|
private val settingsObserver: ContentObserver = SettingsObserver()
|
||||||
|
|
||||||
fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
|
fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
|
||||||
val knownFlagMap = Flags.collectFlags()
|
val knownFlagMap = Flags.collectFlags()
|
||||||
// Possible todo in the future: query systemui async to actually get the known flag ids.
|
// Possible todo in the future: query systemui async to actually get the known flag ids.
|
||||||
@@ -82,6 +91,27 @@ class FlagManager constructor(val context: Context) : FlagReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun addListener(listener: FlagReader.Listener) {
|
||||||
|
synchronized(listeners) {
|
||||||
|
val registerNeeded = listeners.isEmpty()
|
||||||
|
listeners.add(listener)
|
||||||
|
if (registerNeeded) {
|
||||||
|
context.contentResolver.registerContentObserver(
|
||||||
|
Settings.Secure.getUriFor(SETTINGS_PREFIX), true, settingsObserver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun removeListener(listener: FlagReader.Listener) {
|
||||||
|
synchronized(listeners) {
|
||||||
|
val isRegistered = !listeners.isEmpty()
|
||||||
|
listeners.remove(listener)
|
||||||
|
if (isRegistered && listeners.isEmpty()) {
|
||||||
|
context.contentResolver.unregisterContentObserver(settingsObserver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createIntent(id: Int): Intent {
|
private fun createIntent(id: Int): Intent {
|
||||||
val intent = Intent(ACTION_SET_FLAG)
|
val intent = Intent(ACTION_SET_FLAG)
|
||||||
intent.setPackage(RECEIVING_PACKAGE)
|
intent.setPackage(RECEIVING_PACKAGE)
|
||||||
@@ -90,7 +120,7 @@ class FlagManager constructor(val context: Context) : FlagReader {
|
|||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
fun keyToSettingsPrefix(key: Int): String? {
|
fun keyToSettingsPrefix(key: Int): String {
|
||||||
return SETTINGS_PREFIX + "/" + key
|
return SETTINGS_PREFIX + "/" + key
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +131,22 @@ class FlagManager constructor(val context: Context) : FlagReader {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner class SettingsObserver : ContentObserver(handler) {
|
||||||
|
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||||
|
if (uri == null) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val parts = uri.pathSegments
|
||||||
|
val idStr = parts[parts.size - 1]
|
||||||
|
try {
|
||||||
|
val id = idStr.toInt()
|
||||||
|
listeners.forEach { l -> l.onFlagChanged(id) }
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InvalidFlagStorageException : Exception("Data found but is invalid")
|
class InvalidFlagStorageException : Exception("Data found but is invalid")
|
||||||
@@ -31,7 +31,7 @@ interface FlagReader {
|
|||||||
fun removeListener(listener: Listener) {}
|
fun removeListener(listener: Listener) {}
|
||||||
|
|
||||||
/** A simple listener to be alerted when a flag changes. */
|
/** A simple listener to be alerted when a flag changes. */
|
||||||
interface Listener {
|
fun interface Listener {
|
||||||
/** */
|
/** */
|
||||||
fun onFlagChanged(id: Int)
|
fun onFlagChanged(id: Int)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,10 +129,14 @@ public class FeatureFlagManager implements FlagReader, FlagWriter, Dumpable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(Listener run) {}
|
public void addListener(Listener run) {
|
||||||
|
mFlagManager.addListener(run);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeListener(Listener run) {}
|
public void removeListener(Listener run) {
|
||||||
|
mFlagManager.removeListener(run);
|
||||||
|
}
|
||||||
|
|
||||||
private void restartSystemUI() {
|
private void restartSystemUI() {
|
||||||
Log.i(TAG, "Restarting SystemUI");
|
Log.i(TAG, "Restarting SystemUI");
|
||||||
|
|||||||
@@ -17,14 +17,19 @@
|
|||||||
package com.android.systemui.flags
|
package com.android.systemui.flags
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.os.Handler
|
||||||
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
|
import com.android.systemui.util.settings.SettingsUtilModule
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
|
|
||||||
@Module
|
@Module(includes = [
|
||||||
|
SettingsUtilModule::class
|
||||||
|
])
|
||||||
object FlagsModule {
|
object FlagsModule {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@Provides
|
@Provides
|
||||||
fun provideFlagManager(context: Context): FlagManager {
|
fun provideFlagManager(context: Context, @Main handler: Handler): FlagManager {
|
||||||
return FlagManager(context)
|
return FlagManager(context, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user