美文网首页
Android 8 setting源码分析一

Android 8 setting源码分析一

作者: 梧叶已秋声 | 来源:发表于2019-12-07 09:21 被阅读0次

要想了解setting,首先要对Preference的使用有一定的了解。
这里我参考官方文档过了一遍。
设置

首先,在gradle中添加依赖,这个可以通过project structure去添加。


image.png

我这里添加如下所示。
implementation 'androidx.preference:preference:1.1.0'

然后,在 res/xml/ 目录下新建一个文件preferences.xml。

<PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <SwitchPreferenceCompat
        app:key="notifications"
        app:title="Enable message notifications"/>

    <Preference
        app:key="feedback"
        app:title="Send feedback"
        app:summary="Report technical issues or suggest new features"/>

</PreferenceScreen>

预览图如下所示:


image.png

然后就是,这个xml文件的使用。
首先,需要新建一个继承PreferenceFragmentCompat的Fragment。这里新建了一个MySettingsFragment。

public class MySettingsFragment extends PreferenceFragmentCompat {

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.preferences, rootKey);
    }
}

MainActivity如下所示。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.settings_container, new MySettingsFragment())
                .commit();
    }
}

activity_main具体如下。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <FrameLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:id="@+id/settings_container"/>
</FrameLayout>

Preference的简单使用就基本是这样了。
如果修改preferences.xml,给SwitchPreferenceCompat和Preference分别套上PreferenceCategory,改为如下所示

<PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <PreferenceCategory
        app:key="notifications_category"
        app:title="Notifications">

        <SwitchPreferenceCompat
            app:key="notifications"
            app:title="Enable message notifications"/>

    </PreferenceCategory>

    <PreferenceCategory
        app:key="help_category"
        app:title="Help">

        <Preference
            app:key="feedback"
            app:summary="Report technical issues or suggest new features"
            app:title="Send feedback"/>

    </PreferenceCategory>

</PreferenceScreen>

结果就会如下所示。


image.png

如果想要使点击Preference后显示新的界面,可以在Preference下添加app:fragment="com.example.myapplication.xxx",或者使用Preference.setFragment()

首先,把preferences.xml改成如下所示。

<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
    <Preference
        app:fragment="com.example.myapplication.MessageFragment"
        app:key="message_key"
        app:title="message"
        app:summary="AAAAAA"/>

    <Preference
        app:fragment="com.example.myapplication.SyncFragment"
        app:key="sync_key"
        app:title="sync"
        app:summary="BBBBB"/>

</PreferenceScreen>

然后,新建MessageFragment和SyncFragment。

public class MessageFragment extends PreferenceFragmentCompat {
    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.preferences_message, rootKey);

    }
}
public class SyncFragment extends PreferenceFragmentCompat {
    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        setPreferencesFromResource(R.xml.preferences_sync, rootKey);
    }
}

preferences_message和preferences_sync分别如下所示。

<PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <SwitchPreferenceCompat
        app:key="message1"
        app:title="1"/>

    <Preference
        app:key="message2"
        app:title="2"
        app:summary="CCC"/>
</PreferenceScreen>
<PreferenceScreen
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <SwitchPreferenceCompat
        app:key="sync1"
        app:title="3"/>

    <Preference
        app:key="sync2"
        app:title="4"
        app:summary="SSSS"/>
</PreferenceScreen>

这样就实现了嵌套fragment。
结果就类似这种,一开始显示preferences.xml中定义的内容,点击不同的Preference后会显示不同的界面。


image.png

Preference常用的属性
title
表示 Preference 标题的 String 值。
示例:app:title="Title"
summary
表示 Preference 摘要的 String 值。
示例:app:summary="Summary"
icon
表示 Preference 图标的 Drawable 。
示例:app:icon="@drawable/ic_camera"
key
String 值,表示用于保留关联 Preference 值的密钥。 使用密钥,您可以在运行时进一步自定义 Preference。 您应为层次结构中的每个 Preference 设置密钥。
示例:app:key="key"

打开Android设备中的setting界面,然后打开hierarchy view工具,点击com.android.settings.Settings。


image.png

然后tree view中会显示界面构成。


image.png
如果tree view没有正常显示,首先确定设备是否可以使用hierarchy view(需要能正常启动View Server),然后需要确定window下show view中tree view是否设置显示。
image.png
image.png

然后,


image.png

界面的显示主要是FocusRecyclerView(id/dashboard_container)+LinearLayout(id/dashboard_tile)。
LinearLayout又由ImageView(id/icon)和TextView(id/title和id/summary)组成。
例如下图,icon就是那个图标,network就是title,mobile networks就是summary。

然后在Android studio中使用ctrl+shift+f搜索id。例如dashboard_container。找到dashboard.xml和DashboardSummary,DashboardSummary是一个fragment,dashboard.xml中定义了使用了FocusRecyclerView。DashboardSummary就是显示的fragment。那么这个fragment是如何显示的?这些显示的数据又是在哪里产生的?

//\vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardSummary.java
public class DashboardSummary extends InstrumentedFragment
        implements CategoryListener, ConditionListener,
        FocusListener, SuggestionDismissController.Callback, DetachListener {
.....
    private FocusRecyclerView mDashboard;
    private DashboardAdapter mAdapter;
    private SummaryLoader mSummaryLoader;
    private ConditionManager mConditionManager;
    private SuggestionParser mSuggestionParser;
    private LinearLayoutManager mLayoutManager;
    private SuggestionsChecks mSuggestionsChecks;
    private DashboardFeatureProvider mDashboardFeatureProvider;
    private SuggestionFeatureProvider mSuggestionFeatureProvider;
.....


}

fragment必定是依托于activity,DashboardSummary是在SettingsActivity中使用的。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\SettingsActivity.java
public class SettingsActivity extends SettingsDrawerActivity
        implements PreferenceManager.OnPreferenceTreeClickListener,
        PreferenceFragment.OnPreferenceStartFragmentCallback,
        ButtonBarHandler, FragmentManager.OnBackStackChangedListener, OnClickListener {
.....
      @VisibleForTesting
      void launchSettingFragment(String initialFragmentName, boolean isSubSettings, Intent intent) {
        if (!mIsShowingDashboard && initialFragmentName != null) {
            // UP will be shown only if it is a sub settings
            if (mIsShortcut) {
                mDisplayHomeAsUpEnabled = isSubSettings;
            } else if (isSubSettings) {
                mDisplayHomeAsUpEnabled = true;
            } else {
                mDisplayHomeAsUpEnabled = false;
            }
            setTitleFromIntent(intent);

            Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
            switchToFragment(initialFragmentName, initialArguments, true, false,
                mInitialTitleResId, mInitialTitle, false);
        } else {
            // Show search icon as up affordance if we are displaying the main Dashboard
            mDisplayHomeAsUpEnabled = true;
            mInitialTitleResId = R.string.dashboard_title;

            switchToFragment(DashboardSummary.class.getName(), null /* args */, false, false,
                mInitialTitleResId, mInitialTitle, false);
        }
    }
.....
}

通过查看AndroidManifest.xml,可以发现启动activity是Settings。
AndroidManifest.xml中使用的是activity-alias 标签。

       <activity-alias android:name="Settings"
                android:taskAffinity="com.android.settings"
                android:label="@string/settings_label_launcher"
                android:launchMode="singleTask"
                android:targetActivity="Settings">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>
        </activity-alias>

alias翻译为别名,化名。activity-alias是Android里为了重复使用Activity而设计的。
对于activity-alias标签,它有一个属性叫android:targentActivity,这个属性就是用来为该标签设置目标Activity的,或者说它就是这个目标Activity的别名。
那么来看看Settings 这个类。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\Settings.java
public class Settings extends SettingsActivity {
  /*
    * Settings subclasses for launching independently.
    */
    public static class AssistGestureSettingsActivity extends SettingsActivity { /* empty */}
    public static class BluetoothSettingsActivity extends SettingsActivity { /* empty */ }
    public static class SimSettingsActivity extends SettingsActivity { /* empty */ }
    public static class TetherSettingsActivity extends SettingsActivity { /* empty */ }
.....

    }
.....
}

一堆的内部静态类xxxSettingsActivity继承自SettingsActivity,并且内容为empty,所以关键还是在SettingsActivity和DashboardSummary。看源码的时候很容易迷失在一堆代码中,所以找到关键点的时候最好是记下来,并且要注意侧重点,人的精力是有限的。
再回过头来看AndroidManifest.xml。其实这里面用的标签有点多,先简单看一下。

  <!-- Wireless Controls -->
        <activity android:name=".Settings$NetworkDashboardActivity"
            android:taskAffinity="com.android.settings"
            android:label="@string/network_dashboard_title"
            android:icon="@drawable/ic_settings_wireless"
            android:parentActivityName="Settings">
            <intent-filter android:priority="1">
                <action android:name="android.settings.WIRELESS_SETTINGS" />
                <action android:name="android.settings.AIRPLANE_MODE_SETTINGS" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.VOICE_LAUNCH" />
            </intent-filter>
            <intent-filter android:priority="11">
                <action android:name="com.android.settings.action.SETTINGS"/>
            </intent-filter>
            <meta-data android:name="com.android.settings.category"
                android:value="com.android.settings.category.ia.homepage"/>
            <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
                android:value="com.android.settings.network.NetworkDashboardFragment"/>
            <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
                android:value="true" />
        </activity>

<intent-filter android:priority="1">表示是一级菜单。
<intent-filter android:priority="11">
在category中的物理位置(按优先级排序时用到,并不是指第9个位置,数越大优先级越大越靠前)
<action android:name="com.android.settings.action.SETTINGS"/>
指定此项在settings中显示。

看了一圈最后还是要回到DashboardSummary中去。

//\vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardSummary.java
public class DashboardSummary extends InstrumentedFragment ......{
    private FocusRecyclerView mDashboard;
    private DashboardAdapter mAdapter;
}

由于DashboardSummary 用的是RecyclerView,而填充RecyclerView的是DashboardAdapter,而RecyclerView的显示数据是给到Adapter去显示的。所以需要看DashboardAdapter。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardAdapter.java
......
 @Override
    public void onBindViewHolder(DashboardItemHolder holder, int position) {
        final int type = mDashboardData.getItemTypeByPosition(position);
        switch (type) {
            case R.layout.dashboard_tile:
                final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
                onBindTile(holder, tile);
                holder.itemView.setTag(tile);
                holder.itemView.setOnClickListener(mTileClickListener);
                break;
            case R.layout.suggestion_condition_container:
                onBindConditionAndSuggestion(
                        (SuggestionAndConditionContainerHolder) holder, position);
                break;
            case R.layout.suggestion_condition_header:
                onBindSuggestionConditionHeader((SuggestionAndConditionHeaderHolder) holder,
                        (SuggestionConditionHeaderData)
                                mDashboardData.getItemEntityByPosition(position));
                break;
            case R.layout.suggestion_condition_footer:
                holder.itemView.setOnClickListener(v -> {
                    mMetricsFeatureProvider.action(mContext,
                            MetricsEvent.ACTION_SETTINGS_CONDITION_EXPAND, false);
                    DashboardData prevData = mDashboardData;
                    mDashboardData = new DashboardData.Builder(prevData).setSuggestionConditionMode(
                            DashboardData.HEADER_MODE_COLLAPSED).build();
                    notifyDashboardDataChanged(prevData);
                    mRecyclerView.scrollToPosition(SUGGESTION_CONDITION_HEADER_POSITION);
                });
                break;
        }
    }
......

这里会根据mDashboardData以及position去获取type,从而选择显示的layout文件。根据前面的分析可知,这里打开setting后首先显示的是dashboard_tile.xml,也就是所谓的setting的一级菜单内容。数据是存储在DashboardData中的。那么DashboardData是如何产生的?里面存储的数据到底是什么?从实际界面上来看,实际上一级菜单只是一些图标和文字,为什么要这样去做?下面带着这些疑惑去看DashboardData的产生。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardAdapter.java
mDashboardData = new DashboardData.Builder()
                .setConditions(conditions)
                .setSuggestions(suggestions)
                .setCategory(category)
                .setSuggestionConditionMode(suggestionConditionMode)
                .build();

这3个参数是需要重点关注的。conditions,suggestions和category。conditions和suggestions看起来比较复杂,所以先来看category。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardData.java
.....
    public static class Builder {
        @HeaderMode
        private int mSuggestionConditionMode = HEADER_MODE_DEFAULT;

        private DashboardCategory mCategory;
        private List<Condition> mConditions;
        private List<Tile> mSuggestions;
}
.....

我尝试在DashboardData的setCategory(DashboardCategory category)函数中,加log
打印title。
Log.d(TAG,"mCategory = " + mCategory.title );
结果报错,说明其实Category传进来的title实际并没有初始化。
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/com.android.settings.Settings}: java.lang.NullPointerException: Attempt to read from field 'java.lang.CharSequence com.android.settingslib.drawer.DashboardCategory.title' on a null object reference
既然如此,那就打印tiles。

 if (mCategory != null && mCategory.tiles.size() != 0){
                    for(int i = 0; i < mCategory.tiles.size();i++){
                        Tile tile = mCategory.tiles.get(i);
                        Log.d("DashboardData","tile  category = " + tile.category +
                         " , tile title = "+ tile.title + " ,tile summary = " +  tile.summary);
                    }
                }

log如下所示,发现传进来的是category全部都是
com.android.settings.category.ia.homepage。这个是在AndroidManifest.xml中定义的。

2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = Mobile networks
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = Bluetooth
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = 100%
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = 24% used - 1.52 GB free
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = Screen lock
2010-01-07 06:04:26.394 1213-1213/? D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates

对比界面就可以发现,这些就是setting的一级菜单所显示的内容。

那么这个Category数据又是如何产生的呢?
加log打印。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardSummary.java
    @VisibleForTesting
    void updateCategoryAndSuggestion(List<Tile> suggestions) {
        final Activity activity = getActivity();
        if (activity == null) {
            return;
        }

        final DashboardCategory category = mDashboardFeatureProvider.getTilesForCategory(
                CategoryKey.CATEGORY_HOMEPAGE);
        Log.d(TAG,"after getTilesForCategory");
        if (category != null && category.tiles.size() != 0){
            for(int i = 0; i < category.tiles.size();i++){
                Tile tile = category.tiles.get(i);
                Log.d(TAG,"tile  category = " + tile.category +
                        " , tile title = "+ tile.title + " ,tile summary = " +  tile.summary);
            }
        }
        mSummaryLoader.updateSummaryToCache(category);
        if (suggestions != null) {
            mAdapter.setCategoriesAndSuggestions(category, suggestions);
        } else {
            Log.d(TAG,"mAdapter.setCategory");
            mAdapter.setCategory(category);
        }
    }
public static final String CATEGORY_HOMEPAGE = "com.android.settings.category.ia.homepage";

所以其实这里就是在AndroidManifest.xml中定义的数据。

通过加log打印发现,会多次调用getTilesForCategory函数去更新category 。

2010-01-07 06:31:04.599 1175-1175/com.android.settings D/DashboardSummary: after getTilesForCategory
2010-01-07 06:31:04.599 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = null
2010-01-07 06:31:04.599 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = null
2010-01-07 06:31:04.599 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:31:04.600 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = null
2010-01-07 06:31:04.600 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:31:04.600 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:31:04.600 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = null
2010-01-07 06:31:04.601 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = null
2010-01-07 06:31:04.601 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates
2010-01-07 06:31:04.602 1175-1175/com.android.settings D/DashboardSummary: mAdapter.setCategory
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = null
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = null
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = null
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = null
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = null
2010-01-07 06:31:04.603 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates
2010-01-07 06:31:04.903 1175-1175/com.android.settings D/DashboardSummary: Listening for condition changes
2010-01-07 06:31:04.904 1175-1175/com.android.settings D/DashboardSummary: onConditionsChanged
2010-01-07 06:31:04.904 1175-1175/com.android.settings D/DashboardSummary: conditions refreshed
2010-01-07 06:31:04.931 1175-1175/com.android.settings D/DashboardSummary: Suggestion feature is disabled, skipping suggestion entirely
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: after getTilesForCategory
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = null
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = null
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = null
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = null
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = null
2010-01-07 06:31:04.932 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates
2010-01-07 06:31:04.933 1175-1175/com.android.settings D/DashboardSummary: mAdapter.setCategory
2010-01-07 06:31:04.934 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = null
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = null
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = null
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = null
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = null
2010-01-07 06:31:04.935 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates
2010-01-07 06:31:06.877 1175-1175/com.android.settings D/DashboardSummary: Suggestion feature is disabled, skipping suggestion entirely
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: after getTilesForCategory
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = Mobile networks
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = Bluetooth
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = 100%
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = 24% used - 1.52 GB free
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = Screen lock
2010-01-07 06:31:06.878 1175-1175/com.android.settings D/DashboardSummary: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates
2010-01-07 06:31:06.879 1175-1175/com.android.settings D/DashboardSummary: mAdapter.setCategory
2010-01-07 06:31:06.879 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Network ,tile summary = Mobile networks
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Connected devices ,tile summary = Bluetooth
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Apps & notifications ,tile summary = Permissions, default apps
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Battery ,tile summary = 100%
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Display ,tile summary = Wallpaper, sleep, font size
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Sound ,tile summary = Volume, Do Not Disturb
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Storage ,tile summary = 24% used - 1.52 GB free
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = Security ,tile summary = Screen lock
2010-01-07 06:31:06.880 1175-1175/com.android.settings D/DashboardData: tile  category = com.android.settings.category.ia.homepage , tile title = System ,tile summary = Languages, time, backup, updates
2010-01-07 06:31:07.030 1175-1175/com.android.settings D/DashboardSummary: Suggestion feature is disabled, skipping suggestion entirely
2010-01-07 06:31:07.030 1175-1175/com.android.settings D/DashboardSummary: after getTilesForCategory

一开始部分summary 为null是因为AndroidManifest.xml中部分activity中没有定义summary。
例如下面是AndroidManifest中的一个简单的activity的定义。

           <meta-data android:name="com.android.settings.title"
                      android:resource="@string/wallpaper_suggestion_title" />
           <meta-data android:name="com.android.settings.summary"
                      android:resource="@string/wallpaper_suggestion_summary" />

com.android.settings.title所定义的就是DashboardCategory 中的title,com.android.settings.summary所定义的就是DashboardCategory 中的summary。当AndroidManifest中没有定义summary的时候,数据又是从哪里来的?这部分涉及到mDashboardFeatureProvider这部分的内容就先不管了。

通过前面的分析,大致知道了一级菜单的数据的来源,那么下面来看二级菜单。
由于一级菜单的显示是DashboardSummary这个fragment,并且用的是RecyclerView,所以去DashboardAdapter里面找OnClickListener。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardAdapter.java
........
    private View.OnClickListener mTileClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //TODO: get rid of setTag/getTag
            mDashboardFeatureProvider.openTileIntent((Activity) mContext, (Tile) v.getTag());
        }
    };
........
 @Override
    public void onBindViewHolder(DashboardItemHolder holder, int position) {
        final int type = mDashboardData.getItemTypeByPosition(position);
        switch (type) {
            case R.layout.dashboard_tile:
                final Tile tile = (Tile) mDashboardData.getItemEntityByPosition(position);
                onBindTile(holder, tile);
                holder.itemView.setTag(tile);
                holder.itemView.setOnClickListener(mTileClickListener);
                break;
     ........
    }
........

这里可以看出,主要是通过mDashboardFeatureProvider.openTileIntent去跳转不同的二级菜单。

DashboardFeatureProvider只是一个接口,openTileIntent的定义在DashboardFeatureProviderImpl中。openTileIntent会调用launchIntentOrSelectProfile从而实现activity的跳转。

//vendor\mediatek\proprietary\packages\xx\xx\src\com\android\settings\dashboard\DashboardFeatureProviderImpl.java
public class DashboardFeatureProviderImpl implements DashboardFeatureProvider {
........
    @Override
    public void openTileIntent(Activity activity, Tile tile) {
        if (tile == null) {
            Intent intent = new Intent(Settings.ACTION_SETTINGS).addFlags(
                    Intent.FLAG_ACTIVITY_CLEAR_TASK);
            mContext.startActivity(intent);
            return;
        }

        if (tile.intent == null) {
            return;
        }
        final Intent intent = new Intent(tile.intent)
                .putExtra(SettingsActivity.EXTRA_SOURCE_METRICS_CATEGORY,
                        MetricsEvent.DASHBOARD_SUMMARY)
                .putExtra(SettingsDrawerActivity.EXTRA_SHOW_MENU, true)
                .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
        launchIntentOrSelectProfile(activity, tile, intent, MetricsEvent.DASHBOARD_SUMMARY);
    }

    private void launchIntentOrSelectProfile(Activity activity, Tile tile, Intent intent,
            int sourceMetricCategory) {
        if (!isIntentResolvable(intent)) {
            Log.w(TAG, "Cannot resolve intent, skipping. " + intent);
            return;
        }
        ProfileSelectDialog.updateUserHandlesIfNeeded(mContext, tile);
        if (tile.userHandle == null) {
            mMetricsFeatureProvider.logDashboardStartIntent(mContext, intent, sourceMetricCategory);
            activity.startActivityForResult(intent, 0);
        } else if (tile.userHandle.size() == 1) {
            mMetricsFeatureProvider.logDashboardStartIntent(mContext, intent, sourceMetricCategory);
            activity.startActivityForResultAsUser(intent, 0, tile.userHandle.get(0));
        } else {
            ProfileSelectDialog.show(activity.getFragmentManager(), tile);
        }
    }

........
}

所以其实二级菜单也是不同的activity。例如NetworkDashboardActivity和AppAndNotificationDashboardActivity。
NetworkDashboardActivity在注册表中写的是Settings$NetworkDashboardActivity,为什么写Settings$xxxx原因我没有弄清楚。下一篇是二级菜单的分析。

参考链接:
设置

Android系统Settings设置模块

Android7.1 Setting模块添加一级菜单跳转第三方应用

Android系统源码剖析(一)---Settings

Android原生Settings源码分析

Android O Settings源码流程分析(静态界面篇)

Android 8.1 Settings源码解析

settings的使用说明

activity-alias属性的使用

Android 8.0 Settings流程分析与变动

相关文章

网友评论

      本文标题:Android 8 setting源码分析一

      本文链接:https://www.haomeiwen.com/subject/pnosgctx.html