目录
源码:Android 6.0
应用:Settings
目录:Settings\src\com\android\settings\bluetooth
上一回我们简单分析了Settings加载和启动过程(《Android 6.0 Settings源码简单分析》),现在我们来看看Settings中的一个小功能:蓝牙。
我们前面分析到,Settings界面上布局显示的内容是在 SettingsActivity.getDashboardCategories()中解析R.xml.dashboard_categories 内容的,然后在SettingsActivity.updateTilesList()中判断是否需要显示或影藏。(这部分内容在《Android 6.0 Settings源码简单分析》中的第5和第6索引中,有兴趣的可以看看)
这次我们重点看蓝牙,因此看看R.xml.dashboard_categories中对蓝牙的描述。
......
<!-- Bluetooth -->
<dashboard-tile
android:id="@+id/bluetooth_settings"
android:fragment="com.android.settings.bluetooth.BluetoothSettings"
android:icon="@drawable/ic_settings_bluetooth"
android:title="@string/bluetooth_settings_title" />
......
上面包含的信息有id,Fragment,icon和title,BluetoothSettings就是蓝牙的入口类。
既然知道了入口,那我们开始进入正题吧、
0、BluetoothSettings.java的继承关系
我们先看看BluetoothSettings的继承关系,先了解这些关系对我们往下分析很有帮助的。
public final class BluetoothSettings extends DeviceListPreferenceFragment implements Indexable {
......
}
public abstract class DeviceListPreferenceFragment extends
RestrictedSettingsFragment implements BluetoothCallback {
......
}
public abstract class RestrictedSettingsFragment extends SettingsPreferenceFragment {
......
}
public abstract class SettingsPreferenceFragment extends InstrumentedPreferenceFragment
implements DialogCreatable {
......
}
public abstract class InstrumentedPreferenceFragment extends PreferenceFragment {
......
}
public abstract class PreferenceFragment extends Fragment implements
PreferenceManager.OnPreferenceTreeClickListener {
......
}
从上面可以了解到BluetoothSettings的祖祖祖祖父是Fragment,额,是不是很熟悉啦。关于Fragment的简单介绍可以看看我前几天写(摘抄)的《Fragment生命周期的详情》,这里就不再重复。
恩恩,有了前面的Fragment的生命周期知识,我们先看BluetoothSettings的生命周期,首先看的是onCreate(),额,神马,竟然没有实现onCreate(),好吧,那我们只好找他老爸了。
PS:后面分析是根据Fragment的生命周期流程去分析的,这是蓝牙初始化界面的流程。
1、DeviceListPreferenceFragment.onCreate()
//DeviceListPreferenceFragment.java
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
初始化LocalBluetoothManager
mLocalManager = Utils.getLocalBtManager(getActivity());
if (mLocalManager == null) {
看看是否支持蓝牙,如果不支持,Settings中的蓝牙选项是打不开的
Log.e(TAG, "Bluetooth is not supported on this device");
return;
}
mLocalAdapter = mLocalManager.getBluetoothAdapter();
1.1 、这是个抽象方法,具体实现在其子类BluetoothSettings
addPreferencesForActivity();
mDeviceListGroup = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST);
}
addPreferencesForActivity是个抽象方法,具体实现是在BluetoothSettings中。只有该设备支持蓝牙才会继续执行,要不然会点不进入的。
1.1、BluetoothSettings.addPreferencesForActivity()
其实这里没有做啥工作,就是添加布局和设置有Menu栏。
@Override
void addPreferencesForActivity() {
addPreferencesFromResource(R.xml.bluetooth_settings);
setHasOptionsMenu(true);
}
而bluetooth_settings.xml里面啥都没放就一个PreferenceScreen(对于PreferenceScreen使用,后续再说)
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/bluetooth_settings" >
</PreferenceScreen>
2、BluetoothSettings.onActivityCreated()
// BluetoothSettings.java
@Override
public void onActivityCreated(Bundle savedInstanceState) {
......
一开始进入显示空的界面(蓝牙没有开启时的界面)
mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
getListView().setEmptyView(mEmptyView);
mEmptyView.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
初始化mSwitchBar蓝牙开关键
final SettingsActivity activity = (SettingsActivity) getActivity();
mSwitchBar = activity.getSwitchBar();
2.1 初始化mBluetoothEnabler
mBluetoothEnabler = new BluetoothEnabler(activity, mSwitchBar);
显示SwitchBar开关
mBluetoothEnabler.setupSwitchBar();
}
2.1 初始化mBluetoothEnabler
//BluetoothEnabler.java
public BluetoothEnabler(Context context, SwitchBar switchBar) {
......
初始化LocalBluetoothManager
LocalBluetoothManager manager = Utils.getLocalBtManager(context);
if (manager == null) {
如果不支持,就把Switchbar设置为不能使用// Bluetooth is not supported
mLocalAdapter = null;
mSwitch.setEnabled(false);
} else {
mLocalAdapter = manager.getBluetoothAdapter();
}
mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
}
4、BluetoothSettings.onResume()
//BluetoothSettings.java
@Override
public void onResume() {
// resume BluetoothEnabler before calling super.onResume() so we don't get
// any onDeviceAdded() callbacks before setting up view in updateContent()
if (mBluetoothEnabler != null) {
4.1 更新蓝牙开关的状态和注册广播
mBluetoothEnabler.resume(getActivity());
}
......
检测是否被限制操作蓝牙
if (isUiRestricted()) {
setDeviceListGroup(getPreferenceScreen());
removeAllDevices();
mEmptyView.setText(R.string.bluetooth_empty_list_user_restricted);
return;
}
注册蓝牙名字改变广播(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED)
getActivity().registerReceiver(mReceiver, mIntentFilter);
if (mLocalAdapter != null) {
4.3、更新蓝牙状态
updateContent(mLocalAdapter.getBluetoothState());
}
}
4.1、mBluetoothEnabler.resume()
//BluetoothEnabler.java
public void resume(Context context) {
if (mLocalAdapter == null) {
mSwitch.setEnabled(false);
return;
}
......
// Bluetooth state is not sticky, so set it manually
更新蓝牙开关状态(STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF、STATE_OFF)
handleStateChanged(mLocalAdapter.getBluetoothState());
4.2 监听Switch bar 开关的改变事件
mSwitchBar.addOnSwitchChangeListener(this);
注册蓝牙状态改变广播(BluetoothAdapter.ACTION_STATE_CHANGED)
mContext.registerReceiver(mReceiver, mIntentFilter);
mValidListener = true;
}
4.2、 监听Switch bar 开关的改变事件
@Override
public void onSwitchChanged(Switch switchView, boolean isChecked) {
// Show toast message if Bluetooth is not allowed in airplane mode
飞行模式时不能改变蓝牙
if (isChecked &&
!WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
// Reset switch to off
正在更新状态时设置不能点击
switchView.setChecked(false);
}
MetricsLogger.action(mContext, MetricsLogger.ACTION_BLUETOOTH_TOGGLE, isChecked);
更新蓝牙状态
if (mLocalAdapter != null) {
mLocalAdapter.setBluetoothEnabled(isChecked);
}
正在更新状态时设置不能点击
mSwitch.setEnabled(false);
}
如果蓝牙STATE_TURNING_ON或者STATE_TURNING_OFF时需要把mSwitch置为不能点击作用。
4.3、BluetoothSettings.updateContent()
//BluetoothSettings.java
private void updateContent(int bluetoothState) {
final PreferenceScreen preferenceScreen = getPreferenceScreen();
int messageId = 0;
switch (bluetoothState) {
case BluetoothAdapter.STATE_ON:
preferenceScreen.removeAll();
preferenceScreen.setOrderingAsAdded(true);
mDevicePreferenceMap.clear();
if (isUiRestricted()) {
messageId = R.string.bluetooth_empty_list_user_restricted;
break;
}
添加配对过的或已经配对蓝牙
// Paired devices category
if (mPairedDevicesCategory == null) {
mPairedDevicesCategory = new PreferenceCategory(getActivity());
} else {
mPairedDevicesCategory.removeAll();
}
addDeviceCategory(mPairedDevicesCategory,
R.string.bluetooth_preference_paired_devices,
BluetoothDeviceFilter.BONDED_DEVICE_FILTER, true);
int numberOfPairedDevices = mPairedDevicesCategory.getPreferenceCount();
if (isUiRestricted() || numberOfPairedDevices <= 0) {
preferenceScreen.removePreference(mPairedDevicesCategory);
}
添加扫描到的所有蓝牙
// Available devices category
if (mAvailableDevicesCategory == null) {
mAvailableDevicesCategory = new BluetoothProgressCategory(getActivity());
mAvailableDevicesCategory.setSelectable(false);
} else {
mAvailableDevicesCategory.removeAll();
}
addDeviceCategory(mAvailableDevicesCategory,
R.string.bluetooth_preference_found_devices,
BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER, mInitialScanStarted);
int numberOfAvailableDevices = mAvailableDevicesCategory.getPreferenceCount();
if (!mInitialScanStarted) {
startScanning();
}
if (mMyDevicePreference == null) {
mMyDevicePreference = new Preference(getActivity());
}
mMyDevicePreference.setSummary(getResources().getString(
R.string.bluetooth_is_visible_message, mLocalAdapter.getName()));
mMyDevicePreference.setSelectable(false);
preferenceScreen.addPreference(mMyDevicePreference);
更新MENU菜单的选项
getActivity().invalidateOptionsMenu();
// mLocalAdapter.setScanMode is internally synchronized so it is okay for multiple
// threads to execute.
if (mInitiateDiscoverable) {
// Make the device visible to other devices.
mLocalAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
mInitiateDiscoverable = false;
}
return; // not break
case BluetoothAdapter.STATE_TURNING_OFF:
messageId = R.string.bluetooth_turning_off;
break;
case BluetoothAdapter.STATE_OFF:
setOffMessage();
if (isUiRestricted()) {
messageId = R.string.bluetooth_empty_list_user_restricted;
}
break;
case BluetoothAdapter.STATE_TURNING_ON:
messageId = R.string.bluetooth_turning_on;
mInitialScanStarted = false;
break;
}
setDeviceListGroup(preferenceScreen);
removeAllDevices();
if (messageId != 0) {
mEmptyView.setText(messageId);
}
if (!isUiRestricted()) {
getActivity().invalidateOptionsMenu();
}
}
上面代码看起很多,其实就是当蓝牙开或关时更新界面布局和文字等信息。上面蓝牙状态有四种,STATE_TURNING_ON、STATE_ON、STATE_TURNING_OFF、STATE_OFF,如果蓝牙STATE_TURNING_ON或者STATE_TURNING_OFF时需要把Switch bar置为不能点击作用。
4.4、BluetoothSettings.onOptionsItemSelected()菜单选项功能
//BluetoothSettings.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ID_SCAN:扫描附近可用蓝牙
if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) {
MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BLUETOOTH_SCAN);
startScanning();
}
return true;
case MENU_ID_RENAME_DEVICE:蓝牙重新命名
MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BLUETOOTH_RENAME);
new BluetoothNameDialogFragment().show(
getFragmentManager(), "rename device");
return true;
case MENU_ID_SHOW_RECEIVED:显示接收文件
MetricsLogger.action(getActivity(), MetricsLogger.ACTION_BLUETOOTH_FILES);
Intent intent = new Intent(BTOPP_ACTION_OPEN_RECEIVED_FILES);
getActivity().sendBroadcast(intent);
return true;
}
return super.onOptionsItemSelected(item);
}
蓝牙界面大致就这样了,其他状态的改变暂时没有详细列出,涉及到BluetoothAdapter的代码不在Settings中这个后续深入了解。