Android14的QS面板的加载解析

发布于:2025-08-07 ⋅ 阅读:(12) ⋅ 点赞:(0)

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java

QS 面板的创建

  1. getNotificationShadeWindowView():整个systemui的最顶级的视图容器(super_notification_shade.xml)
  2. R.id.qs_frame : 是整个QS 面板的容器视图
  3. mQSPanelController:获取其内部的 QSPanelController(QS 面板的控制器,负责管理快捷开关的显示、点击事件等)
// Set up the quick settings tile pane
 protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
		......
        final View container = getNotificationShadeWindowView().findViewById(R.id.qs_frame);
        if (container != null) {
        //FragmentHostManager:是 SystemUI 中用于管理 Fragment 与宿主视图(这里即 container)关联的工具类,负责 Fragment 的添加、移除、生命周期绑定等。
            FragmentHostManager fragmentHostManager =
                    mFragmentService.getFragmentHostManager(container);
            ExtensionFragmentListener.attachExtensonToFragment(
                    mFragmentService,
                    container,
                    QS.TAG,
                    R.id.qs_frame,
                    mExtensionController
                            .newExtension(QS.class)
                            .withPlugin(QS.class)
                            .withDefault(this::createDefaultQSFragment)
                            .build());
            mBrightnessMirrorController = new BrightnessMirrorController(
                    getNotificationShadeWindowView(),
                    mShadeSurface,
                    mNotificationShadeDepthControllerLazy.get(),
                    mBrightnessSliderFactory,
                    (visible) -> {
                        mBrightnessMirrorVisible = visible;
                        updateScrimController();
                    });
             //给 fragmentHostManager 添加一个标签监听器,监听 QS.TAG 对应的 Fragment(即 QS 面板的 Fragment)的生命周期变化(如 Fragment 被创建或绑定到容器时)
            fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> {
                QS qs = (QS) f;
                if (qs instanceof QSFragment) {
                    mQSPanelController = ((QSFragment) qs).getQSPanelController();
                    ((QSFragment) qs).setBrightnessMirrorController(mBrightnessMirrorController);
                }
            });
        }
}

/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java

这里开始看创建面板QSFragment

    protected QS createDefaultQSFragment() {
        return mFragmentService
                .getFragmentHostManager(getNotificationShadeWindowView())
                .create(QSFragment.class);
    }

/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java

QSFragment 初始化

!!!这里的 qsFragmentComponent.getQSPanelController是重点,返回QSPanelController对象

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
        mQSPanelController = qsFragmentComponent.getQSPanelController();
        mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();

        mQSPanelController.init();
        mQuickQSPanelController.init();

/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java

QSPanelController初始化

!!!这里开始QSHost开始被注入创建,QSHost主要是数据源!!!

 @Inject
    QSPanelController(QSPanel view, TunerService tunerService,
            QSHost qsHost, QSCustomizerController qsCustomizerController,
            @Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
            @Named(QS_PANEL) MediaHost mediaHost,
            QSTileRevealController.Factory qsTileRevealControllerFactory,
            DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
            QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
            BrightnessSliderController.Factory brightnessSliderFactory,
            FalsingManager falsingManager,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
        super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                metricsLogger, uiEventLogger, qsLogger, dumpManager);

QSHost提供实现类是QSHostAdapter

/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt

@Module
interface QSHostModule {
	//@Binds: 表明QSHostAdapter是QSHost的具体实现类,当其他类通过 Dagger 注入 QSHost 时(比如在构造函数中声明 @Inject constructor(private val qsHost: QSHost)),Dagger 会自动创建 QSHostAdapter 的实例
    @Binds fun provideQsHost(controllerImpl: QSHostAdapter): QSHost
}

/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSHostAdapter.kt

QSHostAdapter其实只是一个包装类,具体实现是QSTileHost,记住这个地方

QSTileHost从此刻开始被注入创建

constructor(
    private val qsTileHost: QSTileHost,
    private val interactor: CurrentTilesInteractor,
    private val context: Context,
    private val tileServiceRequestControllerBuilder: TileServiceRequestController.Builder,
    @Application private val scope: CoroutineScope,
    private val featureFlags: FeatureFlags,
    dumpManager: DumpManager,
) : QSHost {

QSTileHost开始初始化

主要看这个tunerService.addTunable(this, TILES_SETTING);

    @Inject
    public QSTileHost(Context context,
            QSFactory defaultFactory,
            @Main Executor mainExecutor,
            PluginManager pluginManager,
            TunerService tunerService,
            Provider<AutoTileManager> autoTiles,
            ShadeController shadeController,
            QSLogger qsLogger,
            UserTracker userTracker,
            SecureSettings secureSettings,
            CustomTileStatePersister customTileStatePersister,
            TileLifecycleManager.Factory tileLifecycleManagerFactory,
            UserFileManager userFileManager,
            FeatureFlags featureFlags
    ) {


        mainExecutor.execute(() -> {
            // This is technically a hack to avoid circular dependency of
            // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
            // finishes before creating any tiles.
			//这个在	QSHost 中定义的
			 String TILES_SETTING = Settings.Secure.QS_TILES;


            tunerService.addTunable(this, TILES_SETTING);
            // AutoTileManager can modify mTiles so make sure mTiles has already been initialized.
            if (!mFeatureFlags.isEnabled(Flags.QS_PIPELINE_AUTO_ADD)) {
                mAutoTiles = autoTiles.get();
            }
        });
    }

其实最后调用了QSTileHost的onTuningChanged方法

这里进行数据获取!!!

private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();

    @MainThread
    @Override
    public void onTuningChanged(String key, String newValue) {
        if (!TILES_SETTING.equals(key)) {
            return;
        }
        int currentUser = mUserTracker.getUserId();
        if (currentUser != mCurrentUser) {
            mUserContext = mUserTracker.getUserContext();
            if (mAutoTiles != null) {
                mAutoTiles.changeUser(UserHandle.of(currentUser));
            }
        }
        // Do not process tiles if the flag is enabled.
        if (mFeatureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
            return;
        }
        if (newValue == null && UserManager.isDeviceInDemoMode(mContext)) {
            newValue = mContext.getResources().getString(R.string.quick_settings_tiles_retail_mode);
        }
        final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
        if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
        Log.d(TAG, "Recreating tiles: " + tileSpecs);
        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()) {
                    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");
                        }
                    } else {
                        Log.d(TAG, "No factory for a spec: " + tileSpec);
                    }
                } catch (Throwable t) {
                    Log.w(TAG, "Error creating tile for spec: " + tileSpec, t);
                }
            }
        }
       ........
    }

看这个loadTileSpecs

quick_settings_tiles:config.xml中的值是default

 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);
            Log.d(TAG, "Loaded tile specs from default config: " + tileList);
        } else {
            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 = QSHost.getDefaultSpecs(context.getResources());
                    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);
                }
            }
        }

        if (!tiles.contains("internet")) {
            if (tiles.contains("wifi")) {
                // Replace the WiFi with Internet, and remove the Cell
                tiles.set(tiles.indexOf("wifi"), "internet");
                tiles.remove("cell");
            } else if (tiles.contains("cell")) {
                // Replace the Cell with Internet
                tiles.set(tiles.indexOf("cell"), "internet");
            }
        } else {
            tiles.remove("wifi");
            tiles.remove("cell");
        }
        return tiles;
    }

QSHost

/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSHost.java

加载quick_settings_tiles_default是config下的配置文件,默认显示下拉状态栏的tiles

    /**
     * Returns the default QS tiles for the context.
     * @param res the resources to use to determine the default tiles
     * @return a list of specs of the default tiles
     */
    static List<String> getDefaultSpecs(Resources res) {
        final ArrayList<String> tiles = new ArrayList();

        final String defaultTileList = res.getString(R.string.quick_settings_tiles_default);

        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;
    }

这时候的数据已经加载完,接下来看创建Tile实例

    private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
    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;
            }
        }
        return null;
    }

!!!其实是QSFactoryImpl 去createTile!!!

经过bind自动注入到mTileMap中了,
这时候用quick_settings_tiles_default去跟mTileMap比较是否存在,存在就返回对应注入好的Tile实例对象

/** Inject FontScalingTile into tileMap in QSModule */
@Binds
@IntoMap
@StringKey(FontScalingTile.TILE_SPEC)
fun bindFontScalingTile(fontScalingTile: FontScalingTile): QSTileImpl<*>

@SysUISingleton
public class QSFactoryImpl implements QSFactory {

    private static final String TAG = "QSFactory";

    protected final Map<String, Provider<QSTileImpl<?>>> mTileMap;
    private final Lazy<QSHost> mQsHostLazy;
    private final Provider<CustomTile.Builder> mCustomTileBuilderProvider;

    @Inject
    public QSFactoryImpl(
            Lazy<QSHost> qsHostLazy,
            Provider<CustomTile.Builder> customTileBuilderProvider,
            Map<String, Provider<QSTileImpl<?>>> tileMap) {
        mQsHostLazy = qsHostLazy;
        mCustomTileBuilderProvider = customTileBuilderProvider;
        mTileMap = tileMap;
    }

    /** Creates a tile with a type based on {@code tileSpec} */
    @Nullable
    public final QSTile createTile(String tileSpec) {
        QSTileImpl tile = createTileInternal(tileSpec);
        if (tile != null) {
            tile.initialize();
            tile.postStale(); // Tile was just created, must be stale.
        }
        return tile;
    }

    @Nullable
    protected QSTileImpl createTileInternal(String tileSpec) {
        // Stock tiles.
        if (mTileMap.containsKey(tileSpec)
                // We should not return a Garbage Monitory Tile if the build is not Debuggable
                && (!tileSpec.equals(GarbageMonitor.MemoryTile.TILE_SPEC) || Build.IS_DEBUGGABLE)) {
            return mTileMap.get(tileSpec).get();
        }

        // Custom tiles
        if (tileSpec.startsWith(CustomTile.PREFIX)) {
            return CustomTile.create(
                    mCustomTileBuilderProvider.get(), tileSpec, mQsHostLazy.get().getUserContext());
        }

        // Broken tiles.
        Log.w(TAG, "No stock tile spec: " + tileSpec);
        return null;
    }

    @Override
    public QSTileView createTileView(Context context, QSTile tile, boolean collapsedView) {
        QSIconView icon = tile.createTileView(context);
        return new QSTileViewImpl(context, icon, collapsedView);
    }

最重要的这一步,获取到全部的数据缓存在mTiles中保存!!!

用户视图加载数据用!!!
public Collection getTiles() {return mTiles.values();}

  @MainThread
    @Override
    public void onTuningChanged(String key, String newValue) {

	......

		 mCurrentUser = currentUser;
        List<String> currentSpecs = new ArrayList<>(mTileSpecs);
        mTileSpecs.clear();
        mTileSpecs.addAll(newTiles.keySet()); // Only add the valid (available) tiles.
        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.");
            changeTilesByUser(currentSpecs, loadTileSpecs(mContext, ""));
        } else {
            String resolvedTiles = TextUtils.join(",", mTileSpecs);
            if (!resolvedTiles.equals(newValue)) {
                // If the resolved tiles (those we actually ended up with) are different than
                // the ones that are in the setting, update the Setting.
                // 用TILES_SETTING进行缓存下拉状态栏的数据
                saveTilesToSettings(mTileSpecs);
            }
            mTilesListDirty = false;
            for (int i = 0; i < mCallbacks.size(); i++) {
                mCallbacks.get(i).onTilesChanged();
            }
        }
}

接下来再看QSFragment 初始化,这时候数据类都已经加载好了,开始把数据加载进视图里面去了

mQSPanelController.init开始初始化,
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
    //start 这里调用完后数据已经加载完毕
    mQSPanelController = qsFragmentComponent.getQSPanelController();
    //end
    mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
	//数据加载进视图
    mQSPanelController.init();
    mQuickQSPanelController.init();

这个init()这里调用的其实是他的基类的init

/frameworks/base/packages/SystemUI/src/com/android/systemui/util/ViewController.java
这里其实是调用子类onViewAttached

 */
public abstract class ViewController<T extends View> {
    protected final T mView;
    private boolean mInited;

    private OnAttachStateChangeListener mOnAttachStateListener = new OnAttachStateChangeListener() {
        @Override
        public void onViewAttachedToWindow(View v) {
            ViewController.this.onViewAttached();
        }

        @Override
        public void onViewDetachedFromWindow(View v) {
            ViewController.this.onViewDetached();
        }
    };

    protected ViewController(T view) {
        mView = view;
    }

    /**
     * Call immediately after constructing Controller in order to handle view lifecycle events.
     *
     * Generally speaking, you don't want to override this method. Instead, override
     * {@link #onInit()} as a way to have an run-once idempotent method that you can use for
     * setup of your ViewController.
     */
    public void init() {
        if (mInited) {
            return;
        }
        onInit();
        mInited = true;

        if (isAttachedToWindow()) {
            mOnAttachStateListener.onViewAttachedToWindow(mView);
        }
        addOnAttachStateChangeListener(mOnAttachStateListener);
    }

/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java

这里是最重要的

这里获取QSTileHost.getTiles()的数据进行数据与视图绑定了
mView这个View是QSPanel,是QSPanelController这个类通过super传递给QSPanelControllerBase的
QSPanelController(QSPanel view, TunerService tunerService,....)
super(view,......)

    @Override
    protected void onViewAttached() {
        mQsTileRevealController = createTileRevealController();
        if (mQsTileRevealController != null) {
            mQsTileRevealController.setExpansion(mRevealExpansion);
        }

        mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
        mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
        mHost.addCallback(mQSHostCallback);
        setTiles();
        mLastOrientation = getResources().getConfiguration().orientation;
        mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
        switchTileLayout(true);

        mDumpManager.registerDumpable(mView.getDumpableTag(), this);
    }



    /** */
    public void setTiles() {
        setTiles(mHost.getTiles(), false);
    }

    /** */
    public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
        // TODO(b/168904199): move this logic into QSPanelController.
        if (!collapsedView && mQsTileRevealController != null) {
            mQsTileRevealController.updateRevealedTiles(tiles);
        }

        for (QSPanelControllerBase.TileRecord record : mRecords) {
            mView.removeTile(record);
            record.tile.removeCallback(record.callback);
        }
        mRecords.clear();
        mCachedSpecs = "";
        for (QSTile tile : tiles) {
            addTile(tile, collapsedView);
        }
    }

	     private void addTile(final QSTile tile, boolean collapsedView) {
	      final TileRecord r =
	              new TileRecord(tile, mHost.createTileView(getContext(), tile, collapsedView));
	      // TODO(b/250618218): Remove the QSLogger in QSTileViewImpl once we know the root cause of
	      // b/250618218.
	      try {
	          QSTileViewImpl qsTileView = (QSTileViewImpl) (r.tileView);
	          if (qsTileView != null) {
	              qsTileView.setQsLogger(mQSLogger);
	          }
	      } catch (ClassCastException e) {
	          Log.e(TAG, "Failed to cast QSTileView to QSTileViewImpl", e);
	      }
	      mView.addTile(r);
	      mRecords.add(r);
	      mCachedSpecs = getTilesSpecs();
	     }

QSPanel

mTileLayout 中添加addTile


    final void addTile(QSPanelControllerBase.TileRecord tileRecord) {
        final QSTile.Callback callback = new QSTile.Callback() {
            @Override
            public void onStateChanged(QSTile.State state) {
                drawTile(tileRecord, state);
            }
        };

        tileRecord.tile.addCallback(callback);
        tileRecord.callback = callback;
        tileRecord.tileView.init(tileRecord.tile);
        tileRecord.tile.refreshState();

        if (mTileLayout != null) {
        	
            mTileLayout.addTile(tileRecord);
        }
    }

TileLayout

    public void addTile(TileRecord tile) {
        mRecords.add(tile);
        tile.tile.setListening(this, mListening);
        addTileView(tile);
    }

    protected void addTileView(TileRecord tile) {
        addView(tile.tileView); 添加子view并刷新
    }

网站公告

今日签到

点亮在社区的每一天
去签到