Android14 QS编辑页面面板的加载解析
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
点击编辑按钮
mEditButton.setOnClickListener(view -> {if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {return;}mActivityStarter.postQSRunnableDismissingKeyguard(() -> mQsPanelController.showEdit(view));});
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
QsCustomizerController开始显示
/** Start customizing the Quick Settings. */public void showEdit(View view) {view.post(() -> {if (!mQsCustomizerController.isCustomizing()) {int[] loc = view.getLocationOnScreen();int x = loc[0] + view.getWidth() / 2;int y = loc[1] + view.getHeight() / 2;mQsCustomizerController.show(x, y, false);}});}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizerController.java
视图开始显示,mView是指QSCustomizer这个视图
@Injectprotected QSCustomizerController(QSCustomizer view, TileQueryHelper tileQueryHelper,QSHost qsHost, TileAdapter tileAdapter, ScreenLifecycle screenLifecycle,KeyguardStateController keyguardStateController, LightBarController lightBarController,ConfigurationController configurationController, UiEventLogger uiEventLogger) {super(view);mTileQueryHelper = tileQueryHelper;mQsHost = qsHost;mTileAdapter = tileAdapter;mScreenLifecycle = screenLifecycle;mKeyguardStateController = keyguardStateController;mLightBarController = lightBarController;mConfigurationController = configurationController;mUiEventLogger = uiEventLogger;mToolbar = mView.findViewById(com.android.internal.R.id.action_bar);}@Overrideprotected void onViewAttached() {mView.updateNavBackDrop(getResources().getConfiguration(), mLightBarController);mConfigurationController.addCallback(mConfigurationListener);//把mTileAdapter这个注册到mTileQueryHelper中,好方便加载完数据进行回调刷新viewmTileQueryHelper.setListener(mTileAdapter);......}public void show(int x, int y, boolean immediate) {if (!mView.isShown()) {setTileSpecs();if (immediate) {mView.showImmediately();} else {mView.show(x, y, mTileAdapter);mUiEventLogger.log(QSEditEvent.QS_EDIT_OPEN);}mTileQueryHelper.queryTiles(mQsHost);mKeyguardStateController.addCallback(mKeyguardCallback);mView.updateNavColors(mLightBarController);}}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
开始加载数据
public void queryTiles(QSHost host) {mTiles.clear();mSpecs.clear();mFinished = false;// Enqueue jobs to fetch every system tile and then ever package tile.addCurrentAndStockTiles(host);}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
主要是R.string.quick_settings_tiles_stock,加载编辑面板中的Stock数据
private void addCurrentAndStockTiles(QSHost host) {String stock = mContext.getString(R.string.quick_settings_tiles_stock);String current = Settings.Secure.getString(mContext.getContentResolver(),Settings.Secure.QS_TILES);final ArrayList<String> possibleTiles = new ArrayList<>();if (current != null) {// The setting QS_TILES is not populated immediately upon Factory ResetpossibleTiles.addAll(Arrays.asList(current.split(",")));} else {current = "";}String[] stockSplit = stock.split(",");for (String spec : stockSplit) {if (!current.contains(spec)) {possibleTiles.add(spec);}}if (Build.IS_DEBUGGABLE && !current.contains(GarbageMonitor.MemoryTile.TILE_SPEC)) {possibleTiles.add(GarbageMonitor.MemoryTile.TILE_SPEC);}final ArrayList<QSTile> tilesToAdd = new ArrayList<>();possibleTiles.remove("cell");possibleTiles.remove("wifi");for (String spec : possibleTiles) {// Only add current and stock tiles that can be created from QSFactoryImpl.// Do not include CustomTile. Those will be created by `addPackageTiles`.if (spec.startsWith(CustomTile.PREFIX)) continue;final QSTile tile = host.createTile(spec);if (tile == null) {continue;} else if (!tile.isAvailable()) {tile.setTileSpec(spec);tile.destroy();continue;}tile.setTileSpec(spec);tilesToAdd.add(tile);}new TileCollector(tilesToAdd, host).startListening();}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
实现监听QSTile的状态变化(观察者模式)
这里主要看这个地方:pair.mTile.refreshState()
实现了QSTile.Callback回调private class TileCollector implements QSTile.Callback {private final List<TilePair> mQSTileList = new ArrayList<>();private final QSHost mQSHost;TileCollector(List<QSTile> tilesToAdd, QSHost host) {for (QSTile tile: tilesToAdd) {//包装一下TilePair pair = new TilePair(tile);mQSTileList.add(pair);}mQSHost = host;if (tilesToAdd.isEmpty()) {mBgExecutor.execute(this::finished);}}private void finished() {notifyTilesChanged(false);addPackageTiles(mQSHost);}//注册每个Tile的Callback回调和Listening回调,使得TileCollector作为监听者加入观察每个Tile的改变private void startListening() {for (TilePair pair: mQSTileList) {pair.mTile.addCallback(this);pair.mTile.setListening(this, true);// Make sure that at least one refresh state happenspair.mTile.refreshState();}}// This is called in the Bg thread@Overridepublic void onStateChanged(State s) {boolean allReady = true;for (TilePair pair: mQSTileList) {if (!pair.mReady && pair.mTile.isTileReady()) {pair.mTile.removeCallback(this);pair.mTile.setListening(this, false);pair.mReady = true;} else if (!pair.mReady) {allReady = false;}}if (allReady) {for (TilePair pair : mQSTileList) {QSTile tile = pair.mTile;final QSTile.State state = tile.getState().copy();// Ignore the current state and get the generic label instead.state.label = tile.getTileLabel();tile.destroy();addTile(tile.getTileSpec(), null, state, true);}finished();}}}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
每个Tile进行刷新自己的状态
//第二步:进行状态刷新回调@Overridepublic void handleMessage(Message msg) {String name = null;try {......if (msg.what == REFRESH_STATE) {name = "handleRefreshState";handleRefreshState(msg.obj);}......}//第一步:发送Handler更新信息protected final void refreshState(@Nullable Object arg) {mHandler.obtainMessage(H.REFRESH_STATE, arg).sendToTarget();}//第三步:处理刷新逻辑protected final void handleRefreshState(@Nullable Object arg) {handleUpdateState(mTmpState, arg);boolean changed = mTmpState.copyTo(mState);if (mReadyState == READY_STATE_READYING) {mReadyState = READY_STATE_READY;changed = true;}if (changed) {mQSLogger.logTileUpdated(mTileSpec, mState);handleStateChanged();}mHandler.removeMessages(H.STALE);mHandler.sendEmptyMessageDelayed(H.STALE, getStaleTimeout());setListening(mStaleListener, false);}//第四步:这里触发状态改变回调private void handleStateChanged() {if (mCallbacks.size() != 0) {for (int i = 0; i < mCallbacks.size(); i++) {mCallbacks.get(i).onStateChanged(mState);}}}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
这里进行触发onStateChanged,遍历所有 tiles 都已就绪,如果已就绪,就添加到集合中进行存储,然后这时候就开始加载第三方的Tiles
// This is called in the Bg thread
@Override
public void onStateChanged(State s) {boolean allReady = true; // 标记是否所有 tiles 都已就绪for (TilePair pair : mQSTileList) { // 遍历所有待收集的 TilePair(包含 QSTile 和就绪状态)if (!pair.mReady && pair.mTile.isTileReady()) { // 如果 tile 未标记就绪,且实际已就绪pair.mTile.removeCallback(this); // 移除当前回调(避免重复监听)pair.mTile.setListening(this, false); // 停止监听该 tile 的状态变化pair.mReady = true; // 标记该 tile 为已就绪} else if (!pair.mReady) { // 如果 tile 未就绪allReady = false; // 则所有 tiles 未全部就绪}}if (allReady) { // 当所有 tiles 都就绪后for (TilePair pair : mQSTileList) { // 遍历所有就绪的 tilesQSTile tile = pair.mTile;final QSTile.State state = tile.getState().copy(); // 复制 tile 的当前状态(如图标、标签等)// Ignore the current state and get the generic label instead.state.label = tile.getTileLabel(); // 覆盖状态中的标签为 tile 的通用标签(忽略动态状态标签)tile.destroy(); // 销毁 tile 实例(释放资源,避免内存泄漏)addTile(tile.getTileSpec(), null, state, true); // 将 tile 信息添加到全局列表(标记为系统 tile)}finished(); // 触发完成逻辑(通知监听器并继续处理第三方应用 tiles)}
}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
addPackageTiles 这里是添加第三方的Tiles
private void finished() {notifyTilesChanged(false); //这里还未将数据绑定到视图中addPackageTiles(mQSHost);}
private void addPackageTiles(final QSHost host) {mBgExecutor.execute(() -> {Collection<QSTile> params = host.getTiles();PackageManager pm = mContext.getPackageManager();// 查询所有声明了 TileService.ACTION_QS_TILE 的服务(即第三方 QS tile 服务)List<ResolveInfo> services = pm.queryIntentServicesAsUser(new Intent(TileService.ACTION_QS_TILE), 0, mUserTracker.getUserId());String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);for (ResolveInfo info : services) {String packageName = info.serviceInfo.packageName;ComponentName componentName = new ComponentName(packageName, info.serviceInfo.name);// Don't include apps that are a part of the default tile set.// 跳过系统默认 tile(已在 stockTiles 中的 tile 不重复添加)if (stockTiles.contains(componentName.flattenToString())) {continue;}final CharSequence appLabel = info.serviceInfo.applicationInfo.loadLabel(pm);String spec = CustomTile.toSpec(componentName);State state = getState(params, spec);if (state != null) {addTile(spec, appLabel, state, false);continue;}if (info.serviceInfo.icon == 0 && info.serviceInfo.applicationInfo.icon == 0) {continue;}Drawable icon = info.serviceInfo.loadIcon(pm);if (!permission.BIND_QUICK_SETTINGS_TILE.equals(info.serviceInfo.permission)) {continue;}if (icon == null) {continue;}icon.mutate();icon.setTint(mContext.getColor(android.R.color.white));CharSequence label = info.serviceInfo.loadLabel(pm);createStateAndAddTile(spec, icon, label != null ? label.toString() : "null",appLabel);}notifyTilesChanged(true); // 通知监听器:第三方 tile 加载完成,可更新 UI});}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
这里开始将数据绑定到视图中,这个mListener是指mTileAdapter
private void notifyTilesChanged(final boolean finished) {final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles);mMainExecutor.execute(() -> {if (mListener != null) {mListener.onTilesChanged(tilesToReturn);}mFinished = finished;});}
/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
数据绑定进视图进行显示
@Overridepublic void onTilesChanged(List<TileInfo> tiles) {mAllTiles = tiles;recalcSpecs();}private void recalcSpecs() {if (mCurrentSpecs == null || mAllTiles == null) {return;}mOtherTiles = new ArrayList<TileInfo>(mAllTiles);mTiles.clear();mTiles.add(null);for (int i = 0; i < mCurrentSpecs.size(); i++) {final TileInfo tile = getAndRemoveOther(mCurrentSpecs.get(i));if (tile != null) {mTiles.add(tile);}}mTiles.add(null);for (int i = 0; i < mOtherTiles.size(); i++) {final TileInfo tile = mOtherTiles.get(i);if (tile.isSystem) {mOtherTiles.remove(i--);mTiles.add(tile);}}mTileDividerIndex = mTiles.size();mTiles.add(null);mTiles.addAll(mOtherTiles);updateDividerLocations();notifyDataSetChanged();}