Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +67 −2 Original line number Diff line number Diff line Loading @@ -48,6 +48,18 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext private val KEY_TIMESTAMP = "appliedTimestamp" private val KNOWN_PLUGINS = mapOf<String, List<ClockMetadata>>( "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")), "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")), "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")), "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")), "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")), "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")), "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")), "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")), "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")), ) private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut( key: TKey, Loading Loading @@ -127,8 +139,61 @@ open class ClockRegistry( private val pluginListener = object : PluginListener<ClockProviderPlugin> { override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) { manager.loadPlugin() override fun onPluginAttached( manager: PluginLifecycleManager<ClockProviderPlugin> ): Boolean { if (keepAllLoaded) { // Always load new plugins if requested return true } val knownClocks = KNOWN_PLUGINS.get(manager.getPackage()) if (knownClocks == null) { logBuffer.tryLog( TAG, LogLevel.WARNING, { str1 = manager.getPackage() }, { "Loading unrecognized clock package: $str1" } ) return true } logBuffer.tryLog( TAG, LogLevel.INFO, { str1 = manager.getPackage() }, { "Skipping initial load of known clock package package: $str1" } ) var isClockListChanged = false for (metadata in knownClocks) { val id = metadata.clockId val info = availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) { isClockListChanged = true onConnected(id) } if (manager != info.manager) { logBuffer.tryLog( TAG, LogLevel.ERROR, { str1 = id }, { "Clock Id conflict on known attach: $str1 is double registered" } ) continue } info.provider = null } if (isClockListChanged) { triggerOnAvailableClocksChanged() } verifyLoadedProviders() // Load executed via verifyLoadedProviders return false } override fun onPluginLoaded( Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +3 −1 Original line number Diff line number Diff line Loading @@ -190,7 +190,9 @@ enum class ClockTickRate(val value: Int) { data class ClockMetadata( val clockId: ClockId, val name: String, ) ) { constructor(clockId: ClockId) : this(clockId, clockId) {} } /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( Loading packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -16,12 +16,20 @@ package com.android.systemui.plugins; import android.content.ComponentName; /** * Provides the ability for consumers to control plugin lifecycle. * * @param <T> is the target plugin type */ public interface PluginLifecycleManager<T extends Plugin> { /** Returns the ComponentName of the target plugin. Maybe be called when not loaded. */ ComponentName getComponentName(); /** Returns the package name of the target plugin. May be called when not loaded. */ String getPackage(); /** Returns the currently loaded plugin instance (if plugin is loaded) */ T getPlugin(); Loading packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java +10 −5 Original line number Diff line number Diff line Loading @@ -60,13 +60,18 @@ public interface PluginListener<T extends Plugin> { /** * Called when the plugin is first attached to the host application. {@link #onPluginLoaded} * will be automatically called as well when first attached. This may be called multiple times * if multiple plugins are allowed. It may also be called in the future if the plugin package * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new * or different {@link PluginLifecycleManager}. * will be automatically called as well when first attached if true is returned. This may be * called multiple times if multiple plugins are allowed. It may also be called in the future * if the plugin package changes and needs to be reloaded. Each call to * {@link #onPluginAttached} will provide a new or different {@link PluginLifecycleManager}. * * @return returning true will immediately load the plugin and call onPluginLoaded with the * created object. false will skip loading, but the listener can load it at any time using the * provided PluginLifecycleManager. Loading plugins immediately is the default behavior. */ default void onPluginAttached(PluginLifecycleManager<T> manager) { default boolean onPluginAttached(PluginLifecycleManager<T> manager) { // Optional return true; } /** Loading packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +22 −14 Original line number Diff line number Diff line Loading @@ -79,10 +79,20 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager /** Alerts listener and plugin that the plugin has been created. */ public void onCreate() { mListener.onPluginAttached(this); boolean loadPlugin = mListener.onPluginAttached(this); if (!loadPlugin) { if (mPlugin != null) { unloadPlugin(); } return; } if (mPlugin == null) { loadPlugin(); } else { return; } mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. Loading @@ -90,7 +100,6 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager } mListener.onPluginLoaded(mPlugin, mPluginContext, this); } } /** Alerts listener and plugin that the plugin is being shutdown. */ public void onDestroy() { Loading Loading @@ -118,6 +127,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return; } mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. Loading Loading @@ -205,12 +215,8 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager PluginFactory<T> pluginFactory = new PluginFactory<T>( context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass, () -> getClassLoader(appInfo, mBaseClassLoader)); // TODO: Only create the plugin before version check if we need it for // legacy version check. T instance = pluginFactory.createPlugin(); pluginFactory.checkVersion(instance); return new PluginInstance<T>( context, listener, componentName, pluginFactory, instance); context, listener, componentName, pluginFactory, null); } private boolean isPluginPackagePrivileged(String packageName) { Loading Loading @@ -332,7 +338,9 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager ClassLoader loader = mClassLoaderFactory.get(); Class<T> instanceClass = (Class<T>) Class.forName( mComponentName.getClassName(), true, loader); return (T) mInstanceFactory.create(instanceClass); T result = (T) mInstanceFactory.create(instanceClass); Log.v(TAG, "Created plugin: " + result); return result; } catch (ClassNotFoundException ex) { Log.e(TAG, "Failed to load plugin", ex); } catch (IllegalAccessException ex) { Loading Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +67 −2 Original line number Diff line number Diff line Loading @@ -48,6 +48,18 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext private val KEY_TIMESTAMP = "appliedTimestamp" private val KNOWN_PLUGINS = mapOf<String, List<ClockMetadata>>( "com.android.systemui.falcon.one" to listOf(ClockMetadata("ANALOG_CLOCK_BIGNUM")), "com.android.systemui.falcon.two" to listOf(ClockMetadata("DIGITAL_CLOCK_CALLIGRAPHY")), "com.android.systemui.falcon.three" to listOf(ClockMetadata("DIGITAL_CLOCK_FLEX")), "com.android.systemui.falcon.four" to listOf(ClockMetadata("DIGITAL_CLOCK_GROWTH")), "com.android.systemui.falcon.five" to listOf(ClockMetadata("DIGITAL_CLOCK_HANDWRITTEN")), "com.android.systemui.falcon.six" to listOf(ClockMetadata("DIGITAL_CLOCK_INFLATE")), "com.android.systemui.falcon.seven" to listOf(ClockMetadata("DIGITAL_CLOCK_METRO")), "com.android.systemui.falcon.eight" to listOf(ClockMetadata("DIGITAL_CLOCK_NUMBEROVERLAP")), "com.android.systemui.falcon.nine" to listOf(ClockMetadata("DIGITAL_CLOCK_WEATHER")), ) private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut( key: TKey, Loading Loading @@ -127,8 +139,61 @@ open class ClockRegistry( private val pluginListener = object : PluginListener<ClockProviderPlugin> { override fun onPluginAttached(manager: PluginLifecycleManager<ClockProviderPlugin>) { manager.loadPlugin() override fun onPluginAttached( manager: PluginLifecycleManager<ClockProviderPlugin> ): Boolean { if (keepAllLoaded) { // Always load new plugins if requested return true } val knownClocks = KNOWN_PLUGINS.get(manager.getPackage()) if (knownClocks == null) { logBuffer.tryLog( TAG, LogLevel.WARNING, { str1 = manager.getPackage() }, { "Loading unrecognized clock package: $str1" } ) return true } logBuffer.tryLog( TAG, LogLevel.INFO, { str1 = manager.getPackage() }, { "Skipping initial load of known clock package package: $str1" } ) var isClockListChanged = false for (metadata in knownClocks) { val id = metadata.clockId val info = availableClocks.concurrentGetOrPut(id, ClockInfo(metadata, null, manager)) { isClockListChanged = true onConnected(id) } if (manager != info.manager) { logBuffer.tryLog( TAG, LogLevel.ERROR, { str1 = id }, { "Clock Id conflict on known attach: $str1 is double registered" } ) continue } info.provider = null } if (isClockListChanged) { triggerOnAvailableClocksChanged() } verifyLoadedProviders() // Load executed via verifyLoadedProviders return false } override fun onPluginLoaded( Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +3 −1 Original line number Diff line number Diff line Loading @@ -190,7 +190,9 @@ enum class ClockTickRate(val value: Int) { data class ClockMetadata( val clockId: ClockId, val name: String, ) ) { constructor(clockId: ClockId) : this(clockId, clockId) {} } /** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */ data class ClockConfig( Loading
packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginLifecycleManager.java +8 −0 Original line number Diff line number Diff line Loading @@ -16,12 +16,20 @@ package com.android.systemui.plugins; import android.content.ComponentName; /** * Provides the ability for consumers to control plugin lifecycle. * * @param <T> is the target plugin type */ public interface PluginLifecycleManager<T extends Plugin> { /** Returns the ComponentName of the target plugin. Maybe be called when not loaded. */ ComponentName getComponentName(); /** Returns the package name of the target plugin. May be called when not loaded. */ String getPackage(); /** Returns the currently loaded plugin instance (if plugin is loaded) */ T getPlugin(); Loading
packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginListener.java +10 −5 Original line number Diff line number Diff line Loading @@ -60,13 +60,18 @@ public interface PluginListener<T extends Plugin> { /** * Called when the plugin is first attached to the host application. {@link #onPluginLoaded} * will be automatically called as well when first attached. This may be called multiple times * if multiple plugins are allowed. It may also be called in the future if the plugin package * changes and needs to be reloaded. Each call to {@link #onPluginAttached} will provide a new * or different {@link PluginLifecycleManager}. * will be automatically called as well when first attached if true is returned. This may be * called multiple times if multiple plugins are allowed. It may also be called in the future * if the plugin package changes and needs to be reloaded. Each call to * {@link #onPluginAttached} will provide a new or different {@link PluginLifecycleManager}. * * @return returning true will immediately load the plugin and call onPluginLoaded with the * created object. false will skip loading, but the listener can load it at any time using the * provided PluginLifecycleManager. Loading plugins immediately is the default behavior. */ default void onPluginAttached(PluginLifecycleManager<T> manager) { default boolean onPluginAttached(PluginLifecycleManager<T> manager) { // Optional return true; } /** Loading
packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstance.java +22 −14 Original line number Diff line number Diff line Loading @@ -79,10 +79,20 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager /** Alerts listener and plugin that the plugin has been created. */ public void onCreate() { mListener.onPluginAttached(this); boolean loadPlugin = mListener.onPluginAttached(this); if (!loadPlugin) { if (mPlugin != null) { unloadPlugin(); } return; } if (mPlugin == null) { loadPlugin(); } else { return; } mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. Loading @@ -90,7 +100,6 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager } mListener.onPluginLoaded(mPlugin, mPluginContext, this); } } /** Alerts listener and plugin that the plugin is being shutdown. */ public void onDestroy() { Loading Loading @@ -118,6 +127,7 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager return; } mPluginFactory.checkVersion(mPlugin); if (!(mPlugin instanceof PluginFragment)) { // Only call onCreate for plugins that aren't fragments, as fragments // will get the onCreate as part of the fragment lifecycle. Loading Loading @@ -205,12 +215,8 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager PluginFactory<T> pluginFactory = new PluginFactory<T>( context, mInstanceFactory, appInfo, componentName, mVersionChecker, pluginClass, () -> getClassLoader(appInfo, mBaseClassLoader)); // TODO: Only create the plugin before version check if we need it for // legacy version check. T instance = pluginFactory.createPlugin(); pluginFactory.checkVersion(instance); return new PluginInstance<T>( context, listener, componentName, pluginFactory, instance); context, listener, componentName, pluginFactory, null); } private boolean isPluginPackagePrivileged(String packageName) { Loading Loading @@ -332,7 +338,9 @@ public class PluginInstance<T extends Plugin> implements PluginLifecycleManager ClassLoader loader = mClassLoaderFactory.get(); Class<T> instanceClass = (Class<T>) Class.forName( mComponentName.getClassName(), true, loader); return (T) mInstanceFactory.create(instanceClass); T result = (T) mInstanceFactory.create(instanceClass); Log.v(TAG, "Created plugin: " + result); return result; } catch (ClassNotFoundException ex) { Log.e(TAG, "Failed to load plugin", ex); } catch (IllegalAccessException ex) { Loading