Brief analysis of Android M setup process

Keywords: Android Java Fragment Attribute

This note mainly records the general process of setting up, so as to quickly understand the big ideas when it is necessary to modify the settings in subsequent projects.

Start with the entry, that is, the first Activity, and find android.intent.category.LAUNCHER in Android Manifest. xml. This activity-alias is the entry you are looking for. In fact, this is just an individual name.
What I really want to see is its android:targetActivity="Settings", that is <activity android: name= "Settings".
  1. <activity android:name="Settings"
  2. android:taskAffinity="com.android.settings"
  3. android:label="@string/settings_label_launcher"
  4. android:launchMode="singleTask">
  5. <intent-filter android:priority="1">
  6. <action android:name="android.settings.SETTINGS" />
  7. <category android:name="android.intent.category.DEFAULT" />
  8. </intent-filter>
  9. <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
  10. android:value="true" />
  11. </activity>
  12. <!-- Alias for launcher activity only, as this belongs to each profile. -->
  13. <activity-alias android:name="Settings"
  14. android:taskAffinity="com.android.settings"
  15. android:label="@string/settings_label_launcher"
  16. android:launchMode="singleTask"
  17. android:targetActivity="Settings">
  18. <intent-filter>
  19. <action android:name="android.intent.action.MAIN" />
  20. <category android:name="android.intent.category.DEFAULT" />
  21. <category android:name="android.intent.category.LAUNCHER" />
  22. </intent-filter>
  23. </activity-alias>

Open this Settings.java and find that there are many internal static activities defined. The main logic is in its parent class, Settings Activity. java. Start with onCreate().
  1. @Override
  2. protected void onCreate(Bundle savedState) {
  3. super.onCreate(savedState);
  4. mExt = UtilsExt.getMiscPlugin(this);
  5. // Should happen before any call to getIntent()
  6. getMetaData();//Get the value of the com.android.settings.FRAGMENT_CLASS attribute in the configuration meta-data, and save the value inmFragmentClass inside
  7. final Intent intent = getIntent();
  8. if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
  9. getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
  10. }
  11. mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
  12. Context.MODE_PRIVATE);
  13. // Getting Intent properties can only be done after the super.onCreate(...)
  14. final String initialFragmentName = intent.getStringExtra(EXTRA_SHOW_FRAGMENT);
  15. mIsShortcut = isShortCutIntent(intent) || isLikeShortCutIntent(intent) ||
  16. intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, false);
  17. final ComponentName cn = intent.getComponent();
  18. final String className = cn.getClassName();
  19. mIsShowingDashboard = className.equals(Settings.class.getName());
  20. // This is a "Sub Settings" when:
  21. // - this is a real SubSettings
  22. // - or :settings:show_fragment_as_subsetting is passed to the Intent
  23. final boolean isSubSettings = this instanceof SubSettings ||
  24. intent.getBooleanExtra(EXTRA_SHOW_FRAGMENT_AS_SUBSETTING, false);
  25. // If this is a sub settings, then apply the SubSettings Theme for the ActionBar content insets
  26. if (isSubSettings) {
  27. // Check also that we are not a Theme Dialog as we don't want to override them
  28. final int themeResId = getThemeResId();
  29. if (themeResId != R.style.Theme_DialogWhenLarge &&
  30. themeResId != R.style.Theme_SubSettingsDialogWhenLarge) {
  31. setTheme(R.style.Theme_SubSettings);
  32. }
  33. }
  34. setContentView(mIsShowingDashboard ?
  35. R.layout.settings_main_dashboard : R.layout.settings_main_prefs);
  36. mContent = (ViewGroup) findViewById(R.id.main_content);
  37. getFragmentManager().addOnBackStackChangedListener(this);
  38. if (mIsShowingDashboard) {
  39. // Run the Index update only if we have some space
  40. if (!Utils.isLowStorage(this)) {
  41. Index.getInstance(getApplicationContext()).update();
  42. } else {
  43. Log.w(LOG_TAG, "Cannot update the Indexer as we are running low on storage space!");
  44. }
  45. }
  46. if (savedState != null) {
  47. // We are restarting from a previous saved state; used that to initialize, instead
  48. // of starting fresh.
  49. mSearchMenuItemExpanded = savedState.getBoolean(SAVE_KEY_SEARCH_MENU_EXPANDED);
  50. mSearchQuery = savedState.getString(SAVE_KEY_SEARCH_QUERY);
  51. setTitleFromIntent(intent);
  52. ArrayList<DashboardCategory> categories =
  53. savedState.getParcelableArrayList(SAVE_KEY_CATEGORIES);
  54. if (categories != null) {
  55. mCategories.clear();
  56. mCategories.addAll(categories);
  57. setTitleFromBackStack();
  58. }
  59. mDisplayHomeAsUpEnabled = savedState.getBoolean(SAVE_KEY_SHOW_HOME_AS_UP);
  60. mDisplaySearch = savedState.getBoolean(SAVE_KEY_SHOW_SEARCH);
  61. mHomeActivitiesCount = savedState.getInt(SAVE_KEY_HOME_ACTIVITIES_COUNT,
  62. 1 /* one home activity by default */);
  63. } else {
  64. if (!mIsShowingDashboard) {
  65. mDisplaySearch = false;
  66. // UP will be shown only if it is a sub settings
  67. if (mIsShortcut) {
  68. mDisplayHomeAsUpEnabled = isSubSettings;
  69. } else if (isSubSettings) {
  70. mDisplayHomeAsUpEnabled = true;
  71. } else {
  72. mDisplayHomeAsUpEnabled = false;
  73. }
  74. setTitleFromIntent(intent);
  75. Bundle initialArguments = intent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
  76. switchToFragment(initialFragmentName, initialArguments, true, false,
  77. mInitialTitleResId, mInitialTitle, false);
  78. } else {
  79. // No UP affordance if we are displaying the main Dashboard
  80. mDisplayHomeAsUpEnabled = false;
  81. // Show Search affordance
  82. //mDisplaySearch = true;
  83. mDisplaySearch = false;//zhangle add :remove Search
  84. mInitialTitleResId = R.string.dashboard_title;
  85. switchToFragment(DashboardSummary.class.getName(), null, false, false,
  86. mInitialTitleResId, mInitialTitle, false);
  87. }
  88. }
  89. mActionBar = getActionBar();
  90. if (mActionBar != null) {
  91. mActionBar.setDisplayHomeAsUpEnabled(mDisplayHomeAsUpEnabled);
  92. mActionBar.setHomeButtonEnabled(mDisplayHomeAsUpEnabled);
  93. }
  94. mSwitchBar = (SwitchBar) findViewById(R.id.switch_bar);
  95. // see if we should show Back/Next buttons
  96. if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, false)) {
  97. View buttonBar = findViewById(R.id.button_bar);
  98. if (buttonBar != null) {
  99. buttonBar.setVisibility(View.VISIBLE);
  100. Button backButton = (Button)findViewById(R.id.back_button);
  101. backButton.setOnClickListener(new OnClickListener() {
  102. public void onClick(View v) {
  103. setResult(RESULT_CANCELED, getResultIntentData());
  104. finish();
  105. }
  106. });
  107. Button skipButton = (Button)findViewById(R.id.skip_button);
  108. skipButton.setOnClickListener(new OnClickListener() {
  109. public void onClick(View v) {
  110. setResult(RESULT_OK, getResultIntentData());
  111. finish();
  112. }
  113. });
  114. mNextButton = (Button)findViewById(R.id.next_button);
  115. mNextButton.setOnClickListener(new OnClickListener() {
  116. public void onClick(View v) {
  117. setResult(RESULT_OK, getResultIntentData());
  118. finish();
  119. }
  120. });
  121. // set our various button parameters
  122. if (intent.hasExtra(EXTRA_PREFS_SET_NEXT_TEXT)) {
  123. String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_NEXT_TEXT);
  124. if (TextUtils.isEmpty(buttonText)) {
  125. mNextButton.setVisibility(View.GONE);
  126. }
  127. else {
  128. mNextButton.setText(buttonText);
  129. }
  130. }
  131. if (intent.hasExtra(EXTRA_PREFS_SET_BACK_TEXT)) {
  132. String buttonText = intent.getStringExtra(EXTRA_PREFS_SET_BACK_TEXT);
  133. if (TextUtils.isEmpty(buttonText)) {
  134. backButton.setVisibility(View.GONE);
  135. }
  136. else {
  137. backButton.setText(buttonText);
  138. }
  139. }
  140. if (intent.getBooleanExtra(EXTRA_PREFS_SHOW_SKIP, false)) {
  141. skipButton.setVisibility(View.VISIBLE);
  142. }
  143. }
  144. }
  145. mHomeActivitiesCount = getHomeActivitiesCount();
  146. }
Line 7 is to get the value of the com.android.settings.FRAGMENT_CLASS attribute in the configuration meta-data, save the value in mFragmentClass, which will be used later. In fact, this variable is the name of the Fragment we want to open.
The mDevelopment Preferences in line 14 are used to determine whether developer options are displayed in the first-level menu list set.
Line 20 mIsShortcut is used to set shortcuts, that is, when creating shortcuts on the desktop.
Line 26 mIsShowing Dashboard is used to determine whether the first-level menu is displayed or not, because there are deeper second-level menus. At the same time, this variable will determine what layout file is used for setContentView on line 44. The first-level menu page is different from the second-level menu page, because the second-level menu page has the return button and the next and next buttons, which are in the setup-security context. When setting the password, the two buttons at the bottom are configured in settings_main_prefs.
At the same time, the if-else of lines 81 to 106 is also determined by mIsShowing Dashboard. If it is a DashBoard page (first-level menu), it is handled by Dashboard Summary. If not, it is handled by the specified initial FragmentName obtained. Here's how switchToFragment() handles it, except that the parameters passed in are different.
The initial FragmentName mentioned above is set in the getIntent() method that overrides the parent class.

The DashBoard list is not the same as the ordinary ListView. As mentioned above, the DashBoard list is implemented through the Fragment of Dashboard Summary. The sliding list of the entire settings is displayed in the ScrollView.
DashboardSummary.java
  1. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  2. Bundle savedInstanceState) {
  3. mLayoutInflater = inflater;
  4. mExt = UtilsExt.getMiscPlugin(this.getActivity());
  5. final View rootView = inflater.inflate(R.layout.dashboard, container, false);
  6. mDashboard = (ViewGroup) rootView.findViewById(R.id.dashboard_container);
  7. return rootView;
  8. }
layout: dashboard
  1. <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:id="@+id/dashboard"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:scrollbarStyle="outsideOverlay"
  6. android:clipToPadding="false">
  7. <LinearLayout
  8. android:id="@+id/dashboard_container"
  9. android:layout_width="match_parent"
  10. android:layout_height="match_parent"
  11. android:layout_gravity="center_horizontal"
  12. android:paddingStart="@dimen/dashboard_padding_start"
  13. android:paddingEnd="@dimen/dashboard_padding_end"
  14. android:paddingTop="@dimen/dashboard_padding_top"
  15. android:paddingBottom="@dimen/dashboard_padding_bottom"
  16. android:clipToPadding="false"
  17. android:orientation="vertical"
  18. />
  19. </ScrollView>
The list of slidable first-level menus we see in the settings is in this dashboard_container.
In onResume() of Dashboard Summary. java, Handler is used to execute rebuildUI() and start parsing layout.dashboard_category. File and generate the corresponding View view.
  1. private void rebuildUI(Context context) {
  2. if (!isAdded()) {
  3. Log.w(LOG_TAG, "Cannot build the DashboardSummary UI yet as the Fragment is not added");
  4. return;
  5. }
  6. long start = System.currentTimeMillis();
  7. final Resources res = getResources();
  8. mDashboard.removeAllViews();
  9. List<DashboardCategory> categories =
  10. ((SettingsActivity) context).getDashboardCategories(true);
  11. final int count = categories.size();
  12. for (int n = 0; n < count; n++) {
  13. DashboardCategory category = categories.get(n);
  14. View categoryView = mLayoutInflater.inflate(R.layout.dashboard_category, mDashboard,
  15. false);
  16. TextView categoryLabel = (TextView) categoryView.findViewById(R.id.category_title);
  17. categoryLabel.setText(category.getTitle(res));
  18. ViewGroup categoryContent =
  19. (ViewGroup) categoryView.findViewById(R.id.category_content);
  20. final int tilesCount = category.getTilesCount();
  21. for (int i = 0; i < tilesCount; i++) {
  22. DashboardTile tile = category.getTile(i);
  23. DashboardTileView tileView = new DashboardTileView(context);
  24. updateTileView(context, res, tile, tileView.getImageView(),
  25. tileView.getTitleTextView(), tileView.getStatusTextView());
  26. tileView.setTile(tile);
  27. if(tile != null && tile.extras != null && tile.extras.containsKey(CUSTOMIZE_ITEM_INDEX)){
  28. int index = tile.extras.getInt(CUSTOMIZE_ITEM_INDEX, -1);
  29. categoryContent.addView(tileView, index);
  30. } else {
  31. categoryContent.addView(tileView);
  32. }
  33. }
  34. // Add the category
  35. mDashboard.addView(categoryView);
  36. }
  37. long delta = System.currentTimeMillis() - start;
  38. Log.d(LOG_TAG, "rebuildUI took: " + delta + " ms");
  39. }
From the above code, you can see that the view corresponding to each setting item is Dashboard TileView. The relevant title, icon, and the corresponding pages to be opened are saved in Dashboard Tile Inside.
Then look at Dashboard TileView ,
  1. public DashboardTileView(Context context, AttributeSet attrs) {
  2. super(context, attrs);
  3. //zhangle update layout res at 20150608 dashboard_tile -> dashboard_tile_doov
  4. final View view = LayoutInflater.from(context).inflate(R.layout.dashboard_tile_doov, this);
  5. mImageView = (ImageView) view.findViewById(R.id.icon);
  6. mTitleTextView = (TextView) view.findViewById(R.id.title);
  7. mStatusTextView = (TextView) view.findViewById(R.id.status);
  8. mDivider = view.findViewById(R.id.tile_divider);
  9. setOnClickListener(this);
  10. setBackgroundResource(R.drawable.dashboard_tile_background);
  11. setFocusable(true);
  12. }
As you can see in the above code, the event effort we click on each of the settings is the processing event set in the constructor of Dashboard TileView.




Posted by visonardo on Tue, 26 Mar 2019 14:39:30 -0700