SystemUI插件
SystemUI 插件提供了一种快速创建 SystemUI 功能原型的简便方法,可以在运行时更改 SystemUI 的行为。 通过创建插件实现 SysUI 中使用的一组基本接口来完成,然后可以比当前更快的速度迭代由该接口控制的部分代码。
底层逻辑
SystemUIApplication.startServicesIfNeeded
Dependency 是定义在 config_systemUIServiceComponents 这个 config 文件中的,在 startServicesIfNeeded 中会遍历此 config 定义的所有 SystemUI 重要类,调用它们的Start方法:
private void startServicesIfNeeded(String[] services) {
Dependency.get(InitController.class).executePostInitTasks();
log.traceEnd();
final Handler mainHandler = new Handler(Looper.getMainLooper());
//调用addPluginListener传入了三个参数,PluginListener,
//OverlayPlugin.class,和一个boolean值true
Dependency.get(PluginManager.class).addPluginListener(
new PluginListener<OverlayPlugin>() {
private ArraySet<OverlayPlugin> mOverlays = new ArraySet<>();
@Override
public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
mainHandler.post(new Runnable() {
@Override
public void run() {
StatusBar statusBar = getComponent(StatusBar.class);
if (statusBar != null) {
plugin.setup(statusBar.getStatusBarWindow(),
statusBar.getNavigationBarView(), new Callback(plugin));
}
}
});
}
@Override
public void onPluginDisconnected(OverlayPlugin plugin) {
mainHandler.post(new Runnable() {
@Override
public void run() {
mOverlays.remove(plugin);
Dependency.get(StatusBarWindowController.class).setForcePluginOpen(
mOverlays.size() != 0);
}
});
}
class Callback implements OverlayPlugin.Callback {
private final OverlayPlugin mPlugin;
Callback(OverlayPlugin plugin) {
mPlugin = plugin;
}
@Override
public void onHoldStatusBarOpenChange() {
if (mPlugin.holdStatusBarOpen()) {
mOverlays.add(mPlugin);
} else {
mOverlays.remove(mPlugin);
}
mainHandler.post(new Runnable() {
@Override
public void run() {
Dependency.get(StatusBarWindowController.class)
.setStateListener(b -> mOverlays.forEach(
o -> o.setCollapseDesired(b)));
Dependency.get(StatusBarWindowController.class)
.setForcePluginOpen(mOverlays.size() != 0);
}
});
}
}
}, OverlayPlugin.class, true /* Allow multiple plugins */);
mServicesStarted = true;
}
Dependency.start
@Override
public void start() {
// TODO: Think about ways to push these creation rules out of Dependency to cut down
// on imports.
mProviders.put(TIME_TICK_HANDLER, mTimeTickHandler::get);
mProviders.put(BG_LOOPER, mBgLooper::get);
mProviders.put(BG_HANDLER, mBgHandler::get);
mProviders.put(MAIN_HANDLER, mMainHandler::get);
mProviders.put(ActivityStarter.class, mActivityStarter::get);
mProviders.put(ActivityStarterDelegate.class, mActivityStarterDelegate::get);
mProviders.put(AsyncSensorManager.class, mAsyncSensorManager::get);
mProviders.put(BluetoothController.class, mBluetoothController::get);
mProviders.put(SensorPrivacyManager.class, mSensorPrivacyManager::get);
mProviders.put(LocationController.class, mLocationController::get);
mProviders.put(RotationLockController.class, mRotationLockController::get);
mProviders.put(NetworkController.class, mNetworkController::get);
mProviders.put(ZenModeController.class, mZenModeController::get);
mProviders.put(HotspotController.class, mHotspotController::get);
mProviders.put(CastController.class, mCastController::get);
mProviders.put(FlashlightController.class, mFlashlightController::get);
mProviders.put(KeyguardMonitor.class, mKeyguardMonitor::get);
mProviders.put(UserSwitcherController.class, mUserSwitcherController::get);
mProviders.put(UserInfoController.class, mUserInfoController::get);
mProviders.put(BatteryController.class, mBatteryController::get);
......
sDependency = this;
}
Dependency.get
get方法最终通过mDependencies.get来获取对象,mDependencies是个ArrayMap,如果没有获取到就调用createDependency创建对象,创建之后放入mDependencies中
private static Dependency sDependency;
@Deprecated
public static <T> T get(Class<T> cls) {
return sDependency.getDependency(cls);
}
protected final <T> T getDependency(Class<T> cls) {
return getDependencyInner(cls);
}
private synchronized <T> T getDependencyInner(Object key) {
@SuppressWarnings("unchecked")
T obj = (T) mDependencies.get(key);
if (obj == null) {
obj = createDependency(key);
mDependencies.put(key, obj);
}
return obj;
}
createDependency
此方法通过mProviders.get来获取对象,mProviders中的类就是在Start方法中添加的,所以能够直接获取,Dependency中用到了dagger框架的Lazy,Provider等机制。
@VisibleForTesting
protected <T> T createDependency(Object cls) {
@SuppressWarnings("unchecked")
LazyDependencyCreator<T> provider = mProviders.get(cls);
if (provider == null) {
throw new IllegalArgumentException("Unsupported dependency " + cls
+ ". " + mProviders.size() + " providers known.");
}
return provider.createDependency();
}
addPluginListener
综上所述,Dependency.get(T.class)就可以简单认为是获取T的实现类
SystemUIApplication的startServicesIfNeeded方法添加插件监听,这里实际调用PluginManagerImpl的addPluginListener方法,此方法接收三个参数:一个PluginListener接口,一个Class对象,一个boolean值
可以看到共有 4 个重载方法,此处调用的为第2个重载方法,获取action之后调用了第 4 个重载方法
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls) {
addPluginListener(listener, cls, false);
}
public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls,
boolean allowMultiple) {
addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple);
}
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
Class<?> cls) {
addPluginListener(action, listener, cls, false);
}
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
Class cls, boolean allowMultiple) {
android.util.Log.d("djtang","addPluginListener...action = :"+action+",name = :"+cls.getName());
mPluginPrefs.addAction(action);
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
allowMultiple, mLooper, cls, this);
p.loadAll();
mPluginMap.put(listener, p);
startListening();
}
PluginListener
PluginListener这个接口,这是个泛型接口,其中T必须是Plugin的子类,此接口提供两个生命周期方法,插件连接时调用onPluginConnected,断开连接调用onPluginDisconnected
public interface PluginListener<T extends Plugin> {
void onPluginConnected(T plugin, Context pluginContext);
default void onPluginDisconnected(T plugin) {
}
}
PluginManager.Helper.getAction(cls)
通过getDeclaredAnnotation方法获取注解,ProvidesInterface这是个注解类,每个继承Plugin的类包含一个ProvidesInterface,这个注解类包含两项,version和action
public interface PluginManager {
class Helper {
public static <P> String getAction(Class<P> cls) {
//获取ProvidesInterface的注解信息
ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class);
if (info == null) {
throw new RuntimeException(cls + " doesn't provide an interface");
}
if (TextUtils.isEmpty(info.action())) {
throw new RuntimeException(cls + " doesn't provide an action");
}
//获取ProvidesInterface注解的action
return info.action();
}
}
public @interface ProvidesInterface {
int version();
String action() default "";
}
用法
@ProvidesInterface(action = MyPlugin.ACTION, version = MyPlugin.VERSION)
public interface MyPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_MY_PLUGIN";
int VERSION = 1;
...
}
// 小米代码在com.android.systemui.plugins.miui.qs
再看第 4 个重载方法
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
Class cls, boolean allowMultiple) {
mPluginPrefs.addAction(action);
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext,
action, listener,allowMultiple, mLooper, cls, this);
p.loadAll();
mPluginMap.put(listener, p);
startListening();
}
首先将action添加到mPluginPrefs,mPluginPrefs是一个PluginPrefs,它的内部有一个ArraySet和一个SharedPreferences,它的存储机制就是将action先存储到ArraySet,再将此ArraySet存到SharedPreferences中,具体代码就不贴出了
接着mFactory.createPluginInstanceManager方法,mFactory是PluginInstanceManagerFactory类型,是PluginManagerImpl的静态内部类
@VisibleForTesting
public static class PluginInstanceManagerFactory {
public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
Class<?> cls, PluginManagerImpl manager) {
return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
new VersionInfo().addClass(cls), manager);
}
}
createPluginInstanceManager其实就是new了一个PluginInstanceManager对象,
接着调用了PluginInstanceManager的loadAll方法
PluginInstanceManager.loadAll()
通过handler处理消息
public void loadAll() {
if (DEBUG) Log.d(TAG, "startListening");
mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
}
该handler用的是子线程,其looper用的是非主线程looper
PluginInstanceManager(Context context, PackageManager pm, String action,
PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version,
PluginManagerImpl manager, boolean debuggable, String[] pluginWhitelist) {
mMainHandler = new MainHandler(Looper.getMainLooper());
mPluginHandler = new PluginHandler(looper);
}
QUERY_ALL
private class PluginHandler extends Handler {
private final ArrayList<PluginInfo<T>> mPlugins = new ArrayList<>();
public void handleMessage(Message msg) {
switch (msg.what) {
case QUERY_ALL:
if (DEBUG) Log.d(TAG, "queryAll " + mAction);
//如果mPlugins不为空,则遍历mPlugins,调用onPluginDisconnected
//清理Plugin
for (int i = mPlugins.size() - 1; i >= 0; i--) {
PluginInfo<T> plugin = mPlugins.get(i);
mListener.onPluginDisconnected(plugin.mPlugin);
if (!(plugin.mPlugin instanceof PluginFragment)) {
plugin.mPlugin.onDestroy();
}
}
//清空mPlugins
mPlugins.clear();
handleQueryPlugins(null);
break;
}
}
}
handleQueryPlugins
private void handleQueryPlugins(String pkgName) {
Intent intent = new Intent(mAction);
//pkgName == null,此处的intent只有action,没有pkgName
if (pkgName != null) {
intent.setPackage(pkgName);
}
//給定intent,返回满足条件的ResolveInfo(本质是service)集合
List<ResolveInfo> result = mPm.queryIntentServices(intent, 0);
if (DEBUG) Log.d(TAG, "Found " + result.size() + " plugins");
//mAllowMultiple == true,不会走进去
if (result.size() > 1 && !mAllowMultiple) {
Log.w(TAG, "Multiple plugins found for " + mAction);
if (DEBUG) {
for (ResolveInfo info : result) {
ComponentName name = new ComponentName(info.serviceInfo.packageName,
info.serviceInfo.name);
Log.w(TAG, " " + name);
}
}
return;
}
//遍历ResolveInfo
for (ResolveInfo info : result) {
ComponentName name = new ComponentName(info.serviceInfo.packageName,
info.serviceInfo.name);
//根据info信息创建插件
PluginInfo<T> t = handleLoadPlugin(name);
if (t == null) continue;
//如果成功创建了PluginInfo就添加到mPlugins保存
mPlugins.add(t);
//将创建好的PluginInfo发送到主线程处理
mMainHandler.obtainMessage(mMainHandler.PLUGIN_CONNECTED, t).sendToTarget();
}
}
PLUGIN_CONNECTED
private class MainHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case PLUGIN_CONNECTED:
if (DEBUG) Log.d(TAG, "onPluginConnected");
PluginPrefs.setHasPlugins(mContext);
//将handler发送过来的obj强转为PluginInfo
PluginInfo<T> info = (PluginInfo<T>) msg.obj;
mManager.handleWtfs();
//如果是Fragment类型的Plugin
if (!(msg.obj instanceof PluginFragment)) {
//调用它的onCreate方法
info.mPlugin.onCreate(mContext, info.mPluginContext);
}
//调用onPluginConnected方法
mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
break;
......
}
}
- mPluginHandler在后台线程处理消息,在收到QUERY_ALL消息后如果有Plugin则先判断是否是Fragment类型Plugin,如果是则调用onDestroy方法,还会调用Plugin添加的PluginListener的回调onPluginDisconnected
- mMainHandler在主线程处理mPluginHandler发送来的Plugin,同样判断如果是Fragment类型Plugin则调用onCreate,还会调用Plugin添加的PluginListener的回调onPluginConnected
经过上面的分析,我们知道了PluginInstanceManager是一个专门用来管理Plugin的类,每个Plugin对应一个
public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener,
Class cls, boolean allowMultiple) {
mPluginPrefs.addAction(action);
PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
allowMultiple, mLooper, cls, this);
p.loadAll();
mPluginMap.put(listener, p);
startListening();
}
mPluginMap是一个ArrayMap,将同一个Plugin的listener和PluginInstanceManager一一对应的保存进去
private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
= new ArrayMap<>();
startListening()
private void startListening() {
//保证只启动一次
if (mListening) return;
mListening = true;
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(PLUGIN_CHANGED);
filter.addAction(DISABLE_PLUGIN);
filter.addDataScheme("package");
mContext.registerReceiver(this, filter);
filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiver(this, filter);
}
注册广播,在收到广播时,交由reciever处理
public void onReceive(Context context, Intent intent) {
if(Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())){
......
}else if(DISABLE_PLUGIN.equals(intent.getAction())){
......
}else {
.....
if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
for (PluginInstanceManager manager : mPluginMap.values()) {
manager.onPackageChange(pkg);
}
} else {
for (PluginInstanceManager manager : mPluginMap.values()) {
manager.onPackageRemoved(pkg);
}
}
}
}
else分支中如果是ACTION_PACKAGE_REMOVED广播则调用
manager.onPackageRemoved,否则调用manager.onPackageChange,这两个方法在PluginInstanceManager类中,其实它们是一对相对应的方法,一个onPackageRemoved最终会调到PluginListener的onPluginDisconnected中,
onPackageChange最终会调到PluginListener的onPluginConnected中
PluginInstanceManager
onPackageChange中相当于先调了一次onPackageRemoved
public void onPackageRemoved(String pkg) {
mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
}
public void onPackageChange(String pkg) {
mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
}
QUERY_PKG和REMOVE_PKG
private class PluginHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case REMOVE_PKG:
mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
plugin.mPlugin).sendToTarget();
break;
case QUERY_PKG:
String p = (String) msg.obj;
handleQueryPlugins(p);
.....
break;
}
}
}
REMOVE_PKG中向MainHandler主线程发送了PLUGIN_DISCONNECTED的msg,QUERY_PKG中调用了handleQueryPlugins方法,此方法前面已经分析过了,最终是向MainHandler主线程发送了PLUGIN_CONNECTED的msg
private class MainHandler extends Handler {
switch (msg.what) {
case PLUGIN_CONNECTED:
......
mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
break;
case PLUGIN_DISCONNECTED:
mListener.onPluginDisconnected((T) msg.obj);
......
break;
}
}
mListener.onPluginConnected(info.mPlugin, info.mPluginContext);
private PluginListener mQsTilePluginListener = new PluginListener<MiuiQSTilePlugin>() {
@Override
public void onPluginConnected(MiuiQSTilePlugin plugin, Context pluginContext) {
// 处理插件, 获取插件内容
// 前面获取到所有的插件后, 遍历插件实例化, 并且通过消息发送, 触发回调
mMiuiQSTilsplugin = plugin;
mPluginStockTiles = plugin.getStockTileWithOrder();
mPluginDefaultTiles = plugin.getDefaultTileWithOrder();
mQsStockTiles = TextUtils.isEmpty(mPluginStockTiles) ? mQsStockTiles : mPluginStockTiles;
mQsDefaultTiles = TextUtils.isEmpty(mPluginDefaultTiles) ? mQsDefaultTiles : mPluginDefaultTiles;
filterIndependentTiles();
onTuningChanged();
}
@Override
public void onPluginDisconnected(MiuiQSTilePlugin plugin) {
mMiuiQSTilsplugin = null;
initQSTiles(mContext);
onTuningChanged();
}
};
onTuningChanged()
private void onTuningChanged() {
onTuningChanged(mTileListKey, "");
// getTileListValue()获取前面写进内存里的mQsDefaultTiles值.即plugin中自定义的插件名
// 格式"wifi, camara" 名称中间用逗号隔开
onTuningChanged(mTileListKey, getTileListValue());
}
public void onTuningChanged(String key, String newValue) {
...
// 将插件名通过识别逗号,解析成string的list
final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
// 新建map用于存储解析出来的插件
final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
for (String tileSpec : tileSpecs) {
QSTile tile = mTiles.get(tileSpec);
// 前面回调触发2次, 第一次用于重置,第二次用于新建
if (tile != null && (!(tile instanceof CustomTile)
|| ((CustomTile) tile).getUser() == currentUser)) {
if (tile.isAvailable()) {
if (DEBUG) Log.d(TAG, "Adding " + tile);
tile.removeCallbacks();
if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
tile.userSwitch(currentUser);
}
newTiles.put(tileSpec, tile);
} else {
Slog.d(TAG, "onTuningChanged: unavailable tile: " + tile);
tile.destroy();
}
} else {
if (DEBUG) Log.d(TAG, "Creating tile: " + tileSpec);
if (tile != null) {
tile.destroy();
}
try {
tile = createTile(tileSpec);
if (tile != null) {
if (tile.isAvailable()) {
tile.setTileSpec(tileSpec);
newTiles.put(tileSpec, tile);
} else {
Slog.d(TAG, "onTuningChanged: unavailable custom tile: " + tile);
tile.destroy();
}
}
} catch (Throwable t) {
Slog.w(TAG, "onTuningChanged: Error creating tile for spec: " + tileSpec, t);
}
}
}
mCurrentUser = currentUser;
List<String> currentSpecs = new ArrayList(mTileSpecs);
mTileSpecs.clear();
mTileSpecs.addAll(tileSpecs);
mTiles.clear();
mTiles.putAll(newTiles);
...
}
小米的比较特殊, listener的回调中, 触发了2次onTuningChanged
根据其注释及代码理解, 第一次用于解绑所有的插件, 第二次用于重新创建插件
mTiles 用于存储已创建插件, 视图上的事件绑定和显示都将用到这个list
createTile(tileSpec)
public QSTile createTile(String tileSpec) {
// Use Extreme battery saver instead of SuperPower if extreme battery saver supported.
if (tileSpec.equals("custom(com.miui.securitycenter/com.miui.superpower.notification.SuperPowerTileService)")
&& (!(ActivityManager.getCurrentUser() == UserHandle.USER_SYSTEM)
|| (Constants.IS_INTERNATIONAL && Constants.SUPPORT_EXTREME_BATTERY_SAVER && !Constants.IS_TABLET))) {
return null;
}
if (tileSpec.equals("custom(com.miui.screenrecorder/.service.QuickService)")
&& !(ActivityManager.getCurrentUser() == UserHandle.USER_SYSTEM)) {
return null;
}
for (int i = 0; i < mQsFactories.size(); i++) {
QSTile t = mQsFactories.get(i).createTile(tileSpec, mControlTileHost);
if (t != null) {
return t;
}
}
return null;
}
通过插件名创建插件对象
工厂方法,最终走到QSFactoryImpl.createTile(String tileSpec)
private QSTileImpl createTileInternal(String tileSpec) {
MiuiQSTilePlugin miuiQSTilePlugin = mHost.getMiuiQSTilePlugin();
HashMap<String, MiuiQSTile> map = null;
if (miuiQSTilePlugin != null) {
map = (HashMap) miuiQSTilePlugin.getAllPluginTiles();
}
if (map != null && map.containsKey(tileSpec)) {
// 三方插件走到这里
return new PluginTile(mHost, map.get(tileSpec));
} else if (tileSpec.equals("wifi")) return new WifiTile(mHost);
else if (tileSpec.equals("bt")) return new BluetoothTile(mHost);
else if (tileSpec.equals("cell")) return new CellularTile(mHost);
else if (tileSpec.equals("airplane")) return new AirplaneModeTile(mHost);
else if (tileSpec.equals("rotation")) return new RotationLockTile(mHost);
else if (tileSpec.equals("flashlight")) return new FlashlightTile(mHost);
else if (tileSpec.equals("gps")) return Build.VERSION.SDK_INT > 28 ? new LocationTile(mHost) : new GpsTile(mHost);
...
}
可以看到部分插件是写死在这里的, 直接返回创建的特定的对象,但是三方插件都是通过miuiQSTilePlugin.getAllPluginTiles()方法获取到自定义的PluginTileMap,并通过tileSpec这个key去查找对应的对象,最后生成通用对象PluginTile()
我们来看一下之前小米实现的Plugin接口
@ProvidesInterface(action = MiuiQSTilePlugin.ACTION, version = MiuiQSTilePlugin.VERSION)
public interface MiuiQSTilePlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_MIUI_QS_TILE";
int VERSION = 1;
//get default tile order,it will override the local default tile order
String getDefaultTileWithOrder();
//get stock tile order,it will override the local stock tile order
String getStockTileWithOrder();
//get all plugin tiles,to achieve function of tilePlugins.The map key is tilespec.
Map<String, MiuiQSTile> getAllPluginTiles();
}
这里可以看到, 要求插件必须继承并实现 getAllPluginTiles()方法,下面举一个实现的例子
@Requires(target = MiuiQSTilePlugin.class, version = MiuiQSTilePlugin.VERSION)
public class LocalMiuiQSTilePlugin implements MiuiQSTilePlugin {
private Context mPluginContext;
private Context mSysuiContext;
HashMap<String, MiuiQSTile> mAllMiuiTilesMap;
@Override
public void onCreate(Context sysuiContext, Context pluginContext) {
mSysuiContext = sysuiContext;
mPluginContext = pluginContext;
initAllMiuiTilesPlugin();
}
private void initAllMiuiTilesPlugin() {
mAllMiuiTilesMap = new HashMap<>();
FreeFormHangTile freeFormHangTile = new FreeFormHangTile(mSysuiContext, mPluginContext);
mAllMiuiTilesMap.put(freeFormHangTile.getTileSpec(), freeFormHangTile);
ScannerTile scannerTile = new ScannerTile(mSysuiContext, mPluginContext);
mAllMiuiTilesMap.put(scannerTile.getTileSpec(), scannerTile);
}
@Override
public Map<String, MiuiQSTile> getAllPluginTiles() {
return mAllMiuiTilesMap;
}
@Override
public String getDefaultTileWithOrder() {
return mPluginContext.getString(R.string.quick_settings_tiles_default);
}
@Override
public String getStockTileWithOrder() {
return mPluginContext.getString(R.string.quick_settings_tiles_stock);
}
@Override
public void onDestroy() {
}
}
简单来说, 即三方插件需要继承MiuiQSTilePlugin,并实现其对应方法
其中2种方法用于获取三方插件中需要添加的插件名称及排序方式
getAllPluginTiles用于获取插件名称和实现对象的对应关系.
本文由 bt 创作,采用 知识共享署名4.0 国际许可协议进行许可。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。