/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这个视图
@Inject
protected 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);
}
@Override
protected void onViewAttached() {
mView.updateNavBackDrop(getResources().getConfiguration(), mLightBarController);
mConfigurationController.addCallback(mConfigurationListener);
//把mTileAdapter这个注册到mTileQueryHelper中,好方便加载完数据进行回调刷新view
mTileQueryHelper.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 Reset
possibleTiles.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 happens
pair.mTile.refreshState();
}
}
// This is called in the Bg thread
@Override
public 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进行刷新自己的状态
//第二步:进行状态刷新回调
@Override
public 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) { // 遍历所有就绪的 tiles
QSTile 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
数据绑定进视图进行显示
@Override
public 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();
}