/frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
QS 面板的创建
- getNotificationShadeWindowView():整个systemui的最顶级的视图容器(super_notification_shade.xml)
- R.id.qs_frame : 是整个QS 面板的容器视图
- 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并刷新
}