Framework StatusBar添加(wifi,bluetooth,gps,sound,rotate)控制面板
学android已经数月了,最近跳槽了一家做手机的公司,一去就是改framework层里的代码,那个纠结啊。第一个项目就是改statusbar,添加控制面板开关。
因为项目公司里任何的资源都带不出来,就用我自己的手机截图示范,跟android原生statusbar对比,源码主要以wifi为示例。
我做的项目是添加WIFI,BLUETOOTH,GPS,SOUND,自动旋转,个人感觉自动旋转是最让人纠结的,因为无法监听到状态的广播,不过可以监听数据库里的变化。我主要以WIFI项目示范,其他的四个提下我的思路,欢迎大家探讨。
修改的图片文件位置:
- platform\frameworks\base\packages\SystemUI\res\drawable-hdpi
添加switch_wifi_on.png,switch_wifi_off.png,switch_ind_on.png,switch_ind_off.png,swith_bg
修改的layout文件位置:
- platform\frameworks\base\packages\SystemUI\res\layout\status_bar_expanded.xml
中文
- platform\frameworks\base\packages\SystemUI\res\values-zh-rCN\strings.xml
- 英文
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">platform\frameworks\base\packages\SystemUI\res\values\strings.xml
代码文件位置:
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">platform\frameworks\base\packages\SystemUI\src\com\android\systemui\statusbar\StatusBarService.java
- </pre>在status_bar_expanded.xml里添加5个开关的排列,添加在最顶层的android:background="@drawable/title_bar_portrait"的这个LinearLayout的上面,那样控制面板就会显示在最顶层了,大家看下源码就知道了,下面添加的是wifi的,添加其他的开关继续在里面添加即可。
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code"> <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@drawable/switch_bg">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/switch_wifi"
- >
- <ImageView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/switch_wifi_img"/>
- <TextView android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/switch_wifi_text"/>
- <ImageView android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:id="@+id/switch_ind_state"/>
- </LinearLayout>
- </LinearLayout>
功能控制添加在StatusBarService.xml文件里:
- 定义变量:
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code"> private WifiManager mWifiManager;
- private IntentFilter mWifiStateFilter;
- LinearLayout mWifiState;
- ImageView mWifiImg;
- TextView mWifiText;
- ImageView mWifiInd;
- </pre>在private void makeStatusBarView(Context context)定义这些变量的值及要用的广播等。
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code"> mWifiState=(LinearLayout)expanded.findViewById(R.id.switch_wifi);
- mWifiState.setOnClickListener(mWifiChangeListener);
- mWifiImg=(ImageView)expanded.findViewById(R.id.switch_wifi_img);
- mWifiText=(TextView)expanded.findViewById(R.id.switch_wifi_text);
- mWifiInd=(ImageView)expanded.findViewById(R.id.switch_ind_state);
- mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
- mWifiStateFilter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
- registerReceiver(mWifiStateReceiver, mWifiStateFilter);
- 然后在其他位置添加广播,定义的5个状态的样式及点击切换等。
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code">
- private final BroadcastReceiver mWifiStateReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
- handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
- mWifiManager.getWifiState()));
- }
- }
- };
- private void handleWifiStateChanged(int wifiState) {
- switch(wifiState){
- case WifiManager.WIFI_STATE_DISABLED:
- mWifiImg.setBackgroundResource(R.drawable.switch_wifi_off);
- mWifiText.setText(R.string.switch_wifi_text_off);
- mWifiInd.setBackgroundResource(R.drawable.switch_ind_off);
- break;
- case WifiManager.WIFI_STATE_DISABLING:
- mWifiImg.setBackgroundResource(R.drawable.switch_wifi_on);
- mWifiText.setText(R.string.switch_wifi_text_closing);
- mWifiInd.setBackgroundResource(R.drawable.switch_ind_on);
- break;
- case WifiManager.WIFI_STATE_ENABLED:
- mWifiImg.setBackgroundResource(R.drawable.switch_wifi_on);
- mWifiText.setText(R.string.switch_wifi_text_on);
- mWifiInd.setBackgroundResource(R.drawable.switch_ind_on);
- break;
- case WifiManager.WIFI_STATE_ENABLING:
- mWifiImg.setBackgroundResource(R.drawable.switch_wifi_off);
- mWifiText.setText(R.string.switch_wifi_text_opening);
- mWifiInd.setBackgroundResource(R.drawable.switch_ind_off);
- break;
- case WifiManager.WIFI_STATE_UNKNOWN:
- mWifiImg.setBackgroundResource(R.drawable.switch_wifi_off);
- mWifiText.setText(R.string.switch_wifi_text_off);
- mWifiInd.setBackgroundResource(R.drawable.switch_wifi_off);
- break;
- }
- }
- private View.OnClickListener mWifiChangeListener = new View.OnClickListener() {
- public void onClick(View v) {
- if(mWifiManager.getWifiState()==WifiManager.WIFI_STATE_DISABLED){
- mWifiManager.setWifiEnabled(true);
- handleWifiStateChanged(WifiManager.WIFI_STATE_ENABLED);
- }else{
- mWifiManager.setWifiEnabled(false);
- handleWifiStateChanged(WifiManager.WIFI_STATE_DISABLED);
- }
- }
- };
- </pre>在strings.xml文件里添加
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code"> <string name="switch_wifi_text_on">wifi</string>
- <string name="switch_wifi_text_off">wifi</string>
- <string name="switch_wifi_text_opening">opening</string>
- <string name="switch_wifi_text_closing">closing</string>
就这样一个wifi的控制开关就完成了。因为是在家里写的,所以完整的代码无法打出来,就当给大家点提示吧。
- wifi的源文件可以参考:<pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">
- platform\packages\apps\Settings\src\com\android\settings\wifi
- </pre>在widget里已经有一个范例了,大家可以参考里面的广播。
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">platform\packages\apps\Settings\src\com\android\settings\widget\SettingsAppWidgetProvider.java
- public void onReceive(Context context, Intent intent) {
- super.onReceive(context, intent);
- String action = intent.getAction();
- if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
- sWifiState.onActualStateChange(context, intent);
- } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
- sBluetoothState.onActualStateChange(context, intent);
- } else if (LocationManager.PROVIDERS_CHANGED_ACTION.equals(action)) {
- sGpsState.onActualStateChange(context, intent);
- } else if (SyncStorageEngine.SYNC_CONNECTION_SETTING_CHANGED_INTENT.getAction()
- .equals(action)) {
- sSyncState.onActualStateChange(context, intent);
- } else if (intent.hasCategory(Intent.CATEGORY_ALTERNATIVE)) {
- Uri data = intent.getData();
- int buttonId = Integer.parseInt(data.getSchemeSpecificPart());
- if (buttonId == BUTTON_WIFI) {
- sWifiState.toggleState(context);
- } else if (buttonId == BUTTON_BRIGHTNESS) {
- toggleBrightness(context);
- } else if (buttonId == BUTTON_SYNC) {
- sSyncState.toggleState(context);
- } else if (buttonId == BUTTON_GPS) {
- sGpsState.toggleState(context);
- } else if (buttonId == BUTTON_BLUETOOTH) {
- sBluetoothState.toggleState(context);
- }
- } else {
- // Don't fall-through to updating the widget. The Intent
- // was something unrelated or that our super class took
- // care of.
- return;
- }
- // State changes fall through
- updateWidget(context);
- }
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">bluetooth也是一样的注册广播,监听变化。
- BluetoothAdapter,主要是这个类的相关应用。
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code"> private void handleStateChanged(int state) {
- switch (state) {
- case BluetoothAdapter.STATE_TURNING_ON:
- mCheckBox.setSummary(R.string.wifi_starting);
- mCheckBox.setEnabled(false);
- break;
- case BluetoothAdapter.STATE_ON:
- mCheckBox.setChecked(true);
- mCheckBox.setSummary(null);
- mCheckBox.setEnabled(true);
- break;
- case BluetoothAdapter.STATE_TURNING_OFF:
- mCheckBox.setSummary(R.string.wifi_stopping);
- mCheckBox.setEnabled(false);
- break;
- case BluetoothAdapter.STATE_OFF:
- mCheckBox.setChecked(false);
- mCheckBox.setSummary(mOriginalSummary);
- mCheckBox.setEnabled(true);
- break;
- default:
- mCheckBox.setChecked(false);
- mCheckBox.setSummary(R.string.wifi_error);
- mCheckBox.setEnabled(true);
- }
- }
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- handleBluetoothStateChanged(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, mBluetoothAdapter.getState()););
- }
- };
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">GPS则是监听数据库里的开关变化
- 参考写法:
- <span style="font-family:monospace;BACKGROUND-COLOR: rgb(240,240,240); WHITE-SPACE: pre"></span>
- platform\packages\apps\Settings\src\com\android\settings\widget\SettingsAppWidgetProvider.java
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code"><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code"> private static final class GpsStateTracker extends StateTracker {
- public int getButtonId() { return R.id.img_gps; }
- public int getIndicatorId() { return R.id.ind_gps; }
- public int getButtonImageId(boolean on) {
- return on ? R.drawable.ic_appwidget_settings_gps_on
- : R.drawable.ic_appwidget_settings_gps_off;
- }
- @Override
- public int getActualState(Context context) {
- ContentResolver resolver = context.getContentResolver();
- boolean on = Settings.Secure.isLocationProviderEnabled(
- resolver, LocationManager.GPS_PROVIDER);
- return on ? STATE_ENABLED : STATE_DISABLED;
- }
- @Override
- public void onActualStateChange(Context context, Intent unused) {
- // Note: the broadcast location providers changed intent
- // doesn't include an extras bundles saying what the new value is.
- setCurrentState(context, getActualState(context));
- }
- @Override
- public void requestStateChange(final Context context, final boolean desiredState) {
- final ContentResolver resolver = context.getContentResolver();
- new AsyncTask<Void, Void, Boolean>() {
- @Override
- protected Boolean doInBackground(Void... args) {
- Settings.Secure.setLocationProviderEnabled(
- resolver,
- LocationManager.GPS_PROVIDER,
- desiredState);
- return desiredState;
- }
- @Override
- protected void onPostExecute(Boolean result) {
- setCurrentState(
- context,
- result ? STATE_ENABLED : STATE_DISABLED);
- updateWidget(context);
- }
- }.execute();
- }
- }
- sound则是参考
- platform\packages\apps\Settings\src\com\android\settings\SoundSettings.java
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code">
- private BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- updateState(false);
- }
- }
- };
- IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
- registerReceiver(mReceiver, filter);
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">
- 自动选择则是参考:
- platform\packages\apps\Settings\src\com\android\settings\DisplaySettings.java
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code"><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code"> public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
- if (preference == mAccelerometer) {
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code">//设置数据库里自动选择的开关,1为开,0为关
- <pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="java" name="code"> Settings.System.putInt(getContentResolver(),
- Settings.System.ACCELEROMETER_ROTATION,
- mAccelerometer.isChecked() ? 1 : 0);
- }
- return true;
- }
写一个类继承于ContentResolver,用于监听数据库里的变化,然后即可。
以上代码皆可在android源码里找到相关资源,欢迎大家互相讨论。
- </pre><pre style="BACKGROUND-COLOR: rgb(240,240,240); MARGIN: 4px 0px" class="html" name="code">
- </pre><br /><p></p><p></p><p></p>