Settings中主界面加载流程(一级菜单 动态加载)

Settings中主界面加载流程(一级菜单 动态加载)

DashboardFragment中的refreshAllPreferences

这个方法中加载了refreshDashboardTiles(tag);方法,此方法就是动态加载

private void refreshAllPreferences(final String tag) {  
Log.d("wuzhangxiao", "wuzhangxiao:   refreshAllPreferences "+tag);   
//这个在oncreate前执行   
、、、、、、、、、、、省略代码
refreshDashboardTiles(tag);
refreshDashboardTiles
private void refreshDashboardTiles(final String tag) {    Log.d("wuzhangxiao", "data  : refreshDashboardTiles"+tag);    final PreferenceScreen screen = getPreferenceScreen();    final DashboardCategory category =            mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());

去看DashboardFragmentRegistry

DashboardFragmentRegistry
/**
 * A registry to keep track of which page hosts which category.
 */
public class DashboardFragmentRegistry {

    /**
     * Map from parent fragment to category key. The parent fragment hosts child with
     * category_key.
     */
    public static final Map<String, String> PARENT_TO_CATEGORY_KEY_MAP;

    /**
     * Map from category_key to parent. This is a helper to look up which fragment hosts the
     * category_key.
     */
    public static final Map<String, String> CATEGORY_KEY_TO_PARENT_MAP;

    static {
        PARENT_TO_CATEGORY_KEY_MAP = new ArrayMap<>();
        PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
                CategoryKey.CATEGORY_HOMEPAGE);
        PARENT_TO_CATEGORY_KEY_MAP.put(
                NetworkDashboardFragment.class.getName(), CategoryKey.CATEGORY_NETWORK);
        PARENT_TO_CATEGORY_KEY_MAP.put(ConnectedDeviceDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_CONNECT);
        PARENT_TO_CATEGORY_KEY_MAP.put(AdvancedConnectedDeviceDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_DEVICE);
        PARENT_TO_CATEGORY_KEY_MAP.put(AppAndNotificationDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_APPS);
        PARENT_TO_CATEGORY_KEY_MAP.put(PowerUsageSummary.class.getName(),
                CategoryKey.CATEGORY_BATTERY);
        PARENT_TO_CATEGORY_KEY_MAP.put(DisplaySettings.class.getName(),
                CategoryKey.CATEGORY_DISPLAY);
        PARENT_TO_CATEGORY_KEY_MAP.put(SoundSettings.class.getName(),
                CategoryKey.CATEGORY_SOUND);
        PARENT_TO_CATEGORY_KEY_MAP.put(StorageDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_STORAGE);
        PARENT_TO_CATEGORY_KEY_MAP.put(SecuritySettings.class.getName(),
                CategoryKey.CATEGORY_SECURITY);
        PARENT_TO_CATEGORY_KEY_MAP.put(AccountDetailDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_ACCOUNT_DETAIL);
        PARENT_TO_CATEGORY_KEY_MAP.put(AccountDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_ACCOUNT);
        PARENT_TO_CATEGORY_KEY_MAP.put(
                SystemDashboardFragment.class.getName(), CategoryKey.CATEGORY_SYSTEM);
        PARENT_TO_CATEGORY_KEY_MAP.put(LanguageAndInputSettings.class.getName(),
                CategoryKey.CATEGORY_SYSTEM_LANGUAGE);
        PARENT_TO_CATEGORY_KEY_MAP.put(DevelopmentSettingsDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_SYSTEM_DEVELOPMENT);
        PARENT_TO_CATEGORY_KEY_MAP.put(ConfigureNotificationSettings.class.getName(),
                CategoryKey.CATEGORY_NOTIFICATIONS);
        PARENT_TO_CATEGORY_KEY_MAP.put(LockscreenDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_SECURITY_LOCKSCREEN);
        PARENT_TO_CATEGORY_KEY_MAP.put(ZenModeSettings.class.getName(),
                CategoryKey.CATEGORY_DO_NOT_DISTURB);
        PARENT_TO_CATEGORY_KEY_MAP.put(GestureSettings.class.getName(),
                CategoryKey.CATEGORY_GESTURES);
        PARENT_TO_CATEGORY_KEY_MAP.put(NightDisplaySettings.class.getName(),
                CategoryKey.CATEGORY_NIGHT_DISPLAY);
        PARENT_TO_CATEGORY_KEY_MAP.put(PrivacyDashboardFragment.class.getName(),
                CategoryKey.CATEGORY_PRIVACY);
        PARENT_TO_CATEGORY_KEY_MAP.put(EnterprisePrivacySettings.class.getName(),
                CategoryKey.CATEGORY_ENTERPRISE_PRIVACY);
        PARENT_TO_CATEGORY_KEY_MAP.put(LegalSettings.class.getName(),
                CategoryKey.CATEGORY_ABOUT_LEGAL);
        PARENT_TO_CATEGORY_KEY_MAP.put(MyDeviceInfoFragment.class.getName(),
                CategoryKey.CATEGORY_MY_DEVICE_INFO);
        PARENT_TO_CATEGORY_KEY_MAP.put(BatterySaverSettings.class.getName(),
                CategoryKey.CATEGORY_BATTERY_SAVER_SETTINGS);

        CATEGORY_KEY_TO_PARENT_MAP = new ArrayMap<>(PARENT_TO_CATEGORY_KEY_MAP.size());

        for (Map.Entry<String, String> parentToKey : PARENT_TO_CATEGORY_KEY_MAP.entrySet()) {
            CATEGORY_KEY_TO_PARENT_MAP.put(parentToKey.getValue(), parentToKey.getKey());
        }
    }
}

这个就是一个注册表的作用,注册记录什么界面(fragment)使用哪一个host去进行相应动态索引加载。
我们可以看见主界面的fragment为TopLeverSettings.java,相应的CategoryKey就是

  PARENT_TO_CATEGORY_KEY_MAP.put(TopLevelSettings.class.getName(),
                CategoryKey.CATEGORY_HOMEPAGE);
// Activities in this category shows up in Settings homepage.
public static final String CATEGORY_HOMEPAGE = "com.android.settings.category.ia.homepage";

1可以看到主界面动态加载关键字应是"com.android.settings.category.ia.homepage"。
再去查看getCategoryKey方法

@VisibleForTestingpublic String getCategoryKey() {    //返回CategoryKey中的数据key    return 
DashboardFragmentRegistry.PARENT_TO_CATEGORY_KEY_MAP.get(getClass().getName());}

此方法是获取相关fragment的CategoryKey用于动态加载,根据上面分析主界面是TopLevelSettings.java,故而key为"com.android.settings.category.ia.homepage"。

2、继续看getTilesForCategory方法(),具体实现是在DashboardFeatureProviderImpl.java中:

DashboardFeatureProviderImpl
@Override
public DashboardCategory getTilesForCategory(String key) {
    return mCategoryManager.getTilesByCategory(mContext, key);
}//

packages/apps/Settings/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java
CategoryManager.java的getTilesByCategory()方法:

CategoryManager
public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
    tryInitCategories(context);

    return mCategoryByKeyMap.get(categoryKey);
}

可以看到方法返回值是通过关键字key(“com.android.settings.category.ia.homepage”)去map集合中索引返回DashboardCategory的对象,故tryInitCategories()方法肯定是存在加载然后对map赋值的操作。直接看tryInitCategories()方法:

CategoryManager
public synchronized DashboardCategory getTilesByCategory(Context context, String 
categoryKey) {   
tryInitCategories(context);   
return mCategoryByKeyMap.get(categoryKey);}

private synchronized void tryInitCategories(Context context) {   
// Keep cached tiles by default. The cache is only invalidated when 
InterestingConfigChange   
// happens.   
tryInitCategories(context, false /* forceClearCache */);}


    private synchronized void tryInitCategories(Context context, boolean forceClearCache) {
        if (mCategories == null) {
            if (forceClearCache) {
                mTileByComponentCache.clear();
            }
            mCategoryByKeyMap.clear();
            mCategories = TileUtils.getCategories(context, mTileByComponentCache);
            for (DashboardCategory category : mCategories) {
                mCategoryByKeyMap.put(category.key, category);
            }
            backwardCompatCleanupForCategory(mTileByComponentCache, mCategoryByKeyMap);
            sortCategories(context, mCategoryByKeyMap);
            filterDuplicateTiles(mCategoryByKeyMap);
        }
    }

此方法的作用是
1、首先清空mCategoryByKeyMap集合;
2、调用getCategories()方法,去查询构建DashboardCategory的list列表;
3、遍历list填充mCategoryByKeyMap集合;
4、检查是否使用旧的category keys,如果是,使用最新的category keys去替换;
5、排序;
6、去掉category中重复的tiles。
我可以看见private List<DashboardCategory> mCategories;mCategories = TileUtils.getCategories(context, mTileByComponentCache);这个是获取相关的数据,是读取TileUtils.java,此文件在
frameworks/base/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java

  /** * Build a list of DashboardCategory. */
  public static List<DashboardCategory> getCategories(Context context,        Map<Pair<String, String>, Tile> cache) {  
  final long startTime = System.currentTimeMillis();  
  //global.DEVICE_PROVISIONED是要检索的设置的名称。  
  final boolean setup =Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 
0) != 0;   
final ArrayList<Tile> tiles = new ArrayList<>();    
final UserManager userManager = (UserManager) context.getSystemService(            Context.USER_SERVICE);   
for (UserHandle user : userManager.getUserProfiles()) {        
// TODO: Needs much optimization, too many PM queries going on here.        
if (user.getIdentifier() == ActivityManager.getCurrentUser()) {            
// Only add Settings for this user.仅为该用户添加设置。            loadTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, 
true);            
loadTilesForAction(context, user, OPERATOR_SETTINGS, cache,                    OPERATOR_DEFAULT_CATEGORY, tiles, false);           
loadTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,                    MANUFACTURER_DEFAULT_CATEGORY, tiles, false);        
}        
if (setup) {            loadTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, 
tiles, false);            loadTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, 
tiles, false);        }    }    final HashMap<String, DashboardCategory> categoryMap = new HashMap<>();    int size = tiles.size();    for (int i = 0; i < size; i++) {        Tile tile = tiles.get(i);        final String categoryKey = tile.getCategory();        DashboardCategory category = categoryMap.get(categoryKey);        if (category == null) {            category = new DashboardCategory(categoryKey);            if (category == null) {                Log.w(LOG_TAG, "Couldn't find category " + categoryKey);                continue;            }            categoryMap.put(categoryKey, category);        }        category.addTile(tile);    }    final ArrayList<DashboardCategory> categories = new 
ArrayList<>(categoryMap.values());    int categorySize = categories.size();    for (int i = 0; i < categorySize; i++) {        DashboardCategory category = categories.get(i);        category.sortTiles();    }    if (DEBUG_TIMING) {        Log.d(LOG_TAG, "getCategories took "                + (System.currentTimeMillis() - startTime) + " ms");    }    return categories;}

可以看见
1、 boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0) != 0;此是判断是否完成开机向导设置,setup为true时表明已完成。
2、ArrayList tiles = new ArrayList<>();新建tiles集合。
3、

  for (UserHandle user : userManager.getUserProfiles()) {   
  // TODO: Needs much optimization, too many PM queries going on here.   
  if (user.getIdentifier() == ActivityManager.getCurrentUser()) {    
  // Only add Settings for this user.仅为该用户添加设置。        loadTilesForAction(context, user, SETTINGS_ACTION, cache, null, tiles, 
true);       
loadTilesForAction(context, user, OPERATOR_SETTINGS, cache,                OPERATOR_DEFAULT_CATEGORY, tiles, false);       
loadTilesForAction(context, user, MANUFACTURER_SETTINGS, cache,                MANUFACTURER_DEFAULT_CATEGORY, tiles, false);   
}  
if (setup) {        loadTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, 
tiles, false);      
loadTilesForAction(context, user, IA_SETTINGS_ACTION, cache, null, tiles, 
false);    }}


遍历设备中的所有用户,调用getTilesForAction方法根据相关action获取相关tiles,填充tiles集合;设置中主要通过此action去搜索系统中符合的活动去作为主页面TopLevelSettings的tile,相关的action定义如下:

    /**
     * Settings will search for system activities of this action and add them as a top level
     * settings tile using the following parameters.
     *
     * <p>A category must be specified in the meta-data for the activity named
     * {@link #EXTRA_CATEGORY_KEY}
     *
     * <p>The title may be defined by meta-data named {@link #META_DATA_PREFERENCE_TITLE}
     * otherwise the label for the activity will be used.
     *
     * <p>The icon may be defined by meta-data named {@link #META_DATA_PREFERENCE_ICON}
     * otherwise the icon for the activity will be used.
     *
     * <p>A summary my be defined by meta-data named {@link #META_DATA_PREFERENCE_SUMMARY}
     */
    public static final String EXTRA_SETTINGS_ACTION = "com.android.settings.action.EXTRA_SETTINGS";

    /**
     * @See {@link #EXTRA_SETTINGS_ACTION}.
     */
    public static final String IA_SETTINGS_ACTION = "com.android.settings.action.IA_SETTINGS";

    /**
     * Same as #EXTRA_SETTINGS_ACTION but used for the platform Settings activities.
     */
    private static final String SETTINGS_ACTION = "com.android.settings.action.SETTINGS";

    private static final String OPERATOR_SETTINGS =
            "com.android.settings.OPERATOR_APPLICATION_SETTING";

    private static final String OPERATOR_DEFAULT_CATEGORY =
            "com.android.settings.category.wireless";

    private static final String MANUFACTURER_SETTINGS =
            "com.android.settings.MANUFACTURER_APPLICATION_SETTING";

    private static final String MANUFACTURER_DEFAULT_CATEGORY =
            "com.android.settings.category.device";

4、新建categoryMap集合, HashMap<String, DashboardCategory> categoryMap,其中map的key为categoryKey;
5、遍历tiles集合,以每个tile的tile.getCategory()的值为构造参数,创建DashboardCategory对象,并将tile添加到此对象中,将此填充到map集合中;
6、将categoryMap的值赋值给ArrayList cagtories以便执行排序算法,遍历新集合利用Collections函数和比较器TILE_COMPARATOR将category.tiles按照priority从大到小排序。
可以看到主要调用loadTilesForAction来获取数据源。

@VisibleForTestingstatic void loadTilesForAction(Context context,       
UserHandle user, String action, Map<Pair<String, String>, Tile> addedCache,        String defaultCategory, List<Tile> outTiles, boolean requireSettings) {  
final Intent intent = new Intent(action);   
if (requireSettings) {       
intent.setPackage(SETTING_PKG);   
}    
loadActivityTiles(context, user, addedCache, defaultCategory, outTiles, 
intent);   
loadProviderTiles(context, user, addedCache, defaultCategory, outTiles, 
intent);}


private static void loadActivityTiles(Context context,      
UserHandle user, Map<Pair<String, String>, Tile> addedCache,       
String defaultCategory, List<Tile> outTiles, Intent intent) {    
final PackageManager pm = context.getPackageManager();   
final List<ResolveInfo> results = pm.queryIntentActivitiesAsUser(intent,            PackageManager.GET_META_DATA, user.getIdentifier());    
for (ResolveInfo resolved : results) {       
if (!resolved.system) {           
// Do not allow any app to add to settings, only system ones.           
continue;        }        
final ActivityInfo activityInfo = resolved.activityInfo;        
final Bundle metaData = activityInfo.metaData;      
loadTile(user, addedCache, defaultCategory, outTiles, intent, metaData, 
activityInfo);    }}


private static void loadTile(UserHandle user, Map<Pair<String, String>, Tile> 
addedCache,        
String defaultCategory, List<Tile> outTiles, Intent intent, Bundle 
metaData,        
ComponentInfo componentInfo) {    
String categoryKey = defaultCategory;    
// Load category    
if ((metaData == null || !metaData.containsKey(EXTRA_CATEGORY_KEY))            
&& categoryKey == null) {        
Log.w(LOG_TAG, "Found " + componentInfo.name + " for intent "                
+ intent + " missing metadata "                
+ (metaData == null ? "" : EXTRA_CATEGORY_KEY));        
return;    } else {        
categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);   
}    
final boolean isProvider = componentInfo instanceof ProviderInfo;    final Pair<String, String> key = isProvider            
? new Pair<>(((ProviderInfo) componentInfo).authority,                    metaData.getString(META_DATA_PREFERENCE_KEYHINT))            
: new Pair<>(componentInfo.packageName, componentInfo.name);    
Tile tile = addedCache.get(key);    
if (tile == null) {        
tile = isProvider                
? new ProviderTile((ProviderInfo) componentInfo, categoryKey, 
metaData)                
: new ActivityTile((ActivityInfo) componentInfo, categoryKey);        addedCache.put(key, tile);    
} else {       
tile.setMetaData(metaData);    
}    
if (!tile.userHandle.contains(user)) {        
tile.userHandle.add(user);   
}    
if (!outTiles.contains(tile)) {        
outTiles.add(tile);    }}

1、通过action来构建intent,根据requireSetting来决定是否指定Settings进程包名:

final Intent intent = new Intent(action);
if (requireSettings) {    
intent.setPackage(SETTING_PKG);}

2、使用PM查询符合相关intent action支持的ResolveInfo集合,每个ResolveInfo对象主要是从AndroidManifest.xml中解析出的

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

3、遍历ResolveInfo集合,获取集合中每一个ResolveInfo对象,判断是否是系统进程,是否AndroidManifest.xml配置的meta标签name包含com.android.settings.category并解析其value值,构建tile对象,并将此添加到tiles集合内输出。

可以看到每个Tile对象,都是包含有从AndroidManifest.xml解析出的Resolveinfo对象和解析meta标签name包含com.android.settings.category value的值。

总结:
1、遍历设备所有用户,调getTilesForAction()方法利用PM去检索设备中所有符合传入action的activity ResolveInfo;
2、判断每一个ResolveInfo是否是系统进程,是否AndroidManifest.xml中配置的meta标签name包含"com.android.settings.category"的 value,将符合条件的以此value的值和ResolveInfo对象构建tile对象;并以此构建填充tiles集合。
3、构建HashMap<String, DashboardCategory> categoryMap集合,以AndroidManifest.xml中配置的meta标签name包含"com.android.settings.category"的 value值为参数来构建DashboardCategory对象,遍历tiles集合将符合条件的tile填充DashboardCategory对象(DashboardCategory对象即包含可以显示在界面上的设置项),并以value为key,DashboardCategory对象为value填充categoryMap集合;
4、将categoryMap的值赋值给ArrayList cagtories以便执行排序算法,遍历新集合利用Collections函数和比较器TILE_COMPARATOR将category.tiles按照priority从大到小排序。
5、经过对Categories集合的更新、排序、去重等操作,得到最终所需的mCategoryByKeyMap集合;
6、再根据所传入的TAG(TopLevelSettings),去mCategoryByKeyMap集合检索,最终得出适合在Settings主界面TopLevelSettings中可以显示的DashboardCategory对象。

这整个流程就分析完毕了,重新结合来看:DashboardFragment.refreshDashboardTiles方法

DashboardFragment.refreshDashboardTiles

此方法是刷新由DashboardCategory支持的首选项

private void refreshDashboardTiles(final String tag) {    
Log.d("wuzhangxiao", "data  : refreshDashboardTiles"+tag);    
final PreferenceScreen screen = getPreferenceScreen();    
final DashboardCategory category =            mDashboardFeatureProvider.getTilesForCategory(getCategoryKey());    
if (category == null) {       
Log.d(tag, "NO dashboard tiles for " + tag);       
return;    }

通过getTilesForCategory()方法得到适合在Settings主界面TopLevelSettings中可以显示的DashboardCategory对象。判断对象是否为空,对象内是否包含tiles集合;

// Create a list to track which tiles are to be removed.创建一个列表以跟踪要删除的图块final Map<String, List<DynamicDataObserver>> remove = new 
ArrayMap(mDashboardTilePrefKeys);
// Install dashboard tiles.安装tiles。
final boolean forceRoundedIcons = shouldForceRoundedIcon();

1、新建remove集合,跟踪那些tile是需要被转移的

开始遍历适合在Settings主界面TopLevelSettings中可以显示的DashboardCategory对象内的tile集合,每个tile包含从AndroidManifest.xml解析出的resolveinfo对象,此即为初步符合条件可以显示在主界面的动态设置项:

for (int i = 0; i < size; i++) {    
Tile tile = tiles.get(i);   
final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);   
if (TextUtils.isEmpty(key)) {        
Log.d(tag, "tile does not contain a key, skipping " + tile);       
continue;   
}    
if (gmsversion && 
key.endsWith(UserBackupSettingsActivity.class.getSimpleName())) {       
continue;    
}    
f (!displayTile(tile)) {        
continue;  
}    
if (mDashboardTilePrefKeys.containsKey(key)) {      
// Have the key already, will rebind.       
final Preference preference = screen.findPreference(key);        
mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),                forceRoundedIcons, getMetricsCategory(), preference, tile, key,                mPlaceholderPreferenceController.getOrder());   
} else {       
// Don't have this key, add it.       
final Preference pref = createPreference(tile);       
final List<DynamicDataObserver> observers =                
mDashboardFeatureProvider.bindPreferenceToTileAndGetObservers(getActivity(),                        forceRoundedIcons, getMetricsCategory(), pref, tile, key,                        mPlaceholderPreferenceController.getOrder());       
screen.addPreference(pref);       
registerDynamicDataObservers(observers);        
mDashboardTilePrefKeys.put(key, observers);   
}   
remove.remove(key);}

final String key = mDashboardFeatureProvider.getDashboardKeyForTile(tile);中的getDashboardKeyForTile方法

DashboardFeatureProviderImpl.getDashboardKeyForTile方法
@Override
public String getDashboardKeyForTile(Tile tile) {
    if (tile == null) {
        return null;
    }
    if (tile.hasKey()) {
        return tile.getKey(mContext);
    }
    final StringBuilder sb = new StringBuilder(DASHBOARD_TILE_PREF_KEY_PREFIX);
    final ComponentName component = tile.getIntent().getComponent();
    sb.append(component.getClassName());
    return sb.toString();
}

判断AndroidManifest.xml中是否配置了meta标签name为"com.android.settings.keyhint"的属性;如果配置,则获取其value值作为后续显示在界面的preference的key值:

    /**
     * Optional key to use for this tile.
     */
    public String getKey(Context context) {
        if (!hasKey()) {
            return null;
        }
        ensureMetadataNotStale(context);
        if (mMetaData.get(META_DATA_PREFERENCE_KEYHINT) instanceof Integer) {
            return context.getResources().getString(mMetaData.getInt(META_DATA_PREFERENCE_KEYHINT));
        } else {
            return mMetaData.getString(META_DATA_PREFERENCE_KEYHINT);
        }
    }

其中的ensureMetadataNotStale()方法主要是能确保获取最新的mMetaData

/** * Ensures metadata is not stale for this tile. */
private void ensureMetadataNotStale(Context context) {   
final PackageManager pm = context.getApplicationContext().getPackageManager();    try {        
final long lastUpdateTime = pm.getPackageInfo(mComponentPackage,                PackageManager.GET_META_DATA).lastUpdateTime;        
if (lastUpdateTime == mLastUpdateTime) {           
// All good. Do nothing           
return;        
}       
// App has been updated since we load metadata last time. Reload metadata.        mComponentInfo = null;        
getComponentInfo(context);        
mLastUpdateTime = lastUpdateTime;   
} catch (PackageManager.NameNotFoundException e) {       
Log.d(TAG, "Can't find package, probably uninstalled.");   
}}

查询此App最后一次修改的时间与上一次修改时间是否一致,如果不是则重新通过PM查询更新mMetaData属性,确保mMetaData属性是从App内获取到的最新的。

如果未配置meta标签name为"com.android.settings.keyhint"的属性,则通过将activity name拼接处理

DashboardFeatureProviderImpl.getDashboardKeyForTile
    final StringBuilder sb = new StringBuilder(DASHBOARD_TILE_PREF_KEY_PREFIX);
    final ComponentName component = tile.getIntent().getComponent();
    sb.append(component.getClassName());
    return sb.toString();

我来以主设置界面的Google设置项为例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XVnrOOXO-1658131332512)(en-resource://database/1458:0)]

其拼接处理后即为:“dashboard_tile_pref_com.google.android.gms.app.settings.GoogleSettingsIALink”

1、判断获取到的key是否为空,判断此设置项是否需要被显示;
2、调用bindPreferenceToTile()方法,对preference进行数据绑定;
3、调用setListening()方法,设置监听,以便于后续各个preference后续可以自行根据需要更新summary。

bindPreferenceToTileAndGetObservers()

packages/apps/Settings/src/com/android/settings/dashboard/DashboardFeatureProviderImpl.java

@Override
public List<DynamicDataObserver> 
bindPreferenceToTileAndGetObservers(FragmentActivity activity,      
boolean forceRoundedIcon, int sourceMetricsCategory, Preference pref, Tile 
tile,        
String key, int baseOrder) {   
if (pref == null) {        
return null;    }    
if (!TextUtils.isEmpty(key)) {        
pref.setKey(key);    } else {       
pref.setKey(getDashboardKeyForTile(tile));    }    
final List<DynamicDataObserver> outObservers = new ArrayList<>();   
DynamicDataObserver observer = bindTitleAndGetObserver(pref, tile);    
if (observer != null) {        
outObservers.add(observer);    }    
observer = bindSummaryAndGetObserver(pref, tile);   
if (observer != null) {        
outObservers.add(observer);    }   
observer = bindSwitchAndGetObserver(pref, tile);   
if (observer != null) {        
outObservers.add(observer);    }   
bindIcon(pref, tile, forceRoundedIcon);   
if (tile instanceof ActivityTile) {        
final Bundle metadata = tile.getMetaData();        
String clsName = null;        
String action = null;        
if (metadata != null) {            
clsName = 
metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);           
action = metadata.getString(META_DATA_KEY_INTENT_ACTION);        }       
if (!TextUtils.isEmpty(clsName)) {            
pref.setFragment(clsName);        
} else {           
final Intent intent = new Intent(tile.getIntent());            intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,                    sourceMetricsCategory);           
if (action != null) {               
intent.setAction(action);            
}            
pref.setOnPreferenceClickListener(preference -> {               
launchIntentOrSelectProfile(activity, tile, intent, 
sourceMetricsCategory);                
return true;           
});       
}    
}   
if (tile.hasOrder()) {       
final String skipOffsetPackageName = activity.getPackageName();        
final int order = tile.getOrder();       
boolean shouldSkipBaseOrderOffset = TextUtils.equals(               
skipOffsetPackageName, 
tile.getIntent().getComponent().getPackageName());       
if (shouldSkipBaseOrderOffset || baseOrder == Preference.DEFAULT_ORDER) {            pref.setOrder(order);       
} else {           
pref.setOrder(order + baseOrder);       
}    }    
return outObservers.isEmpty() ? null : outObservers;}

1、设置preference的key,主要还是通过调用getDashboardKeyForTile()方法去获取

    if (!TextUtils.isEmpty(key)) {
        pref.setKey(key);
    } else {
        pref.setKey(getDashboardKeyForTile(tile));
    }

2、设置preference的summary:
observer = bindSummaryAndGetObserver(pref, tile);

private DynamicDataObserver bindSummaryAndGetObserver(Preference preference, Tile 
tile) {    
final CharSequence summary = tile.getSummary(mContext);    
if (summary != null) {        
preference.setSummary(summary);   
} else if (tile.getMetaData() != null&& tile.getMetaData().containsKey(META_DATA_PREFERENCE_SUMMARY_URI)) {       
// Set a placeholder summary before starting to fetch real summary, this is 
necessary       
// to avoid preference height change.       
// preference.setSummary(R.string.summary_placeholder);       
/**         
* 在开始获取真实摘要之前设置占位符摘要,这是避免首选项高度更改所必需的。偏爱setSummary         */        
final Uri uri = TileUtils.getCompleteUri(tile, 
META_DATA_PREFERENCE_SUMMARY_URI,                
METHOD_GET_DYNAMIC_SUMMARY);        
refreshSummary(uri, preference);        
return createDynamicDataObserver(METHOD_GET_DYNAMIC_SUMMARY, uri, 
preference);    
} else {       
preference.setSummary(R.string.summary_placeholder);    
}    
return null;}

首先判断tile对象是否设置了mSummaryOverride,是,则以此作为preference的summary;
其次再此确保此时tile保存的meta属性是最新的,通过读取"com.android.settings.summary_uri"、"com.android.settings.summary"属性,根据需要取其value作为preference的summary。

3、设置preference的icon,通过读取meta的属性"com.android.settings.icon_uri"、"com.android.settings.icon"的value的值;
4、设置preference的点击跳转界面:

if (tile instanceof ActivityTile) {   
final Bundle metadata = tile.getMetaData();   
String clsName = null;    
String action = null;    
if (metadata != null) {        
clsName = 
metadata.getString(SettingsActivity.META_DATA_KEY_FRAGMENT_CLASS);       
action = metadata.getString(META_DATA_KEY_INTENT_ACTION);    }    
if (!TextUtils.isEmpty(clsName)) {       
pref.setFragment(clsName);   
} else {        
final Intent intent = new Intent(tile.getIntent());        intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,                sourceMetricsCategory);       
if (action != null) {            
intent.setAction(action);        }        
pref.setOnPreferenceClickListener(preference -> {           
launchIntentOrSelectProfile(activity, tile, intent, 
sourceMetricsCategory);            
return true;        });    }}

如果设置了"com.android.settings.FRAGMENT_CLASS"属性,则直接设置此value为跳转的fragment;反之,则构建intent,设置点击监听,跳转activity;

5、如果设置了"com.android.settings.order"属性,则根据其value值来设置preference显示前后。order为负时,绝对值越高,界面显示越靠前;order为正时,值越高,显示越靠后。

总结:

1、主要是通过解析tile对象内保存的meta属性去设置preference的title、key、summary、icon、跳转界面、order显示优先级;
2、Android 11.0中设置主界面的设置项除了加载三方应用的,其余设置基本都是top_level_settings.xml定义的。故对于动态AndroidManifest中配置加载,以其它界面的设置配置项为例,示例如下:

    <activity
        android:name="Settings$DevelopmentSettingsDashboardActivity"
        android:label="@string/development_settings_title"              <!-- preference 标题 -->
        android:icon="@drawable/ic_settings_development"                <!-- preference 图标 -->
        android:parentActivityName="Settings"
        android:enabled="false">
        <intent-filter android:priority="1">
            <action android:name="android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
            <action android:name="com.android.settings.APPLICATION_DEVELOPMENT_SETTINGS" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
        <intent-filter>
            <!-- 可以被Settings搜索到的action,主要逻辑在TileUtils#getTilesForAction()方法 -->
            <action android:name="com.android.settings.action.SETTINGS" />
        </intent-filter>
        <!-- preference order值,列表显示的优先级 -->
        <meta-data android:name="com.android.settings.order" android:value="-40"/>
        <!-- 类别,定义显示在哪个fragment,这里定义的值代表显示在SystemDashboardFragment中,具体看DashboardFragmentRegistry.java中map集合定义 -->
        <meta-data android:name="com.android.settings.category"
                   android:value="com.android.settings.category.ia.system" />
        <!-- preference summary -->
        <meta-data android:name="com.android.settings.summary"
                   android:resource="@string/summary_empty"/>
        <!-- preference 点击跳转fragment界面 -->
        <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                   android:value="com.android.settings.development.DevelopmentSettingsDashboardFragment" />
        <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                   android:value="true" />
    </activity>

3、refreshDashboardTiles()主要是动态的通过PM从AndroidManifest.xml中读取相关配置来加载可以显示的设置item;
4、getTilesForCategory();通过PM去检索AndroidManifest.xml中符合相关action的可以显示在当前fragment上的设置项;
5、bindPreferenceToTile();解析AndroidManifest.xml中配置的meta属性来对设置项preference进行数据绑定;

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Vivado是一款由Xilinx公司开发的FPGA设计工具,其默认界面是英文的,但用户可以根据个人喜好进行设置,使用界面。 首先,打开Vivado软件,进入主界面。在顶部的菜单栏上,找到并点击"Tools"(工具)选项。然后,在下拉菜单选择"Settings"(设置)。 接下来,会打开"Settings"对话框。在对话框的左侧面板,找到并点击"General"(通用)选项。在右侧面板,可以看到"Locale"(语言环境)选项。 点击"Locale"选项后,会出现一个下拉菜单。从这个菜单选择"Chinese"(文)。 然后,点击对话框下方的"OK"按钮,保存设置。之后,Vivado软件将会应用界面。 需要注意的是,有时候在首次运行Vivado软件时,可能需要等待一段时间,以加载和应用新的语言设置。此外,一些较旧的版本可能不支持界面。 通过上述步骤,我们可以设置Vivado的界面文,便于文用户更加方便地使用该软件进行FPGA设计。 ### 回答2: 在vivado设置界面需要遵循以下步骤: 1. 安装好vivado之后,打开软件。 2. 点击“Tools”选项卡,然后选择“Settings”。 3. 在弹出的“Settings”对话框,点击“General”选项,并选择“Locale”。 4. 在“Locale”下拉菜单选择“Chinese Simplified”(简体文)或“Chinese Traditional”(繁体文),然后点击“OK”。 5. 关闭vivado并重新启动软件,界面将会以选择的文语言重新加载。 值得注意的是,vivado的界面仅适用于软件菜单和对话框的显示,对于工程内部的文件和代码注释,仍然需要使用英文或其他非文字符。 希望以上回答对你有所帮助! ### 回答3: vivado是一款由赛灵思公司开发的电子设计自动化工具,它是用于设计、验证和实现复杂的数字和模拟系统的。 要设置vivado的界面,可以按照以下步骤操作: 1. 打开vivado软件。在启动界面上选择“Tools”(工具)菜单。 2. 在“Settings”(设置)菜单,选择“Option”(选项)。 3. 在弹出的对话框,点击左侧的“General”(常规)选项卡。 4. 在右侧的“Language”(语言)下拉菜单,选择“Chinese”(文)作为界面语言。 5. 点击对话框底部的“OK”(确定)按钮,完成设置。 设置完成后,vivado的界面将会变成文显示,并且菜单、工具栏和对话框的文本都会以文的形式呈现。 请注意,如果您是第一次安装vivado软件,需要确保您已经选择了文作为安装程序的语言选项。否则,即使您在软件设置了界面,安装程序和软件界面可能仍然是英文的。 希望这些步骤能够帮助您成功设置vivado的界面。如果您在设置过程遇到任何问题,请随时向我们询问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值