赞
踩
QSPanel 创建是从 StatusBar#makeStatusBarView 开始的。
- // Set up the quick settings tile panel
- final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
- if (container != null) {
- FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
- ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
- mExtensionController
- .newExtension(QS.class)
- .withPlugin(QS.class)
- .withDefault(this::createDefaultQSFragment) //FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class);
- .build());
- mBrightnessMirrorController = new BrightnessMirrorController(
- mNotificationShadeWindowView,
- mNotificationPanelViewController,
- mNotificationShadeDepthControllerLazy.get(),
- (visible) -> {
- mBrightnessMirrorVisible = visible;
- updateScrimController();
- });
- fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
- QS qs = (QS) f;
- if (qs instanceof QSFragment) {
- mQSPanel = ((QSFragment) qs).getQsPanel();
- mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
- }
- });
- }

R.id.qs_frame 是一个 FrameLayout 布局,将 QSFragment 布局添加到其中。所以 R.id.qs_frame 最终显示的是 QSFragment。接下来就先看看 QSFragment 的 onCreateView() 方法。
- @Override
- public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
- Bundle savedInstanceState) {
- inflater = mInjectionInflater.injectable(
- inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
- return inflater.inflate(R.layout.qs_panel, container, false);
- }
再看 QSFragment 的构造函数:
- public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
- InjectionInflationController injectionInflater, QSTileHost qsTileHost,
- StatusBarStateController statusBarStateController, CommandQueue commandQueue,
- QSContainerImplController.Builder qsContainerImplControllerBuilder) {
- mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
- mInjectionInflater = injectionInflater;
- mQSContainerImplControllerBuilder = qsContainerImplControllerBuilder;
- commandQueue.observe(getLifecycle(), this);
- mHost = qsTileHost;
- mStatusBarStateController = statusBarStateController;
- }
这里实例化了QSTileHost 。下面我们就进入到 QSTileHost 的构造方法:
- @Inject
- public QSTileHost(Context context,
- StatusBarIconController iconController,
- QSFactory defaultFactory,
- @Main Handler mainHandler,
- @Background Looper bgLooper,
- PluginManager pluginManager,
- TunerService tunerService,
- Provider<AutoTileManager> autoTiles,
- DumpManager dumpManager,
- BroadcastDispatcher broadcastDispatcher,
- Optional<StatusBar> statusBarOptional,
- QSLogger qsLogger,
- UiEventLogger uiEventLogger) {
- mIconController = iconController;
- mContext = context;
- mUserContext = context;
- mTunerService = tunerService;
- mPluginManager = pluginManager;
- mDumpManager = dumpManager;
- mQSLogger = qsLogger;
- mUiEventLogger = uiEventLogger;
- mBroadcastDispatcher = broadcastDispatcher;
-
- // M: @ {
- mQuickSettingsExt = OpSystemUICustomizationFactoryBase
- .getOpFactory(context).makeQuickSettings(context);
- // @ }
-
- mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
- mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
- mStatusBarOptional = statusBarOptional;
-
- mQsFactories.add(defaultFactory);
- pluginManager.addPluginListener(this, QSFactory.class, true);
- mDumpManager.registerDumpable(TAG, this);
-
- mainHandler.post(() -> {
- // This is technically a hack to avoid circular dependency of
- // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
- // finishes before creating any tiles.
- tunerService.addTunable(this, TILES_SETTING);
- // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
- mAutoTiles = autoTiles.get();
- });
- }

在 QSTileHost 的构造函数里,我们主要看 tunerService.addTunable(this, TILES_SETTING); 很明显,调用 tunerService 里的 addTunabe() 方法,跟进去会发现,最终的是调用的 TunerServiceImpl 里面的 addTunabe() 方法。
- private void addTunable(Tunable tunable, String key) {
- if (!mTunableLookup.containsKey(key)) {
- mTunableLookup.put(key, new ArraySet<Tunable>());
- }
- mTunableLookup.get(key).add(tunable);
- if (LeakDetector.ENABLED) {
- mTunables.add(tunable);
- mLeakDetector.trackCollection(mTunables, "TunerService.mTunables");
- }
- Uri uri = Settings.Secure.getUriFor(key);
- if (!mListeningUris.containsKey(uri)) {
- mListeningUris.put(uri, key);
- mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
- }
- // Send the first state.
- String value = DejankUtils.whitelistIpcs(() -> Settings.Secure
- .getStringForUser(mContentResolver, key, mCurrentUser));
- tunable.onTuningChanged(key, value);
- }

tunable.onTuningChanged() 回调 QSTileHost#onTuningChanged():
- @Override
- public void onTuningChanged(String key, String newValue) {
- if (!TILES_SETTING.equals(key)) {
- return;
- }
- Log.d(TAG, "Recreating tiles");
- if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
- newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
- }
- final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
- int currentUser = ActivityManager.getCurrentUser();
- if (currentUser != mCurrentUser) {
- mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
- if (mAutoTiles != null) {
- mAutoTiles.changeUser(UserHandle.of(currentUser));
- }
- }
- if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
- mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
- tile -> {
- Log.d(TAG, "Destroying tile: " + tile.getKey());
- mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
- tile.getValue().destroy();
- });
- final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
- for (String tileSpec : tileSpecs) {
- QSTile tile = mTiles.get(tileSpec);
- 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);
- mQSLogger.logTileAdded(tileSpec);
- } else {
- tile.destroy();
- Log.d(TAG, "Destroying not available tile: " + tileSpec);
- mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
- }
- } else {
- // This means that the tile is a CustomTile AND the user is different, so let's
- // destroy it
- if (tile != null) {
- tile.destroy();
- Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
- mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
- }
- Log.d(TAG, "Creating tile: " + tileSpec);
- try {
- tile = createTile(tileSpec);
- if (tile != null) {
- tile.setTileSpec(tileSpec);
- if (tile.isAvailable()) {
- newTiles.put(tileSpec, tile);
- mQSLogger.logTileAdded(tileSpec);
- } else {
- tile.destroy();
- Log.d(TAG, "Destroying not available tile: " + tileSpec);
- mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
- }
- }
- } catch (Throwable t) {
- Log.w(TAG, "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);
- if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
- // If we didn't manage to create any tiles, set it to empty (default)
- Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
- changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
- } else {
- for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onTilesChanged();
- }
- }
- }

这里有两个重要的方法:一个是获取 config 里字符串信息 loadTileSpecs(mContext, newValue);一个实例化 Tile 的 createTile(tileSpec)。
先看第一个 QSTileHost#loadTileSpecs():
- protected static List<String> loadTileSpecs(Context context, String tileList) {
- final Resources res = context.getResources();
-
- if (TextUtils.isEmpty(tileList)) {
- tileList = res.getString(R.string.quick_settings_tiles);
- if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
- } else {
- if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
- }
- final ArrayList<String> tiles = new ArrayList<String>();
- boolean addedDefault = false;
- Set<String> addedSpecs = new ArraySet<>();
- for (String tile : tileList.split(",")) {
- tile = tile.trim();
- if (tile.isEmpty()) continue;
- if (tile.equals("default")) {
- if (!addedDefault) {
- List<String> defaultSpecs = getDefaultSpecs(context);
- for (String spec : defaultSpecs) {
- if (!addedSpecs.contains(spec)) {
- tiles.add(spec);
- addedSpecs.add(spec);
- }
- }
- addedDefault = true;
- }
- } else {
- if (!addedSpecs.contains(tile)) {
- tiles.add(tile);
- addedSpecs.add(tile);
- }
- }
- }
- return tiles;
- }

上述代码中第一次 tileList 为空,调用了 getDefaultSpecs(context) 获取字符串
- public static List<String> getDefaultSpecs(Context context) {
- final ArrayList<String> tiles = new ArrayList<String>();
-
- final Resources res = context.getResources();
- String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
-
- /// M: Customize the quick settings tile order for operator. @{
- if (mQuickSettingsExt != null) {
- defaultTileList = mQuickSettingsExt.addOpTileSpecs(defaultTileList);
- // @}
- defaultTileList = mQuickSettingsExt.customizeQuickSettingsTileOrder(defaultTileList);
- }
- /// M: Customize the quick settings tile order for operator. @}
- Log.d(TAG, "loadTileSpecs() default tile list: " + defaultTileList);
-
- tiles.addAll(Arrays.asList(defaultTileList.split(",")));
- if (Build.IS_DEBUGGABLE
- && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
- tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
- }
- return tiles;
- }

接着看第二个 QSTileHost#createTile(tileSpec) 方法:
- public QSTile createTile(String tileSpec) {
- for (int i = 0; i < mQsFactories.size(); i++) {
- QSTile t = mQsFactories.get(i).createTile(tileSpec);
- if (t != null) {
- return t;
- }
- }
- // M: @ {
- if (mQuickSettingsExt != null && mQuickSettingsExt.doOperatorSupportTile(tileSpec)) {
- // WifiCalling
- return (QSTile) mQuickSettingsExt.createTile(this, tileSpec);
- }
- // @ }
- return null;
- }
这里调用 QSFactory#createTile(),而 QSFactory 接口又由 QSFactoryImpl 实现。所以这里直接看 QSFactoryImpl #createTile():
- public QSTile createTile(String tileSpec) {
- QSTileImpl tile = createTileInternal(tileSpec);
- if (tile != null) {
- tile.handleStale(); // Tile was just created, must be stale.
- }
- return tile;
- }
-
- private QSTileImpl createTileInternal(String tileSpec) {
- /// M: Add extra tiles in quicksetting @{
- Context context = mQsHostLazy.get().getContext();
- IQuickSettingsPlugin quickSettingsPlugin = OpSystemUICustomizationFactoryBase
- .getOpFactory(context).makeQuickSettings(context);
- /// @}
- // Stock tiles.
- switch (tileSpec) {
- case "wifi":
- return mWifiTileProvider.get();
- case "bt":
- return mBluetoothTileProvider.get();
- case "cell":
- return mCellularTileProvider.get();
- case "dnd":
- return mDndTileProvider.get();
- case "inversion":
- return mColorInversionTileProvider.get();
- case "airplane":
- return mAirplaneModeTileProvider.get();
- case "work":
- return mWorkModeTileProvider.get();
- case "rotation":
- return mRotationLockTileProvider.get();
- case "flashlight":
- return mFlashlightTileProvider.get();
- case "location":
- return mLocationTileProvider.get();
- case "cast":
- return mCastTileProvider.get();
- case "hotspot":
- return mHotspotTileProvider.get();
- case "user":
- return mUserTileProvider.get();
- case "battery":
- return mBatterySaverTileProvider.get();
- case "saver":
- return mDataSaverTileProvider.get();
- case "night":
- return mNightDisplayTileProvider.get();
- case "nfc":
- return mNfcTileProvider.get();
- case "dark":
- return mUiModeNightTileProvider.get();
- case "screenrecord":
- return mScreenRecordTileProvider.get();
- }
-
- /// M: Customize the quick settings tiles for operator. @{
- if (tileSpec.equals("dataconnection") && !SIMHelper.isWifiOnlyDevice()) {
- return new MobileDataTile(mQsHostLazy.get());
- }
-
- // Custom tiles
- if (tileSpec.startsWith(CustomTile.PREFIX)) {
- return CustomTile.create(mQsHostLazy.get(), tileSpec,
- mQsHostLazy.get().getUserContext());
- }
-
- // Debug tiles.
- if (Build.IS_DEBUGGABLE) {
- if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
- return mMemoryTileProvider.get();
- }
- }
-
- // Broken tiles.
- Log.w(TAG, "No stock tile spec: " + tileSpec);
- return null;
- }

看到这里通过对应的字符串分别实例化了对应的 Tile。
- public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
- if (!collapsedView) {
- mQsTileRevealController.updateRevealedTiles(tiles);
- }
- for (TileRecord record : mRecords) {
- mTileLayout.removeTile(record);
- record.tile.removeCallback(record.callback);
- }
- mRecords.clear();
- mCachedSpecs = "";
- for (QSTile tile : tiles) {
- addTile(tile, collapsedView);
- }
- }
- protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
- final TileRecord r = new TileRecord();
- r.tile = tile;
- r.tileView = createTileView(tile, collapsedView);
- final QSTile.Callback callback = new QSTile.Callback() {
- @Override
- public void onStateChanged(QSTile.State state) {
- drawTile(r, state);
- }
-
- @Override
- public void onShowDetail(boolean show) {
- // Both the collapsed and full QS panels get this callback, this check determines
- // which one should handle showing the detail.
- if (shouldShowDetail()) {
- QSPanel.this.showDetail(show, r);
- }
- }
-
- @Override
- public void onToggleStateChanged(boolean state) {
- if (mDetailRecord == r) {
- fireToggleStateChanged(state);
- }
- }
-
- @Override
- public void onScanStateChanged(boolean state) {
- r.scanState = state;
- if (mDetailRecord == r) {
- fireScanStateChanged(r.scanState);
- }
- }
-
- @Override
- public void onAnnouncementRequested(CharSequence announcement) {
- if (announcement != null) {
- mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
- .sendToTarget();
- }
- }
- };
- r.tile.addCallback(callback);
- r.callback = callback;
- r.tileView.init(r.tile);
- r.tile.refreshState();
- mRecords.add(r);
- mCachedSpecs = getTilesSpecs();
-
- if (mTileLayout != null) {
- mTileLayout.addTile(r); //加载到页面上
- }
-
- return r;
- }

- @Override
- public QSTileView createTileView(QSTile tile, boolean collapsedView) {
- Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
- QSIconView icon = tile.createTileView(context);
- if (collapsedView) {
- return new QSTileBaseView(context, icon, collapsedView);
- } else {
- return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
- }
- }
这段代码的工作有两个:1.由tile的数据创建QSTileView,并且保持在TileRecord。
2.把创建好的TileRecord 添加的快捷面板中 mTileLayout.addTile(r)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。