Analyze the main interface loading of Android 11.0 Settings source code

  • 2021-12-12 05:42:40
  • OfStack

This article mainly records the loading process of AndroidR Settings source code main interface, which is convenient for subsequent work and debugging its process.

Settings code path:

packages/app/Settings/

Settings code get:

Download address of Setting source code: https://github.com/aosp-mirror/platform_packages_apps_settings
git Address: https://github.com/aosp-mirror/platform_packages_apps_settings. git

Main interface loading:

First, let's look at the AndroidManifest. xml file in the Settings module and find the default startup entry Activity information:


<activity android:name=".homepage.SettingsHomepageActivity"
      android:label="@string/settings_label_launcher"
      android:theme="@style/Theme.Settings.Home"
      android:taskAffinity="com.android.settings.root"
      android:launchMode="singleTask"
      android:configChanges="keyboard|keyboardHidden">
<intent-filter android:priority="1">
    <action android:name="android.settings.SETTINGS" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
           android:value="true" />
</activity>
//activity-alias Can be used to set a Activity You can put it on the desktop or quickly call it up by other components through this alias.                    
//android:targetActivity For the goal Activity. 
<!-- Alias for launcher activity only, as this belongs to each profile. -->
<activity-alias android:name="Settings"
    android:label="@string/settings_label_launcher"
    android:taskAffinity="com.android.settings.root"
    android:launchMode="singleTask"
    android:targetActivity=".homepage.SettingsHomepageActivity">
<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>

It can be seen that the main interface started by the desktop icon of Settings is Settings. java, but its xml defines targetActivity attribute, which should be SettingsHomepageActivity. java in essence, starting from onCreate () method:


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.settings_homepage_container);
    final View root = findViewById(R.id.settings_homepage_container);
    root.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    setHomepageContainerPaddingTop();

    final Toolbar toolbar = findViewById(R.id.search_action_bar);
    FeatureFactory.getFactory(this).getSearchFeatureProvider()
            .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);

    final ImageView avatarView = findViewById(R.id.account_avatar);
    getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
    getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));

    if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
        // Only allow contextual feature on high ram devices.
        showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
    }
    showFragment(new TopLevelSettings(), R.id.main_content);
    ((FrameLayout) findViewById(R.id.main_content))
            .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}

You can see that the layout of the main interface is settings_homepage_container. xml:


<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/settings_homepage_container"
    android:fitsSystemWindows="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.core.widget.NestedScrollView
        android:id="@+id/main_content_scrollable_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="com.android.settings.widget.FloatingAppBarScrollingViewBehavior">

        <LinearLayout
            android:id="@+id/homepage_container"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <FrameLayout
                android:id="@+id/contextual_cards_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/contextual_card_side_margin"
                android:layout_marginEnd="@dimen/contextual_card_side_margin"/>

            <FrameLayout
                android:id="@+id/main_content"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:animateLayoutChanges="true"
                android:background="?android:attr/windowBackground"/>

        </LinearLayout>
    </androidx.core.widget.NestedScrollView>

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:touchscreenBlocksFocus="false"
        android:keyboardNavigationCluster="false">
        <include layout="@layout/search_bar"/>
    </com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

The main interface layout mainly includes two parts: a top shortcut search bar, and an FrameLayout whose Id is main_content is used to display the main settings, that is, the first-level menu item interface of Settings.
Back to the onCreate () method:


showFragment(new TopLevelSettings(), R.id.main_content);

You can see that the Level 1 menu starts TopLevelSettings, which inherits from DashboardFragment. java:


public class TopLevelSettings extends DashboardFragment implements
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback

The construction method of TopLevelSettings:


public TopLevelSettings() {
    final Bundle args = new Bundle();
    // Disable the search icon because this page uses a full search view in actionbar.
    args.putBoolean(NEED_SEARCH_ICON_IN_ACTION_BAR, false);
    setArguments(args);
}

You can see that a parameter is passed through the constructor, and as you can see from the comments, the intention of this parameter is to hide the search icon in the actionbar of the main interface because the main interface uses a complete search view. Then look at the onAttach () method according to the framgments life cycle:


@Override
public void onAttach(Context context) {
    super.onAttach(context);
    use(SupportPreferenceController.class).setActivity(getActivity());
}

Call the onAttach () method of the parent class DashboardFragment. java:


@Override
public void onAttach(Context context) {
    super.onAttach(context);
    mSuppressInjectedTileKeys = Arrays.asList(context.getResources().getStringArray(
            R.array.config_suppress_injected_tile_keys));
    mDashboardFeatureProvider = FeatureFactory.getFactory(context).
            getDashboardFeatureProvider(context);
    // Load preference controllers from code
    final List<AbstractPreferenceController> controllersFromCode =
            createPreferenceControllers(context);
    // Load preference controllers from xml definition
    final List<BasePreferenceController> controllersFromXml = PreferenceControllerListHelper
            .getPreferenceControllersFromXml(context, getPreferenceScreenResId());
    // Filter xml-based controllers in case a similar controller is created from code already.
    final List<BasePreferenceController> uniqueControllerFromXml =
            PreferenceControllerListHelper.filterControllers(
                    controllersFromXml, controllersFromCode);

    // Add unique controllers to list.
    if (controllersFromCode != null) {
        mControllers.addAll(controllersFromCode);
    }
    mControllers.addAll(uniqueControllerFromXml);

    // And wire up with lifecycle.
    final Lifecycle lifecycle = getSettingsLifecycle();
    uniqueControllerFromXml.forEach(controller -> {
        if (controller instanceof LifecycleObserver) {
            lifecycle.addObserver((LifecycleObserver) controller);
        }
    });

    // Set metrics category for BasePreferenceController.
    final int metricCategory = getMetricsCategory();
    mControllers.forEach(controller -> {
        if (controller instanceof BasePreferenceController) {
            ((BasePreferenceController) controller).setMetricsCategory(metricCategory);
        }
    });

    mPlaceholderPreferenceController =
            new DashboardTilePlaceholderPreferenceController(context);
    mControllers.add(mPlaceholderPreferenceController);
    for (AbstractPreferenceController controller : mControllers) {
        addPreferenceController(controller);
    }
}

Through the method annotation, we can know that this method is mainly to complete the loading of preference controllers.
DashboardFragment. onCreate () method for java:


@Override
public void onCreate(Bundle icicle) {
    super.onCreate(icicle);
    // Set ComparisonCallback so we get better animation when list changes.
    getPreferenceManager().setPreferenceComparisonCallback(
            new PreferenceManager.SimplePreferenceComparisonCallback());
    if (icicle != null) {
        // Upon rotation configuration change we need to update preference states before any
        // editing dialog is recreated (that would happen before onResume is called).
        updatePreferenceStates();
    }
}

Set ComparisonCallback for better animation when the list changes.
When entering for the first time, icicle is null. According to the location discovery of log, the onCreatePreferences () method of DashboardFragment. java is called afterwards:


@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    checkUiBlocker(mControllers);
    refreshAllPreferences(getLogTag());
    mControllers.stream()
            .map(controller -> (Preference) findPreference(controller.getPreferenceKey()))
            .filter(Objects::nonNull)
            .forEach(preference -> {
                // Give all controllers a chance to handle click.
                preference.getExtras().putInt(CATEGORY, getMetricsCategory());
            });
}

Call refreshAllPreferences ():


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.settings_homepage_container);
    final View root = findViewById(R.id.settings_homepage_container);
    root.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    setHomepageContainerPaddingTop();

    final Toolbar toolbar = findViewById(R.id.search_action_bar);
    FeatureFactory.getFactory(this).getSearchFeatureProvider()
            .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);

    final ImageView avatarView = findViewById(R.id.account_avatar);
    getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
    getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));

    if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
        // Only allow contextual feature on high ram devices.
        showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
    }
    showFragment(new TopLevelSettings(), R.id.main_content);
    ((FrameLayout) findViewById(R.id.main_content))
            .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
0

Refresh all preference items, including static preference from xml and dynamic preference from DashboardCategory, prefs defined by static xml (calling displayResourceTiles () method), dynamic DashboardCategory dynamic load (calling refreshDashboardTiles (TAG) method, where TAG is "TopLevelSettings").
displayResourceTiles (): This method mainly loads the display prefs from the xml resource file:


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.settings_homepage_container);
    final View root = findViewById(R.id.settings_homepage_container);
    root.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    setHomepageContainerPaddingTop();

    final Toolbar toolbar = findViewById(R.id.search_action_bar);
    FeatureFactory.getFactory(this).getSearchFeatureProvider()
            .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);

    final ImageView avatarView = findViewById(R.id.account_avatar);
    getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
    getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));

    if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
        // Only allow contextual feature on high ram devices.
        showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
    }
    showFragment(new TopLevelSettings(), R.id.main_content);
    ((FrameLayout) findViewById(R.id.main_content))
            .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
1

Static loading

First call the getPreferenceScreenResId () method to get the ID of the xml to be loaded, and then call the getPreferenceScreenResId () method of the subclass TopLevelSettings. java:


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.settings_homepage_container);
    final View root = findViewById(R.id.settings_homepage_container);
    root.setSystemUiVisibility(
            View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);

    setHomepageContainerPaddingTop();

    final Toolbar toolbar = findViewById(R.id.search_action_bar);
    FeatureFactory.getFactory(this).getSearchFeatureProvider()
            .initSearchToolbar(this /* activity */, toolbar, SettingsEnums.SETTINGS_HOMEPAGE);

    final ImageView avatarView = findViewById(R.id.account_avatar);
    getLifecycle().addObserver(new AvatarViewMixin(this, avatarView));
    getLifecycle().addObserver(new HideNonSystemOverlayMixin(this));

    if (!getSystemService(ActivityManager.class).isLowRamDevice()) {
        // Only allow contextual feature on high ram devices.
        showFragment(new ContextualCardsFragment(), R.id.contextual_cards_content);
    }
    showFragment(new TopLevelSettings(), R.id.main_content);
    ((FrameLayout) findViewById(R.id.main_content))
            .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
2

You can see that the xml file loaded by the Settings main interface is top_level_settings:


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

    <Preference
        android:key="top_level_network"
        android:title="@string/network_dashboard_title"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_network"
        android:order="-120"
        android:fragment="com.android.settings.network.NetworkDashboardFragment"
        settings:controller="com.android.settings.network.TopLevelNetworkEntryPreferenceController"/>

    <Preference
        android:key="top_level_connected_devices"
        android:title="@string/connected_devices_dashboard_title"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_connected_device"
        android:order="-110"
        android:fragment="com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment"
        settings:controller="com.android.settings.connecteddevice.TopLevelConnectedDevicesPreferenceController"/>

    <Preference
        android:key="top_level_apps_and_notifs"
        android:title="@string/app_and_notification_dashboard_title"
        android:summary="@string/app_and_notification_dashboard_summary"
        android:icon="@drawable/ic_homepage_apps"
        android:order="-100"
        android:fragment="com.android.settings.applications.AppAndNotificationDashboardFragment"/>

    <Preference
        android:key="top_level_battery"
        android:title="@string/power_usage_summary_title"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_battery"
        android:fragment="com.android.settings.fuelgauge.PowerUsageSummary"
        android:order="-90"
        settings:controller="com.android.settings.fuelgauge.TopLevelBatteryPreferenceController"/>

    <Preference
        android:key="top_level_display"
        android:title="@string/display_settings"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_display"
        android:order="-80"
        android:fragment="com.android.settings.DisplaySettings"
        settings:controller="com.android.settings.display.TopLevelDisplayPreferenceController"/>

    <Preference
        android:key="top_level_sound"
        android:title="@string/sound_settings"
        android:summary="@string/sound_dashboard_summary"
        android:icon="@drawable/ic_homepage_sound"
        android:order="-70"
        android:fragment="com.android.settings.notification.SoundSettings"/>

    <Preference
        android:key="top_level_storage"
        android:title="@string/storage_settings"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_storage"
        android:order="-60"
        android:fragment="com.android.settings.deviceinfo.StorageSettings"
        settings:controller="com.android.settings.deviceinfo.TopLevelStoragePreferenceController"/>

    <Preference
        android:key="top_level_privacy"
        android:title="@string/privacy_dashboard_title"
        android:summary="@string/privacy_dashboard_summary"
        android:icon="@drawable/ic_homepage_privacy"
        android:order="-55"
        android:fragment="com.android.settings.privacy.PrivacyDashboardFragment"/>

    <Preference
        android:key="top_level_location"
        android:title="@string/location_settings_title"
        android:summary="@string/location_settings_loading_app_permission_stats"
        android:icon="@drawable/ic_homepage_location"
        android:order="-50"
        android:fragment="com.android.settings.location.LocationSettings"
        settings:controller="com.android.settings.location.TopLevelLocationPreferenceController"/>

    <Preference
        android:key="top_level_security"
        android:title="@string/security_settings_title"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_security"
        android:order="-40"
        android:fragment="com.android.settings.security.SecuritySettings"
        settings:controller="com.android.settings.security.TopLevelSecurityEntryPreferenceController"/>

    <Preference
        android:key="top_level_accounts"
        android:title="@string/account_dashboard_title"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_accounts"
        android:order="-30"
        android:fragment="com.android.settings.accounts.AccountDashboardFragment"
        settings:controller="com.android.settings.accounts.TopLevelAccountEntryPreferenceController"/>

    <Preference
        android:key="top_level_accessibility"
        android:title="@string/accessibility_settings"
        android:summary="@string/accessibility_settings_summary"
        android:icon="@drawable/ic_homepage_accessibility"
        android:order="-20"
        android:fragment="com.android.settings.accessibility.AccessibilitySettings"
        settings:controller="com.android.settings.accessibility.TopLevelAccessibilityPreferenceController"/>

    <Preference
        android:key="top_level_system"
        android:title="@string/header_category_system"
        android:summary="@string/system_dashboard_summary"
        android:icon="@drawable/ic_homepage_system_dashboard"
        android:order="10"
        android:fragment="com.android.settings.system.SystemDashboardFragment"/>

    <Preference
        android:key="top_level_about_device"
        android:title="@string/about_settings"
        android:summary="@string/summary_placeholder"
        android:icon="@drawable/ic_homepage_about"
        android:order="20"
        android:fragment="com.android.settings.deviceinfo.aboutphone.MyDeviceInfoFragment"
        settings:controller="com.android.settings.deviceinfo.aboutphone.TopLevelAboutDevicePreferenceController"/>

    <Preference
        android:key="top_level_support"
        android:summary="@string/support_summary"
        android:title="@string/page_tab_title_support"
        android:icon="@drawable/ic_homepage_support"
        android:order="100"
        settings:controller="com.android.settings.support.SupportPreferenceController"/>

</PreferenceScreen>

You can see that the main configuration items are 1 Preference menu items such as network and internet, connected devices, applications and notifications, batteries, etc. The configuration meaning of Preference:

key: Uniquely ID; title: Title; summary: Introduction; ico: Icon; order: Load display priority. When order is negative, the higher the absolute value, the higher the interface display; order is positive, the higher the value, the more backward the display; fragment: Click on the fragment interface to which this preference jumps; controller: Control management class.
 

Dynamic loading

refreshDashboardTiles

Summary:

The main Activity substantive implementation of Settings is within SettingsHomepageActivity. java; In the main interface of Settings, the display of item is on fragment, fragment is TopLevelSettings. java, and the layout of loading display is top_level_settings. xml; The loading display of item, the main interface setting item of Settings, is mainly divided into two parts. Part 1 is static loading defined by xml, and xml is top_level_settings. xml; Part 1 is DashboardCategory to obtain dynamic loading; Each setting item item is one preference. When loading through xml definition, there must be one controller, which can be the attribute declaration of "settings: controller" defined in xml, and the name must be the same as the package name path of the class; You can also implement the createPreferenceControllers () method directly in the related fragment to invoke the construction related controller. One of the two can be saved. When configuring preference in xml, the "android: key" attribute must be defined;

The above is the analysis of Android 11.0 Settings source code of the main interface loading details, more information about Android 11.0 Settings source code please pay attention to other related articles on this site!


Related articles: