版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yin1031468524/article/details/78276679
在Android手机上插入耳机后,状态栏会显示耳机图标,之前手机都是在手机最上面状态栏右边的区域显示,在Android 7.1.1上,插入耳机后不会显示这个图标,而是在最左边有个耳机的通知,这个通知是在下面的类中监听耳机插入或拔出的广播来显示或取消插入耳机的通
packages/services/Telecomm/src/com/android/server/telecom/TtyManager.java
@Override
public void onWiredHeadsetPluggedInChanged(boolean oldIsPluggedIn, boolean newIsPluggedIn) {
Log.v(this, "onWiredHeadsetPluggedInChanged");
updateCurrentTtyMode();
if (newIsPluggedIn) {
showHeadSetPlugin();
} else {
cancelHeadSetPlugin();
}
}
void showHeadSetPlugin() {
Log.v(TtyManager.this, "showHeadSetPlugin()...");
String titleText = mContext.getString(
R.string.headset_plugin_view_title);
String expandedText = mContext.getString(
R.string.headset_plugin_view_text);
Notification notification = new Notification();
notification.icon = android.R.drawable.stat_sys_headset;
notification.flags |= Notification.FLAG_NO_CLEAR;
notification.tickerText = titleText;
// create the target network operators settings intent
Intent intent = new Intent("android.intent.action.NO_ACTION");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
notification.setLatestEventInfo(mContext, titleText, expandedText, pi);
mNotificationManager.notify(HEADSET_PLUGIN_NOTIFICATION, notification);
}
void cancelHeadSetPlugin() {
Log.v(TtyManager.this, "cancelHeadSetPlugin()...");
mNotificationManager.cancel(HEADSET_PLUGIN_NOTIFICATION);
}
实际上在状态栏右边区域也有显示一些系统图标(蓝牙、闹钟等)的位置,接下来就根据源码说下怎么在状态栏添加显示系统图标,以耳机图标为例:
1、在系统framework配置文件中添加需要显示的图标
frameworks/base/core/res/res/values/config.xml
<string-array name="config_statusBarIcons">
<item><xliff:g id="id">@string/status_bar_headset</xliff:g></item>
</string-array>
<string translatable="false" name="status_bar_headset">headset</string>
2、在PhoneStatusBarPolicy.java中设置图标显示
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController,
CastController cast, HotspotController hotspot, UserInfoController userInfoController,
BluetoothController bluetooth, RotationLockController rotationLockController,
DataSaverController dataSaver) {
...
mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
...
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
// listen for broadcasts
IntentFilter filter = new IntentFilter();
filter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
filter.addAction(AudioManager.ACTION_HEADSET_PLUG); //监听耳机插入广播
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
filter.addAction(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
// bluetooth status
updateBluetooth();
...
}
注册广播,根据不同action处理不同逻辑
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
updateAlarm();
} else if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
updateHeadsetPlug(intent); //更新耳机图标
}
}
};
调用updateHeadsetPlug更新耳机图标
private void updateHeadsetPlug(Intent intent) {
boolean connected = intent.getIntExtra("state", 0) != 0;
boolean hasMic = intent.getIntExtra("microphone", 0) != 0;
if (connected) {
String contentDescription = mContext.getString(hasMic
? R.string.accessibility_status_bar_headset
: R.string.accessibility_status_bar_headphones);
mIconController.setIcon(mSlotHeadset, hasMic ? R.drawable.ic_headset_mic
: R.drawable.ic_headset, contentDescription);
mIconController.setIconVisibility(mSlotHeadset, true);
} else {
mIconController.setIconVisibility(mSlotHeadset, false);
}
}
需要注意的是在上面mIconController.setIcon和mIconController.setIconVisibility中有个坑,在这两个方法中都会调用到下面的handleSet,如果遇到这样的图标不显示,可以在下面的view.set后添加一行view.getVisibility()看当前view是否显示
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
private void handleSet(int index, StatusBarIcon icon) {
int viewIndex = getViewIndex(index);
StatusBarIconView view = (StatusBarIconView) mStatusIcons.getChildAt(viewIndex);
view.set(icon); //这个地方有个坑,继续往下看
view = (StatusBarIconView) mStatusIconsKeyguard.getChildAt(viewIndex);
view.set(icon);
applyIconTint();
}
在上面的view.set中有个需要注意的地方,除了根据当前view是否可见,还要根据mBlocked确定是否显示当前图标
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
/**
* Returns whether the set succeeded.
*/
public boolean set(StatusBarIcon icon) {
...
if (!visibilityEquals) {
setVisibility(icon.visible && !mBlocked ? VISIBLE : GONE); //坑来了,除了根据当前view是否可见,还要根据mBlocked确定是否显示
}
return true;
}
mBlocked是那来的呢,我们接下来看看下面代码,上面的StatusBarIconView是通过mStatusIcons.getChildAt(viewIndex)获取的,这个view是在调用StatusBarIconController@setIcon中创建的
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
private void addSystemIcon(int index, StatusBarIcon icon) {
String slot = getSlot(index);
int viewIndex = getViewIndex(index);
boolean blocked = mIconBlacklist.contains(slot);
StatusBarIconView view = new StatusBarIconView(mContext, slot, null, blocked);
view.set(icon);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
lp.setMargins(mIconHPadding, 0, mIconHPadding, 0);
mStatusIcons.addView(view, viewIndex, lp);
view = new StatusBarIconView(mContext, slot, null, blocked);
view.set(icon);
mStatusIconsKeyguard.addView(view, viewIndex, new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize));
applyIconTint();
}
上面的blocked是根据mIconBlacklist来获取相应值的,而mIconBlacklist是通过下面的方法赋值的
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@Override
public void onTuningChanged(String key, String newValue) {
if (!ICON_BLACKLIST.equals(key)) {
return;
}
mIconBlacklist.clear();
mIconBlacklist.addAll(getIconBlacklist(newValue));
ArrayList<StatusBarIconView> views = new ArrayList<StatusBarIconView>();
// Get all the current views.
for (int i = 0; i < mStatusIcons.getChildCount(); i++) {
views.add((StatusBarIconView) mStatusIcons.getChildAt(i));
}
// Remove all the icons.
for (int i = views.size() - 1; i >= 0; i--) {
removeIcon(views.get(i).getSlot());
}
// Add them all back
for (int i = 0; i < views.size(); i++) {
setIcon(views.get(i).getSlot(), views.get(i).getStatusBarIcon());
}
}
getIconBlacklist实现如下
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
public static ArraySet<String> getIconBlacklist(String blackListStr) {
ArraySet<String> ret = new ArraySet<String>();
if (blackListStr == null) {
blackListStr = "rotate,headset"; //headset默认是黑名单里的,所以没有显示出来
}
String[] blacklist = blackListStr.split(",");
for (String slot : blacklist) {
if (!TextUtils.isEmpty(slot)) {
ret.add(slot);
}
}
return ret;
}
记录下插入耳机图标显示的逻辑,免得以后每次都需要花费时间看这些代码