当前位置:   article > 正文

Android SystemUI之下拉菜单栏(三)_android qsfactoryimpl

android qsfactoryimpl

一、QS创建

QSPanel 创建是从 StatusBar#makeStatusBarView 开始的。

  1. // Set up the quick settings tile panel
  2. final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame);
  3. if (container != null) {
  4. FragmentHostManager fragmentHostManager = FragmentHostManager.get(container);
  5. ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
  6. mExtensionController
  7. .newExtension(QS.class)
  8. .withPlugin(QS.class)
  9. .withDefault(this::createDefaultQSFragment) //FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class);
  10. .build());
  11. mBrightnessMirrorController = new BrightnessMirrorController(
  12. mNotificationShadeWindowView,
  13. mNotificationPanelViewController,
  14. mNotificationShadeDepthControllerLazy.get(),
  15. (visible) -> {
  16. mBrightnessMirrorVisible = visible;
  17. updateScrimController();
  18. });
  19. fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
  20. QS qs = (QS) f;
  21. if (qs instanceof QSFragment) {
  22. mQSPanel = ((QSFragment) qs).getQsPanel();
  23. mQSPanel.setBrightnessMirror(mBrightnessMirrorController);
  24. }
  25. });
  26. }

R.id.qs_frame 是一个 FrameLayout 布局,将 QSFragment 布局添加到其中。所以 R.id.qs_frame 最终显示的是 QSFragment。接下来就先看看 QSFragment 的 onCreateView() 方法。

  1. @Override
  2. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
  3. Bundle savedInstanceState) {
  4. inflater = mInjectionInflater.injectable(
  5. inflater.cloneInContext(new ContextThemeWrapper(getContext(), R.style.qs_theme)));
  6. return inflater.inflate(R.layout.qs_panel, container, false);
  7. }

再看 QSFragment 的构造函数

  1. public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
  2. InjectionInflationController injectionInflater, QSTileHost qsTileHost,
  3. StatusBarStateController statusBarStateController, CommandQueue commandQueue,
  4. QSContainerImplController.Builder qsContainerImplControllerBuilder) {
  5. mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
  6. mInjectionInflater = injectionInflater;
  7. mQSContainerImplControllerBuilder = qsContainerImplControllerBuilder;
  8. commandQueue.observe(getLifecycle(), this);
  9. mHost = qsTileHost;
  10. mStatusBarStateController = statusBarStateController;
  11. }

这里实例化了QSTileHost 。下面我们就进入到 QSTileHost 的构造方法:

  1. @Inject
  2. public QSTileHost(Context context,
  3. StatusBarIconController iconController,
  4. QSFactory defaultFactory,
  5. @Main Handler mainHandler,
  6. @Background Looper bgLooper,
  7. PluginManager pluginManager,
  8. TunerService tunerService,
  9. Provider<AutoTileManager> autoTiles,
  10. DumpManager dumpManager,
  11. BroadcastDispatcher broadcastDispatcher,
  12. Optional<StatusBar> statusBarOptional,
  13. QSLogger qsLogger,
  14. UiEventLogger uiEventLogger) {
  15. mIconController = iconController;
  16. mContext = context;
  17. mUserContext = context;
  18. mTunerService = tunerService;
  19. mPluginManager = pluginManager;
  20. mDumpManager = dumpManager;
  21. mQSLogger = qsLogger;
  22. mUiEventLogger = uiEventLogger;
  23. mBroadcastDispatcher = broadcastDispatcher;
  24. // M: @ {
  25. mQuickSettingsExt = OpSystemUICustomizationFactoryBase
  26. .getOpFactory(context).makeQuickSettings(context);
  27. // @ }
  28. mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
  29. mServices = new TileServices(this, bgLooper, mBroadcastDispatcher);
  30. mStatusBarOptional = statusBarOptional;
  31. mQsFactories.add(defaultFactory);
  32. pluginManager.addPluginListener(this, QSFactory.class, true);
  33. mDumpManager.registerDumpable(TAG, this);
  34. mainHandler.post(() -> {
  35. // This is technically a hack to avoid circular dependency of
  36. // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
  37. // finishes before creating any tiles.
  38. tunerService.addTunable(this, TILES_SETTING);
  39. // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
  40. mAutoTiles = autoTiles.get();
  41. });
  42. }

在 QSTileHost 的构造函数里,我们主要看 tunerService.addTunable(this, TILES_SETTING); 很明显,调用 tunerService 里的 addTunabe() 方法,跟进去会发现,最终的是调用的 TunerServiceImpl 里面的 addTunabe() 方法。

  1. private void addTunable(Tunable tunable, String key) {
  2. if (!mTunableLookup.containsKey(key)) {
  3. mTunableLookup.put(key, new ArraySet<Tunable>());
  4. }
  5. mTunableLookup.get(key).add(tunable);
  6. if (LeakDetector.ENABLED) {
  7. mTunables.add(tunable);
  8. mLeakDetector.trackCollection(mTunables, "TunerService.mTunables");
  9. }
  10. Uri uri = Settings.Secure.getUriFor(key);
  11. if (!mListeningUris.containsKey(uri)) {
  12. mListeningUris.put(uri, key);
  13. mContentResolver.registerContentObserver(uri, false, mObserver, mCurrentUser);
  14. }
  15. // Send the first state.
  16. String value = DejankUtils.whitelistIpcs(() -> Settings.Secure
  17. .getStringForUser(mContentResolver, key, mCurrentUser));
  18. tunable.onTuningChanged(key, value);
  19. }

tunable.onTuningChanged() 回调 QSTileHost#onTuningChanged():

  1. @Override
  2. public void onTuningChanged(String key, String newValue) {
  3. if (!TILES_SETTING.equals(key)) {
  4. return;
  5. }
  6. Log.d(TAG, "Recreating tiles");
  7. if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
  8. newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
  9. }
  10. final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
  11. int currentUser = ActivityManager.getCurrentUser();
  12. if (currentUser != mCurrentUser) {
  13. mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
  14. if (mAutoTiles != null) {
  15. mAutoTiles.changeUser(UserHandle.of(currentUser));
  16. }
  17. }
  18. if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
  19. mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
  20. tile -> {
  21. Log.d(TAG, "Destroying tile: " + tile.getKey());
  22. mQSLogger.logTileDestroyed(tile.getKey(), "Tile removed");
  23. tile.getValue().destroy();
  24. });
  25. final LinkedHashMap<String, QSTile> newTiles = new LinkedHashMap<>();
  26. for (String tileSpec : tileSpecs) {
  27. QSTile tile = mTiles.get(tileSpec);
  28. if (tile != null && (!(tile instanceof CustomTile)
  29. || ((CustomTile) tile).getUser() == currentUser)) {
  30. if (tile.isAvailable()) {
  31. if (DEBUG) Log.d(TAG, "Adding " + tile);
  32. tile.removeCallbacks();
  33. if (!(tile instanceof CustomTile) && mCurrentUser != currentUser) {
  34. tile.userSwitch(currentUser);
  35. }
  36. newTiles.put(tileSpec, tile);
  37. mQSLogger.logTileAdded(tileSpec);
  38. } else {
  39. tile.destroy();
  40. Log.d(TAG, "Destroying not available tile: " + tileSpec);
  41. mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
  42. }
  43. } else {
  44. // This means that the tile is a CustomTile AND the user is different, so let's
  45. // destroy it
  46. if (tile != null) {
  47. tile.destroy();
  48. Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
  49. mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
  50. }
  51. Log.d(TAG, "Creating tile: " + tileSpec);
  52. try {
  53. tile = createTile(tileSpec);
  54. if (tile != null) {
  55. tile.setTileSpec(tileSpec);
  56. if (tile.isAvailable()) {
  57. newTiles.put(tileSpec, tile);
  58. mQSLogger.logTileAdded(tileSpec);
  59. } else {
  60. tile.destroy();
  61. Log.d(TAG, "Destroying not available tile: " + tileSpec);
  62. mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
  63. }
  64. }
  65. } catch (Throwable t) {
  66. Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
  67. }
  68. }
  69. }
  70. mCurrentUser = currentUser;
  71. List<String> currentSpecs = new ArrayList<>(mTileSpecs);
  72. mTileSpecs.clear();
  73. mTileSpecs.addAll(tileSpecs);
  74. mTiles.clear();
  75. mTiles.putAll(newTiles);
  76. if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
  77. // If we didn't manage to create any tiles, set it to empty (default)
  78. Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
  79. changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
  80. } else {
  81. for (int i = 0; i < mCallbacks.size(); i++) {
  82. mCallbacks.get(i).onTilesChanged();
  83. }
  84. }
  85. }

这里有两个重要的方法:一个是获取 config 里字符串信息 loadTileSpecs(mContext, newValue);一个实例化 Tile 的 createTile(tileSpec)。
先看第一个 QSTileHost#loadTileSpecs():

  1. protected static List<String> loadTileSpecs(Context context, String tileList) {
  2. final Resources res = context.getResources();
  3. if (TextUtils.isEmpty(tileList)) {
  4. tileList = res.getString(R.string.quick_settings_tiles);
  5. if (DEBUG) Log.d(TAG, "Loaded tile specs from config: " + tileList);
  6. } else {
  7. if (DEBUG) Log.d(TAG, "Loaded tile specs from setting: " + tileList);
  8. }
  9. final ArrayList<String> tiles = new ArrayList<String>();
  10. boolean addedDefault = false;
  11. Set<String> addedSpecs = new ArraySet<>();
  12. for (String tile : tileList.split(",")) {
  13. tile = tile.trim();
  14. if (tile.isEmpty()) continue;
  15. if (tile.equals("default")) {
  16. if (!addedDefault) {
  17. List<String> defaultSpecs = getDefaultSpecs(context);
  18. for (String spec : defaultSpecs) {
  19. if (!addedSpecs.contains(spec)) {
  20. tiles.add(spec);
  21. addedSpecs.add(spec);
  22. }
  23. }
  24. addedDefault = true;
  25. }
  26. } else {
  27. if (!addedSpecs.contains(tile)) {
  28. tiles.add(tile);
  29. addedSpecs.add(tile);
  30. }
  31. }
  32. }
  33. return tiles;
  34. }


上述代码中第一次 tileList 为空,调用了 getDefaultSpecs(context) 获取字符串

  1. public static List<String> getDefaultSpecs(Context context) {
  2. final ArrayList<String> tiles = new ArrayList<String>();
  3. final Resources res = context.getResources();
  4. String defaultTileList = res.getString(R.string.quick_settings_tiles_default);
  5. /// M: Customize the quick settings tile order for operator. @{
  6. if (mQuickSettingsExt != null) {
  7. defaultTileList = mQuickSettingsExt.addOpTileSpecs(defaultTileList);
  8. // @}
  9. defaultTileList = mQuickSettingsExt.customizeQuickSettingsTileOrder(defaultTileList);
  10. }
  11. /// M: Customize the quick settings tile order for operator. @}
  12. Log.d(TAG, "loadTileSpecs() default tile list: " + defaultTileList);
  13. tiles.addAll(Arrays.asList(defaultTileList.split(",")));
  14. if (Build.IS_DEBUGGABLE
  15. && GarbageMonitor.ADD_MEMORY_TILE_TO_DEFAULT_ON_DEBUGGABLE_BUILDS) {
  16. tiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);
  17. }
  18. return tiles;
  19. }

接着看第二个 QSTileHost#createTile(tileSpec) 方法:

  1. public QSTile createTile(String tileSpec) {
  2. for (int i = 0; i < mQsFactories.size(); i++) {
  3. QSTile t = mQsFactories.get(i).createTile(tileSpec);
  4. if (t != null) {
  5. return t;
  6. }
  7. }
  8. // M: @ {
  9. if (mQuickSettingsExt != null && mQuickSettingsExt.doOperatorSupportTile(tileSpec)) {
  10. // WifiCalling
  11. return (QSTile) mQuickSettingsExt.createTile(this, tileSpec);
  12. }
  13. // @ }
  14. return null;
  15. }

这里调用 QSFactory#createTile(),而 QSFactory 接口又由 QSFactoryImpl 实现。所以这里直接看 QSFactoryImpl #createTile():

  1. public QSTile createTile(String tileSpec) {
  2. QSTileImpl tile = createTileInternal(tileSpec);
  3. if (tile != null) {
  4. tile.handleStale(); // Tile was just created, must be stale.
  5. }
  6. return tile;
  7. }
  8. private QSTileImpl createTileInternal(String tileSpec) {
  9. /// M: Add extra tiles in quicksetting @{
  10. Context context = mQsHostLazy.get().getContext();
  11. IQuickSettingsPlugin quickSettingsPlugin = OpSystemUICustomizationFactoryBase
  12. .getOpFactory(context).makeQuickSettings(context);
  13. /// @}
  14. // Stock tiles.
  15. switch (tileSpec) {
  16. case "wifi":
  17. return mWifiTileProvider.get();
  18. case "bt":
  19. return mBluetoothTileProvider.get();
  20. case "cell":
  21. return mCellularTileProvider.get();
  22. case "dnd":
  23. return mDndTileProvider.get();
  24. case "inversion":
  25. return mColorInversionTileProvider.get();
  26. case "airplane":
  27. return mAirplaneModeTileProvider.get();
  28. case "work":
  29. return mWorkModeTileProvider.get();
  30. case "rotation":
  31. return mRotationLockTileProvider.get();
  32. case "flashlight":
  33. return mFlashlightTileProvider.get();
  34. case "location":
  35. return mLocationTileProvider.get();
  36. case "cast":
  37. return mCastTileProvider.get();
  38. case "hotspot":
  39. return mHotspotTileProvider.get();
  40. case "user":
  41. return mUserTileProvider.get();
  42. case "battery":
  43. return mBatterySaverTileProvider.get();
  44. case "saver":
  45. return mDataSaverTileProvider.get();
  46. case "night":
  47. return mNightDisplayTileProvider.get();
  48. case "nfc":
  49. return mNfcTileProvider.get();
  50. case "dark":
  51. return mUiModeNightTileProvider.get();
  52. case "screenrecord":
  53. return mScreenRecordTileProvider.get();
  54. }
  55. /// M: Customize the quick settings tiles for operator. @{
  56. if (tileSpec.equals("dataconnection") && !SIMHelper.isWifiOnlyDevice()) {
  57. return new MobileDataTile(mQsHostLazy.get());
  58. }
  59. // Custom tiles
  60. if (tileSpec.startsWith(CustomTile.PREFIX)) {
  61. return CustomTile.create(mQsHostLazy.get(), tileSpec,
  62. mQsHostLazy.get().getUserContext());
  63. }
  64. // Debug tiles.
  65. if (Build.IS_DEBUGGABLE) {
  66. if (tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC)) {
  67. return mMemoryTileProvider.get();
  68. }
  69. }
  70. // Broken tiles.
  71. Log.w(TAG, "No stock tile spec: " + tileSpec);
  72. return null;
  73. }

看到这里通过对应的字符串分别实例化了对应的 Tile。

二、QS显示

  1. public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
  2. if (!collapsedView) {
  3. mQsTileRevealController.updateRevealedTiles(tiles);
  4. }
  5. for (TileRecord record : mRecords) {
  6. mTileLayout.removeTile(record);
  7. record.tile.removeCallback(record.callback);
  8. }
  9. mRecords.clear();
  10. mCachedSpecs = "";
  11. for (QSTile tile : tiles) {
  12. addTile(tile, collapsedView);
  13. }
  14. }
  1. protected TileRecord addTile(final QSTile tile, boolean collapsedView) {
  2. final TileRecord r = new TileRecord();
  3. r.tile = tile;
  4. r.tileView = createTileView(tile, collapsedView);
  5. final QSTile.Callback callback = new QSTile.Callback() {
  6. @Override
  7. public void onStateChanged(QSTile.State state) {
  8. drawTile(r, state);
  9. }
  10. @Override
  11. public void onShowDetail(boolean show) {
  12. // Both the collapsed and full QS panels get this callback, this check determines
  13. // which one should handle showing the detail.
  14. if (shouldShowDetail()) {
  15. QSPanel.this.showDetail(show, r);
  16. }
  17. }
  18. @Override
  19. public void onToggleStateChanged(boolean state) {
  20. if (mDetailRecord == r) {
  21. fireToggleStateChanged(state);
  22. }
  23. }
  24. @Override
  25. public void onScanStateChanged(boolean state) {
  26. r.scanState = state;
  27. if (mDetailRecord == r) {
  28. fireScanStateChanged(r.scanState);
  29. }
  30. }
  31. @Override
  32. public void onAnnouncementRequested(CharSequence announcement) {
  33. if (announcement != null) {
  34. mHandler.obtainMessage(H.ANNOUNCE_FOR_ACCESSIBILITY, announcement)
  35. .sendToTarget();
  36. }
  37. }
  38. };
  39. r.tile.addCallback(callback);
  40. r.callback = callback;
  41. r.tileView.init(r.tile);
  42. r.tile.refreshState();
  43. mRecords.add(r);
  44. mCachedSpecs = getTilesSpecs();
  45. if (mTileLayout != null) {
  46. mTileLayout.addTile(r); //加载到页面上
  47. }
  48. return r;
  49. }
  1. @Override
  2. public QSTileView createTileView(QSTile tile, boolean collapsedView) {
  3. Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme);
  4. QSIconView icon = tile.createTileView(context);
  5. if (collapsedView) {
  6. return new QSTileBaseView(context, icon, collapsedView);
  7. } else {
  8. return new com.android.systemui.qs.tileimpl.QSTileView(context, icon);
  9. }
  10. }

这段代码的工作有两个:1.由tile的数据创建QSTileView,并且保持在TileRecord。

                                        2.把创建好的TileRecord 添加的快捷面板中 mTileLayout.addTile(r)。

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号