以跳转到蓝牙控制面板为例,控制面板如图所示:
其 Fragment 所在的位置是:
packages/apps/Settings/src/com/android/settings/connecteddevice/BluetoothDashboardFragment.java
第一步
要在 Settings的主要 Activity 中定义继承同一个父类的内部类,作为启动时承载BluetoothDashboardFragment 的 Activity:
packages/apps/Settings/src/com/android/settings/Settings.java
diff --git a/apps/Settings/src/com/android/settings/Settings.java b/apps/Settings/src/com/android/settings/Settings.java
index e94e647ff..959f6fbf8 100644
--- a/apps/Settings/src/com/android/settings/Settings.java
+++ b/apps/Settings/src/com/android/settings/Settings.java
@@ -230,6 +230,9 @@ public class Settings extends SettingsActivity {
// Top level categories for new IA
public static class NetworkDashboardActivity extends SettingsActivity {}
public static class ConnectedDeviceDashboardActivity extends SettingsActivity {}
+ //Add by huanghoufu
+ public static class BluetoothDashboardActivity extends SettingsActivity {}
+ //end
public static class PowerUsageSummaryActivity extends SettingsActivity { /* empty */ }
public static class AppAndNotificationDashboardActivity extends SettingsActivity {}
public static class StorageDashboardActivity extends SettingsActivity {}
第二步
将BluetoothDashboardActivity添加到 SettingsBaseActivity 的自动更新Fragment列表中,代码逻辑如下:
packages/apps/Settings/src/com/android/settings/SettingsActivity.java
(1) 更新方法及关键注释
private void updateTilesList() {
// Generally the items that are will be changing from these updates will
// not be in the top list of tiles, so run it in the background and the
// SettingsBaseActivity will pick up on the updates automatically.
AsyncTask.execute(() -> doUpdateTilesList());
}
(2)添加位置
diff --git a/apps/Settings/src/com/android/settings/SettingsActivity.java b/apps/Settings/src/com/android/settings/SettingsActivity.java
index 76739f049..ccd7051fd 100644
--- a/apps/Settings/src/com/android/settings/SettingsActivity.java
+++ b/apps/Settings/src/com/android/settings/SettingsActivity.java
@@ -627,6 +627,14 @@ public class SettingsActivity extends SettingsBaseActivity
private void doUpdateTilesList() {
……
+ //Add by huanghoufu
+ somethingChanged = setTileEnabled(changedList,
+ new ComponentName(packageName,
+ Settings.BluetoothDashboardActivity.class.getName()),
+ !UserManager.isDeviceInDemoMode(this) /* enabled */,
+ isAdmin) || somethingChanged;
+ //end
+
somethingChanged = setTileEnabled(changedList, new ComponentName(packageName,
Settings.PowerUsageSummaryActivity.class.getName()),
mBatteryPresent, isAdmin) || somethingChanged;
第三步
为了安全考虑,在切换Fragment时,程序会检查将要切换 Fragment 是否在可显示列表中:
packages/apps/Settings/src/com/android/settings/SettingsActivity.java
/**
* Switch to a specific Fragment with taking care of validation, Title and BackStack
*/
private Fragment switchToFragment(String fragmentName, Bundle args, boolean validate,
int titleResId, CharSequence title) {
Log.d(LOG_TAG, "Switching to fragment " + fragmentName);
if (validate && !isValidFragment(fragmentName)) {
throw new IllegalArgumentException("Invalid fragment for this activity: "
+ fragmentName);
}
Fragment f = Utils.getTargetFragment(this, fragmentName, args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_content, f);
if (titleResId > 0) {
transaction.setBreadCrumbTitle(titleResId);
} else if (title != null) {
transaction.setBreadCrumbTitle(title);
}
transaction.commitAllowingStateLoss();
getSupportFragmentManager().executePendingTransactions();
Log.d(LOG_TAG, "Executed frag manager pendingTransactions");
return f;
}
如果将要切换的 Fragment 不在显示列表中,则会抛出“Invalid fragment for this activity……”的异常,例如:
07-26 17:15:36.574 2151 2151 D SettingsActivity: Starting onCreate
07-26 17:15:36.632 2151 2151 D SettingsActivity: Starting to set activity title
07-26 17:15:36.633 2151 2151 D SettingsActivity: Done setting title
07-26 17:15:36.633 2151 2151 D SettingsActivity: Switching to fragment com.android.settings.connecteddevice.BluetoothDashboardFragment
07-26 17:15:36.669 2151 2151 D AndroidRuntime: Shutting down VM
07-26 17:15:36.670 2151 2151 E AndroidRuntime: FATAL EXCEPTION: main
07-26 17:15:36.670 2151 2151 E AndroidRuntime: Process: com.android.settings, PID: 2151
07-26 17:15:36.670 2151 2151 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.settings/com.android.settings.Settings$BluetoothDashboardActivity}: java.lang.IllegalArgumentException: Invalid fragment for this activity: com.android.settings.connecteddevice.BluetoothDashboardFragment
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3431)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3595)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:106)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.os.Looper.loop(Looper.java:223)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7664)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: Caused by: java.lang.IllegalArgumentException: Invalid fragment for this activity: com.android.settings.connecteddevice.BluetoothDashboardFragment
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at com.android.settings.SettingsActivity.switchToFragment(SettingsActivity.java:577)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at com.android.settings.SettingsActivity.launchSettingFragment(SettingsActivity.java:377)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at com.android.settings.SettingsActivity.onCreate(SettingsActivity.java:285)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8053)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.Activity.performCreate(Activity.java:8037)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3404)
07-26 17:15:36.670 2151 2151 E AndroidRuntime: ... 11 more
因此,需要在可显示名单中添加相应Fragment:
diff --git a/apps/Settings/src/com/android/settings/core/gateway/SettingsGateway.java b/apps/Settings/src/com/android/settings/core/gateway/SettingsGateway.java
index 77adbf040..76804a99b 100644
--- a/apps/Settings/src/com/android/settings/core/gateway/SettingsGateway.java
+++ b/apps/Settings/src/com/android/settings/core/gateway/SettingsGateway.java
@@ -63,6 +63,7 @@ import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment;
import com.android.settings.bugreporthandler.BugReportHandlerPicker;
import com.android.settings.connecteddevice.AdvancedConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.ConnectedDeviceDashboardFragment;
+import com.android.settings.connecteddevice.BluetoothDashboardFragment;
import com.android.settings.connecteddevice.PreviouslyConnectedDeviceDashboardFragment;
import com.android.settings.connecteddevice.usb.UsbDetailsFragment;
import com.android.settings.datausage.DataSaverSummary;
@@ -313,7 +314,10 @@ public class SettingsGateway {
/**
* A list of fragment that can be hosted by SettingsActivity. SettingsActivity will throw a
* security exception if the fragment it needs to display is not in this list.
*/
public static final String[] ENTRY_FRAGMENTS = {
GestureNavigationSettingsFragment.class.getName(),
InteractAcrossProfilesSettings.class.getName(),
InteractAcrossProfilesDetails.class.getName(),
- MediaControlsSettings.class.getName()
+ MediaControlsSettings.class.getName(),
+ //Add by huanghoufu
+ BluetoothDashboardFragment.class.getName()
+ //end
};
public static final String[] SETTINGS_FOR_RESTRICTED = {
@@ -356,5 +360,8 @@ public class SettingsGateway {
UserBackupSettingsActivity.class.getName(),
// Home page > Display
Settings.HdmiSettingsActivity.class.getName(),
+ //Add by huanghoufu
+ Settings.BluetoothDashboardActivity.class.getName()
+ //end
};
}
第四步
为 Fragment 在清单文件中添加隐试启动的意图即可:
注意,这里用于启动的自定义 action 是 android.settings.BLUETOOTH_DASHBOARD
diff --git a/apps/Settings/AndroidManifest.xml b/apps/Settings/AndroidManifest.xml
index e74734b98..faa34330e 100644
--- a/apps/Settings/AndroidManifest.xml
+++ b/apps/Settings/AndroidManifest.xml
@@ -233,6 +231,21 @@
android:value="true" />
</activity>
+ <!-- Add by huanghoufu -->
+ <activity
+ android:name=".Settings$BluetoothDashboardActivity">
+ <intent-filter android:priority="1">
+ <action android:name="android.settings.BLUETOOTH_DASHBOARD" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <meta-data android:name="com.android.settings.FRAGMENT_CLASS"
+ android:value="com.android.settings.connecteddevice.BluetoothDashboardFragment"/>
+ <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
+ android:value="true" />
+ </activity>
+ <!-- end -->
+
+