Setting 主界面加载以及外部注入

Setting 主界面加载以及外部注入

DashboardSummary DashboardFeatureProviderImpl CategoryManager TileUtils onCreateView rebuildUI() updateCategory getTilesForCategory getTilesByCategory tryInitCategories getCategories getTilesForAction getTilesForIntent categories mCategoryByKeyMap DashboardSummary DashboardFeatureProviderImpl CategoryManager TileUtils

Settings的入口是SettingsActivity

 protected void onCreate(Bundle savedState) {
 	...
	 if (savedState != null) {
// We are restarting from a previous saved state; used that to initialize, instead
280            // of starting fresh.
281            setTitleFromIntent(intent);
282
283            ArrayList<DashboardCategory> categories =
284                    savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
285            if (categories != null) {
286                mCategories.clear();
287                mCategories.addAll(categories);
288                setTitleFromBackStack();
289            }
290        } else {
291            launchSettingFragment(initialFragmentName, isSubSettings, intent);
292        }
	...
 }

我们看到SettingActivity的onCreate函数,初始化时savedState为空,所以我们直接看到launchSettingFragment。

382    void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
383        if (!mIsShowingDashboard && initialFragmentName != null) {
384            setTitleFromIntent(intent);
385
386            Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
387            switchToFragment(initialFragmentName, initialArguments, true, false,
388                    mInitialTitleResId, mInitialTitle, false);
389        } else {
390            // Show search icon as up affordance if we are displaying the main Dashboard
391            mInitialTitleResId = R.string.dashboard_title;
392
393            switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
394                    mInitialTitleResId, mInitialTitle, false);
395        }
396    }

switchTofragment会调用dashboardSummary这个类,我们直接看到DashboardSummary的 onCreateView方法。

196    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
197        long startTime = System.currentTimeMillis();
198        final View root = inflater.inflate(R.layout.dashboard, container, false);
199        mDashboard = root.findViewById(R.id.dashboard_container);
200        mLayoutManager = new LinearLayoutManager(getContext());
201        mLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
202        if (bundle != null) {
203            int scrollPosition = bundle.getInt(STATE_SCROLL_POSITION);
204            mLayoutManager.scrollToPosition(scrollPosition);
205        }
206        mDashboard.setLayoutManager(mLayoutManager);
207        mDashboard.setHasFixedSize(true);
208        mDashboard.setListener(this);
209        mDashboard.setItemAnimator(new DashboardItemAnimator());
210        mAdapter = new DashboardAdapter(getContext(), bundle,
211                mConditionManager.getConditions(), mSuggestionControllerMixin, getLifecycle());
212        mDashboard.setAdapter(mAdapter);
213        mSummaryLoader.setSummaryConsumer(mAdapter);
214        ActionBarShadowController.attachToRecyclerView(
215                getActivity().findViewById(R.id.search_bar_container), getLifecycle(), mDashboard);
216        rebuildUI();
217        if (DEBUG_TIMING) {
218            Log.d(TAG, "onCreateView took "
219                    + (System.currentTimeMillis() - startTime) + " ms");
220        }
221        return root;
222    }

这里为fragment设置一些基础属性,像监听器、适配器等。而数据的核心加载在rebuildUI处。

225    void rebuildUI() {
226        ThreadUtils.postOnBackgroundThread(() -> updateCategory());
227    }
		......
273    void updateCategory() {
274        final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
275                CategoryKey.CATEGORY_HOMEPAGE);
276        mSummaryLoader.updateSummaryToCache(category);
277        mStagingCategory = category;
278        if (mSuggestionControllerMixin == null) {
279            ThreadUtils.postOnMainThread(() -> mAdapter.setCategory(mStagingCategory));
280            return;
281        }
282        if (mSuggestionControllerMixin.isSuggestionLoaded()) {
283            Log.d(TAG, "Suggestion has loaded, setting suggestion/category");
284            ThreadUtils.postOnMainThread(() -> {
285                if (mStagingSuggestions != null) {
286                    mAdapter.setSuggestions(mStagingSuggestions);
287                }
288                mAdapter.setCategory(mStagingCategory);
289            });
290        } else {
291            Log.d(TAG, "Suggestion NOT loaded, delaying setCategory by " + MAX_WAIT_MILLIS + "ms");
292            mHandler.postDelayed(() -> mAdapter.setCategory(mStagingCategory), MAX_WAIT_MILLIS);
293        }
294    }
295}

我们先看一下CATEGORY_HOMEPAGE的值:

public static final String CATEGORY_HOMEPAGE = "com.android.settings.category.ia.homepage"; 

这是activity的meta-data中的值,settings会根据此字段筛选出要显示到主界面的activity。
getTilesByCategory方法我们先等等再看,直接来到CategoryManager#tryInitCategories

120    private synchronized void tryInitCategories(Context context, boolean forceClearCache,
121            String settingPkg) {
122        if (mCategories == null) {
123            if (forceClearCache) {
124                mTileByComponentCache.clear();
125            }
126            mCategoryByKeyMap.clear();
127            mCategories = TileUtils.getCategories(context, mTileByComponentCache,
128                    false /* categoryDefinedInManifest */, mExtraAction, settingPkg);
129            for (DashboardCategory category : mCategories) {
130                mCategoryByKeyMap.put(category.key, category);
131            }
132            backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
133            sortCategories(context, mCategoryByKeyMap);
134            filterDuplicateTiles(mCategoryByKeyMap);
135        }
136    }

我们返回到dashboardsummary的就是这个mCategoryByKeyMap,所以我们直接看TileUtils#getCategories这个方法就行了。

215    public static List<DashboardCategory> getCategories(Context context,
216            Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest,
217            String extraAction, String settingPkg) {
218        final long startTime = System.currentTimeMillis();
219        boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
220                != 0;
221        ArrayList<Tile> tiles = new ArrayList<>();
222        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
223        for (UserHandle user : userManager.getUserProfiles()) {
224            // TODO: Needs much optimization, too many PM queries going on here.
225            if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
226                // Only add Settings for this user.
227                getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
228                        settingPkg);
229                getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
230                        OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
231                getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
232                        MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
233            }
234            if (setup) {
235                getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
236                        settingPkg);
237                if (!categoryDefinedInManifest) {
238                    getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
239                            settingPkg);
240                    if (extraAction != null) {
241                        getTilesForAction(context, user, extraAction, cache, null, tiles, false,
242                                settingPkg);
243                    }
244                }
245            }
246        }
247
248        HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
249        for (Tile tile : tiles) {
250            DashboardCategory category = categoryMap.get(tile.category);
251            if (category == null) {
252                category = createCategory(context, tile.category, categoryDefinedInManifest);
253                if (category == null) {
254                    Log.w(LOG_TAG, "Couldn't find category " + tile.category);
255                    continue;
256                }
257                categoryMap.put(category.key, category);
258            }
259            category.addTile(tile);
260        }
261        ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
262        for (DashboardCategory category : categories) {
263            category.sortTiles();
264        }
265        Collections.sort(categories, CATEGORY_COMPARATOR);
266        if (DEBUG_TIMING) Log.d(LOG_TAG, "getCategories took "
267                + (System.currentTimeMillis() - startTime) + " ms");
268        return categories;
269    }

我们首先会根据activity的action进行筛选,下列是筛选条件的值:

68    public static final String EXTRA_SETTINGS_ACTION =
69            "com.android.settings.action.EXTRA_SETTINGS";
74    private static final String IA_SETTINGS_ACTION =
75            "com.android.settings.action.IA_SETTINGS";
81    private static final String SETTINGS_ACTION =
82            "com.android.settings.action.SETTINGS";
84    private static final String OPERATOR_SETTINGS =
85            "com.android.settings.OPERATOR_APPLICATION_SETTING";
90    private static final String MANUFACTURER_SETTINGS =
91            "com.android.settings.MANUFACTURER_APPLICATION_SETTING";
92

Settings里面的activity基本上用的是SETTINGS,如果要外部注入的话我们一般用EXTRA_SETTINGS。traceur用的是IA_SETTINGS,不过我还没研究过具体区别。接下来我们直接看到getTilesForIntent方法:


    public static void getTilesForIntent(
334            Context context, UserHandle user, Intent intent,
335            Map<Pair<String, String>, Tile> addedCache, String defaultCategory, List<Tile> outTiles,
336            boolean usePriority, boolean checkCategory, boolean forceTintExternalIcon,
337            boolean shouldUpdateTiles) {
338        PackageManager pm = context.getPackageManager();
339        List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
340                PackageManager.GET_META_DATA, user.getIdentifier());
341        Map<String, IContentProvider> providerMap = new HashMap<>();
342        for (ResolveInfo resolved : results) {
343            if (!resolved.system) {
344                // Do not allow any app to add to settings, only system ones.
345                continue;
346            }
347            ActivityInfo activityInfo = resolved.activityInfo;
348            Bundle metaData = activityInfo.metaData;
349            String categoryKey = defaultCategory;
350
351            // Load category
352            if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
353                    && categoryKey == null) {
354                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
355                        + intent + " missing metadata "
356                        + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
357                continue;
358            } else {
359                categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
360            }
361
362            Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
363                    activityInfo.name);
364            Tile tile = addedCache.get(key);
365            if (tile == null) {
366                tile = new Tile();
367                tile.intent = new Intent().setClassName(
368                        activityInfo.packageName, activityInfo.name);
369                tile.category = categoryKey;
370                tile.priority = usePriority ? resolved.priority : 0;
371                tile.metaData = activityInfo.metaData;
372                updateTileData(context, tile, activityInfo, activityInfo.applicationInfo,
373                        pm, providerMap, forceTintExternalIcon);
374                if (DEBUG) Log.d(LOG_TAG, "Adding tile " + tile.title);
375                addedCache.put(key, tile);
376            } else if (shouldUpdateTiles) {
377                updateSummaryAndTitle(context, providerMap, tile);
378            }
379
380            if (!tile.userHandle.contains(user)) {
381                tile.userHandle.add(user);
382            }
383            if (!outTiles.contains(tile)) {
384                outTiles.add(tile);
385            }
386        }
387    }

我们首先通过pms获取所有包的信息。

338        PackageManager pm = context.getPackageManager();
339        List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,
340                PackageManager.GET_META_DATA, user.getIdentifier());

settings只接受系统应用外部注入到settings中

343            if (!resolved.system) {
344                // Do not allow any app to add to settings, only system ones.
345                continue;
346            }

我们接着往下看

352            if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
353                    && categoryKey == null) {
354                Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
355                        + intent + " missing metadata "
356                        + (metaData == null ? "" : EXTRA_CATEGORY_KEY));
357                continue;
358            } else {
359                categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
360            }
105    private static final String EXTRA_CATEGORY_KEY = "com.android.settings.category";

当metadata中必须有 "com.android.settings.category"时,才会将该activity存进map中。后面是给tile设置一些基本属性,这里我们注意需要注意一下的是设置优先级的位置:

tile.priority = usePriority ? resolved.priority : 0;

这个我们需要回到最开始筛选action时:

225            if (user.getIdentifier() == ActivityManager.getCurrentUser()) {
226                // Only add Settings for this user.
227                getTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, true,
228                        settingPkg);
229                getTilesForAction(context, user, OPERATOR_SETTINGS, cache,
230                        OPERATOR_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
231                getTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,
232                        MANUFACTURER_DEFAULT_CATEGORY, tiles, false, true, settingPkg);
233            }
234            if (setup) {
235                getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false,
236                        settingPkg);
237                if (!categoryDefinedInManifest) {
238                    getTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, false,
239                            settingPkg);
240                    if (extraAction != null) {
241                        getTilesForAction(context, user, extraAction, cache, null, tiles, false,
242                                settingPkg);
243                    }
244                }
245            }
246        }

我们看倒数第二个参数就行了,外部注入的usePriority都是false。所以外部注入的应用的优先级都为0,排在界面最下面。
回到getCategories方法:

248        HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
249        for (Tile tile : tiles) {
250            DashboardCategory category = categoryMap.get(tile.category);
251            if (category == null) {
252                category = createCategory(context, tile.category, categoryDefinedInManifest);
253                if (category == null) {
254                    Log.w(LOG_TAG, "Couldn't find category " + tile.category);
255                    continue;
256                }
257                categoryMap.put(category.key, category);
258            }
259            category.addTile(tile);
260        }
261        ArrayList<DashboardCategory> categories = new ArrayList<>(categoryMap.values());
262        for (DashboardCategory category : categories) {
263            category.sortTiles();
264        }
265        Collections.sort(categories, CATEGORY_COMPARATOR);

这里将我们刚刚获得到的tiles装到category中,然后对categories从大到小排列。

我们回到跳过没看的getTilesByCategory方法。

76    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey,
77            String settingPkg) {
78        tryInitCategories(context, settingPkg);
79
80        return mCategoryByKeyMap.get(categoryKey);
81    }

这里会通过"com.android.settings.category.ia.homepage"去查询我们刚刚插入的mCategoryByKeyMap。

主界面的加载到这就差不多了。

二级界面以及外部注入

我们接着getTilesByCategory继续讲一下,我们的子界面外部注入加载流程和主界面其实是一样的,都是通过这个接口,只不过使用的categoryKey不一样。子界面是通过DashboardFragment来管理的。
DashboardFragment#refreshDashboardTiles

333    void refreshDashboardTiles(final String TAG) {
334        final PreferenceScreen screen = getPreferenceScreen();
335
336        final DashboardCategory category =
337                mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());
			......
399		}
233    public String getCategoryKey() {
234        return DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP.get(getClass().getName());
235    }

这里通过类名获取对应的category值

64        PARENT_TO_CATEGORY_KEY_MAP.put(
65                NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
66        PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
67                CategoryKey.CATEGORY_CONNECT);
68        PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(),
69                CategoryKey.CATEGORY_DEVICE);
70        PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(),
71                CategoryKey.CATEGORY_APPS);
72        PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(),
73                CategoryKey.CATEGORY_BATTERY);
74        PARENT_TO_CATEGORY_KEY_MAP.put(DefaultAppSettings.class.getName(),
75                CategoryKey.CATEGORY_APPS_DEFAULT);
76        PARENT_TO_CATEGORY_KEY_MAP.put(DisplaySettings.class.getName(),
77                CategoryKey.CATEGORY_DISPLAY);
78        PARENT_TO_CATEGORY_KEY_MAP.put(SoundSettings.class.getName(),
79                CategoryKey.CATEGORY_SOUND);
80        PARENT_TO_CATEGORY_KEY_MAP.put(StorageDashboardFragment.class.getName(),
81                CategoryKey.CATEGORY_STORAGE);
82        PARENT_TO_CATEGORY_KEY_MAP.put(SecuritySettings.class.getName(),
83                CategoryKey.CATEGORY_SECURITY);
84        PARENT_TO_CATEGORY_KEY_MAP.put(AccountDetailDashboardFragment.class.getName(),
85                CategoryKey.CATEGORY_ACCOUNT_DETAIL);
86        PARENT_TO_CATEGORY_KEY_MAP.put(AccountDashboardFragment.class.getName(),
87                CategoryKey.CATEGORY_ACCOUNT);
88        PARENT_TO_CATEGORY_KEY_MAP.put(
89                SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM);
90        PARENT_TO_CATEGORY_KEY_MAP.put(LanguageAndInputSettings.class.getName(),
91                CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
92        PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettingsDashboardFragment.class.getName(),
93                CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
94        PARENT_TO_CATEGORY_KEY_MAP.put(ConfigureNotificationSettings.class.getName(),
95                CategoryKey.CATEGORY_NOTIFICATIONS);
96        PARENT_TO_CATEGORY_KEY_MAP.put(LockscreenDashboardFragment.class.getName(),
97                CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
98        PARENT_TO_CATEGORY_KEY_MAP.put(ZenModeSettings.class.getName(),
99                CategoryKey.CATEGORY_DO_NOT_DISTURB);
100        PARENT_TO_CATEGORY_KEY_MAP.put(GestureSettings.class.getName(),
101            CategoryKey.CATEGORY_GESTURES);
102        PARENT_TO_CATEGORY_KEY_MAP.put(NightDisplaySettings.class.getName(),
103            CategoryKey.CATEGORY_NIGHT_DISPLAY);

这里是不同的子页面的category

24    public static final String CATEGORY_HOMEPAGE = "com.android.settings.category.ia.homepage"; 
25
26    // Top level category.
27    public static final String CATEGORY_NETWORK = "com.android.settings.category.ia.wireless";
28    public static final String CATEGORY_CONNECT = "com.android.settings.category.ia.connect";
29    public static final String CATEGORY_DEVICE = "com.android.settings.category.ia.device";
30    public static final String CATEGORY_APPS = "com.android.settings.category.ia.apps";
31    public static final String CATEGORY_APPS_DEFAULT =
32            "com.android.settings.category.ia.apps.default";
33    public static final String CATEGORY_BATTERY = "com.android.settings.category.ia.battery";
34    public static final String CATEGORY_DISPLAY = "com.android.settings.category.ia.display";
35    public static final String CATEGORY_SOUND = "com.android.settings.category.ia.sound";
36    public static final String CATEGORY_STORAGE = "com.android.settings.category.ia.storage";
37    public static final String CATEGORY_SECURITY = "com.android.settings.category.ia.security";
38    public static final String CATEGORY_SECURITY_LOCKSCREEN =
39            "com.android.settings.category.ia.lockscreen";
40    public static final String CATEGORY_ACCOUNT = "com.android.settings.category.ia.accounts";
41    public static final String CATEGORY_ACCOUNT_DETAIL =
42            "com.android.settings.category.ia.account_detail";
43    public static final String CATEGORY_SYSTEM = "com.android.settings.category.ia.system";
44    public static final String CATEGORY_SYSTEM_LANGUAGE =
45            "com.android.settings.category.ia.language";
46    public static final String CATEGORY_SYSTEM_DEVELOPMENT =
47            "com.android.settings.category.ia.development";
48    public static final String CATEGORY_NOTIFICATIONS =
49            "com.android.settings.category.ia.notifications";
50    public static final String CATEGORY_DO_NOT_DISTURB = "com.android.settings.category.ia.dnd";
51    public static final String CATEGORY_GESTURES = "com.android.settings.category.ia.gestures";
52    public static final String CATEGORY_NIGHT_DISPLAY =
53            "com.android.settings.category.ia.night_display";
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值