今天来看一下android 手机的状态栏的源码流程分析。
相关类
frameworks\base\services\java\com\android\server\SystemServer.java
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.java
frameworks\base\packages\SystemUI\res\values\config.xml
systemui\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java
app\src\main\java\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment
app\src\main\java\com\android\systemui\statusbar\phone\StatusBarWindowManager
app\src\main\java\com\android\systemui\statusbar\phone\PhoneStatusBarPolicy.java
settingslib\src\main\java\com\android\settingslib\graph\BatteryMeterDrawableBase
frameworks\base\services\java\com\android\server\SystemServer.java
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIService.java
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
// 在这里启动
// For debugging RescueParty
if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
throw new RuntimeException();
}
if (Build.IS_DEBUGGABLE) {
// b/71353150 - looking for leaked binder proxies
BinderInternal.nSetBinderProxyCountEnabled(true);
BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
BinderInternal.setBinderProxyCountCallback(
new BinderInternal.BinderProxyLimitListener() {
@Override
public void onLimitReached(int uid) {
Slog.w(SystemUIApplication.TAG,
"uid " + uid + " sent too many Binder proxies to uid "
+ Process.myUid());
}
}, Dependency.get(Dependency.MAIN_HANDLER));
}
}
frameworks\base\packages\SystemUI\src\com\android\systemui\SystemUIApplication.java
public void startServicesIfNeeded() {
String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
startServicesIfNeeded(names);
}
//这里可以看到有一个array的数组需要启动
private void startServicesIfNeeded(String[] services) {
if (mServicesStarted) {
return;
}
mServices = new SystemUI[services.length];
if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleted = true;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
Log.v(TAG, "Starting SystemUI services for user " +
Process.myUserHandle().getIdentifier() + ".");
TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
Trace.TRACE_TAG_APP);
log.traceBegin("StartServices");
final int N = services.length;
for (int i = 0; i < N; i++) {
String clsName = services[i];
if (DEBUG) Log.d(TAG, "loading: " + clsName);
log.traceBegin("StartServices" + clsName);
long ti = System.currentTimeMillis();
Class cls;
try {
cls = Class.forName(clsName);
mServices[i] = (SystemUI) cls.newInstance();
} catch(ClassNotFoundException ex){
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
}
mServices[i].mContext = this;
mServices[i].mComponents = mComponents;
if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
mServices[i].start(); // 每个都从这里启动
log.traceEnd();
// Warn if initialization of component takes too long
ti = System.currentTimeMillis() - ti;
if (ti > 1000) {
Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
}
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
这里其实大家不要被名称搞混了,这不是服务,而是继承systemUI的类。通过start方法去加载启动
frameworks\base\packages\SystemUI\res\values\config.xml
这里可以看到有这么多类需要启动加载。
<string-array name="config_systemUIServiceComponents" translatable="false">
<item>com.android.systemui.Dependencyitem>
<item>com.android.systemui.util.NotificationChannelsitem>
<item>com.android.systemui.statusbar.CommandQueue$CommandQueueStartitem>
<item>com.android.systemui.keyguard.KeyguardViewMediatoritem>
<item>com.android.systemui.recents.Recentsitem>
<item>com.android.systemui.volume.VolumeUIitem>
<item>com.android.systemui.stackdivider.Divideritem>
<item>com.android.systemui.SystemBarsitem>
<item>com.android.systemui.usb.StorageNotificationitem>
<item>com.android.systemui.power.PowerUIitem>
<item>com.android.systemui.media.RingtonePlayeritem>
<item>com.android.systemui.keyboard.KeyboardUIitem>
<item>com.android.systemui.pip.PipUIitem>
<item>com.android.systemui.shortcut.ShortcutKeyDispatcheritem>
<item>@string/config_systemUIVendorServiceComponentitem>
<item>com.android.systemui.util.leak.GarbageMonitor$Serviceitem>
<item>com.android.systemui.LatencyTesteritem>
<item>com.android.systemui.globalactions.GlobalActionsComponentitem>
<item>com.android.systemui.ScreenDecorationsitem>
<item>com.android.systemui.fingerprint.FingerprintDialogImplitem>
<item>com.android.systemui.SliceBroadcastRelayHandleritem>
string-array>
这里只分析com.android.systemui.SystemBars的流程
systemui\app\src\main\java\com\android\systemui\SystemBars
@Override
public void start() {
if (DEBUG) Log.d(TAG, "start");
createStatusBrFromConfig();
}
private void createStatusBarFromConfig() {
if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
final String clsName = mContext.getString(R.string.config_statusBarComponent);
// com.android.systemui.statusbar.phone.StatusBar
// 这里这个采用反射
if (clsName == null || clsName.length() == 0) {
throw andLog("No status bar component configured", null);
}
Class<?> cls = null;
try {
cls = mContext.getClassLoader().loadClass(clsName);
} catch (Throwable t) {
throw andLog("Error loading status bar component: " + clsName, t);
}
try {
mStatusBar = (SystemUI) cls.newInstance();
} catch (Throwable t) {
throw andLog("Error creating status bar component: " + clsName, t);
}
mStatusBar.mContext = mContext;
mStatusBar.mComponents = mComponents;
mStatusBar.start();
if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
}
systemui\app\src\main\java\com\android\systemui\statusbar\phone\StatusBar.java
@Override
public void start() {
... 省略部分代码,只保留文章相关的
createAndAddWindows() ;
...
}
private void addStatusBarWindow() {
makeStatusBarView();// 调用此处方法
mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,
new RemoteInputController.Delegate() {
public void setRemoteInputActive(NotificationData.Entry entry,
boolean remoteInputActive) {
mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
entry.row.notifyHeightChanged(true /* needsAnimation */);
updateFooter();
}
public void lockScrollTo(NotificationData.Entry entry) {
mStackScroller.lockScrollTo(entry.row);
}
public void requestDisallowLongPressAndDismiss() {
mStackScroller.requestDisallowLongPress();
mStackScroller.requestDisallowDismiss();
}
});
mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);
mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // 这个方法里面其实就可以看出是个window
}
protected void makeStatusBarView() {
inflateStatusBarWindow(context); // 加载布局,下面分析
FragmentHostManager.get(mStatusBarWindow)
.addTagListener(CollapsedStatusBarFragment.TAG, new FragmentHostManager.FragmentListener() {
@Override
public void onFragmentViewCreated(String tag, Fragment fragment) {
CollapsedStatusBarFragment statusBarFragment =
(CollapsedStatusBarFragment) fragment;
//这里这个fragment 才是我们的状态栏的fragment statusBarFragment.initNotificationIconArea(mNotificationIconAreaController);
mStatusBarView = (PhoneStatusBarView) fragment.getView();
mStatusBarView.setBar(StatusBar.this);
mStatusBarView.setPanel(mNotificationPanel);
mStatusBarView.setScrimController(mScrimController);
mStatusBarView.setBouncerShowing(mBouncerShowing);
if (mHeadsUpAppearanceController != null) {
// This view is being recreated, let's destroy the old one
mHeadsUpAppearanceController.destroy();
}
mHeadsUpAppearanceController = new HeadsUpAppearanceController(
mNotificationIconAreaController, mHeadsUpManager, mStatusBarWindow);
StatusBar.this.setAreThereNotifications();
StatusBar.this.checkBarModes();
/// M: add for plmn display feature @{
StatusBar.this.attachPlmnPlugin();
///@}
}
}).getFragmentManager()
.beginTransaction()
.replace(R.id.status_bar_container, new CollapsedStatusBarFragment(),
CollapsedStatusBarFragment.TAG) // 这可以看到这里也是在加载这个fragment 状态栏
.commit();
mIconController = Dependency.get(StatusBarIconController.class);
}
// 加载布局
protected void inflateStatusBarWindow(Context context) {
mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
R.layout.super_status_bar, null);
}
app\src\main\java\com\android\systemui\statusbar\phone\CollapsedStatusBarFragment
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.status_bar, container, false);
// 这个布局文件就是我们手机的状态栏
}
app\src\main\java\com\android\systemui\statusbar\phone\StatusBarWindowManager
public void add(View statusBarView, int barHeight) {
// Now that the status bar window encompasses the sliding panel and its
// translucent backdrop, the entire thing is made TRANSLUCENT and is
// hardware-accelerated.
mLp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
barHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mStatusBarView = statusBarView;
mBarHeight = barHeight;
mWindowManager.addView(mStatusBarView, mLp); // 可以看到这个其实也是一个window
mLpChanged = new WindowManager.LayoutParams();
mLpChanged.copyFrom(mLp);
}
到此,状态栏就启动了。接下来说说状态栏的加载以及布局
super_status_bar.xml
<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:sysui="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<com.android.systemui.statusbar.BackDropView
android:id="@+id/backdrop"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
sysui:ignoreRightInset="true">
<ImageView
android:id="@+id/backdrop_back"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop" />
<ImageView
android:id="@+id/backdrop_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:visibility="invisible" />
</com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_behind"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true" />
<FrameLayout
android:id="@+id/status_bar_container" // 这个fragment就是我们之前说的那个状态栏的fragment
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ViewStub
android:id="@+id/fullscreen_user_switcher_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/car_fullscreen_user_switcher" />
<include
layout="@layout/status_bar_expanded"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="invisible" />
<include layout="@layout/brightness_mirror" />
<com.android.systemui.statusbar.ScrimView
android:id="@+id/scrim_in_front"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
sysui:ignoreRightInset="true" />
</com.android.systemui.statusbar.phone.StatusBarWindowView>
// 真正状态栏的显示
status_bar.xml
<com.android.systemui.statusbar.phone.PhoneStatusBarView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:layout_width="match_parent"
android:layout_height="@dimen/status_bar_height"
android:id="@+id/status_bar"
android:background="@drawable/system_bar_background"
android:orientation="vertical"
android:focusable="false"
android:descendantFocusability="afterDescendants"
android:accessibilityPaneTitle="@string/status_bar"
>
<ImageView
android:id="@+id/notification_lights_out"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingBottom="2dip"
android:src="@drawable/ic_sysbar_lights_out_dot_small"
android:scaleType="center"
android:visibility="gone"
/>
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:orientation="horizontal"
>
<ViewStub
android:id="@+id/operator_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/operator_name" />
<FrameLayout
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1">
<include layout="@layout/heads_up_status_bar_layout" />
<!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the
individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and
DISABLE_NOTIFICATION_ICONS, respectively -->
<LinearLayout
android:id="@+id/status_bar_left_side"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:clipChildren="false"
>
// 时间显示
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textAppearance="@style/TextAppearance.StatusBar.Clock"
android:singleLine="true"
android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
android:gravity="center_vertical|start"
/>
// 通知显示区域
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
android:id="@+id/notification_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:clipChildren="false"/>
</LinearLayout>
</FrameLayout>
<!-- Space should cover the notch (if it exists) and let other views lay out around it -->
<android.widget.Space
android:id="@+id/cutout_space_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:gravity="center_horizontal|center_vertical"
/>
// 系统icon
<com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="horizontal"
android:gravity="center_vertical|end"
>
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
<ViewStub
android:id="@+id/emergency_cryptkeeper_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout="@layout/emergency_cryptkeeper_text"
/>
</com.android.systemui.statusbar.phone.PhoneStatusBarView>
system_icons.xml
这里主要就是显示一些系统的图标,包括闹钟,wifi。蓝牙等,系统中这些图片都是矢量图
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/system_icons"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
<com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
android:paddingEnd="@dimen/signal_cluster_battery_padding"
android:gravity="center_vertical"
android:orientation="horizontal"/>
// 电池
<com.android.systemui.BatteryMeterView android:id="@+id/battery"
android:layout_height="match_parent"
android:layout_width="wrap_content"
android:clipToPadding="false"
android:clipChildren="false" />
</LinearLayout>
app\src\main\java\com\android\systemui\statusbar\phone\PhoneStatusBarPolicy.java
系统icon 栏的初始化和更新都是在这个类里面去实现的
@VisibleForTesting
public PhoneStatusBarPolicy(Context context, StatusBarIconController iconController) {
mContext = context;
mIconController = iconController;
mCast = Dependency.get(CastController.class);
mHotspot = Dependency.get(HotspotController.class);
mBluetooth = Dependency.get(BluetoothController.class);
mNextAlarmController = Dependency.get(NextAlarmController.class);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mUserInfoController = Dependency.get(UserInfoController.class);
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mRotationLockController = Dependency.get(RotationLockController.class);
mDataSaver = Dependency.get(DataSaverController.class);
mZenController = Dependency.get(ZenModeController.class);
mProvisionedController = Dependency.get(DeviceProvisionedController.class);
mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
mLocationController = Dependency.get(LocationController.class);
mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
mSlotHotspot = context.getString(com.android.internal.R.string.status_bar_hotspot);
mSlotBluetooth = context.getString(com.android.internal.R.string.status_bar_bluetooth);
mSlotTty = context.getString(com.android.internal.R.string.status_bar_tty);
mSlotZen = context.getString(com.android.internal.R.string.status_bar_zen);
mSlotVolume = context.getString(com.android.internal.R.string.status_bar_volume);
mSlotAlarmClock = context.getString(com.android.internal.R.string.status_bar_alarm_clock);
mSlotManagedProfile = context.getString(
com.android.internal.R.string.status_bar_managed_profile);
mSlotRotate = context.getString(com.android.internal.R.string.status_bar_rotate);
mSlotHeadset = context.getString(com.android.internal.R.string.status_bar_headset);
mSlotDataSaver = context.getString(com.android.internal.R.string.status_bar_data_saver);
mSlotLocation = context.getString(com.android.internal.R.string.status_bar_location);
// systemicon 的初始化
// listen for broadcasts
IntentFilter filter = new IntentFilter();
/// M: [Multi-User] Will register this action using special receiver.
//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);
/// M: [Multi-User] Add user switched action for updating possible alarm icon.
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
// 广播的注册
/// M: Add icon for embms session.
filter.addAction(TelephonyIntents.ACTION_EMBMS_SESSION_STATUS_CHANGED);
filter.addAction(SMART_DEVICE_BROADCAST);
mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
/// M: [Multi-User] Register Alarm intent by user
registerAlarmClockChanged(UserHandle.USER_OWNER, false);
// Alarm clock
mIconController.setIcon(mSlotAlarmClock, R.drawable.stat_sys_alarm, null);
mIconController.setIconVisibility(mSlotAlarmClock, false);
接受到广播后去更新icon
private void updateAlarm() {
final AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT);
final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0;
int zen = mZenController.getZen();
final boolean zenNone = zen == Global.ZEN_MODE_NO_INTERRUPTIONS;
mIconController.setIcon(mSlotAlarmClock, zenNone ? R.drawable.stat_sys_alarm_dim
: R.drawable.stat_sys_alarm, buildAlarmContentDescription());
mIconController.setIconVisibility(mSlotAlarmClock, mCurrentUserSetup && hasAlarm);
}
// 广播接收器
@VisibleForTesting
/*private*/ BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
switch (action) {
case AudioManager.RINGER_MODE_CHANGED_ACTION:
case AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION:
updateVolumeZen();
break;
case TelephonyIntents.ACTION_SIM_STATE_CHANGED:
// Avoid rebroadcast because SysUI is direct boot aware.
if (intent.getBooleanExtra(TelephonyIntents.EXTRA_REBROADCAST_ON_UNLOCK,
false)) {
break;
}
updateSimState(intent);
break;
case TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED:
updateTTY(intent.getIntExtra(TelecomManager.EXTRA_CURRENT_TTY_MODE,
TelecomManager.TTY_MODE_OFF));
break;
case Intent.ACTION_MANAGED_PROFILE_AVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE:
case Intent.ACTION_MANAGED_PROFILE_REMOVED:
updateManagedProfile();
break;
case AudioManager.ACTION_HEADSET_PLUG:
updateHeadsetPlug(intent);
break;
case Intent.ACTION_USER_SWITCHED:
updateAlarm();
int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
registerAlarmClockChanged(newUserId, true);
break;
case TelephonyIntents.ACTION_EMBMS_SESSION_STATUS_CHANGED:
updateEmbmsState(intent);
break;
case SMART_DEVICE_BROADCAST:
updateSmartDevice(intent);
break;
}
}
};
所以如果,需要在systemicon 栏客制化加入一个其他的icon,则可以在这个类里面仿照做修改
从上面的xml 中可以看到电池电量的UI是在BatteryMeterView.java 类中
而真正去绘制电视的UI显示则是在settingslib的BatteryMeterDrawableBase.java 类中
settingslib\src\main\java\com\android\settingslib\graph\BatteryMeterDrawableBase
// 初始化画笔
public BatteryMeterDrawableBase(Context context, int frameColor) {
mContext = context;
final Resources res = context.getResources();
TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
final int N = levels.length();
mColors = new int[2 * N];
for (int i = 0; i < N; i++) {
mColors[2 * i] = levels.getInt(i, 0);
if (colors.getType(i) == TypedValue.TYPE_ATTRIBUTE) {
mColors[2 * i + 1] = Utils.getColorAttr(context, colors.getThemeAttributeId(i, 0));
} else {
mColors[2 * i + 1] = colors.getColor(i, 0);
}
}
levels.recycle();
colors.recycle();
mWarningString = context.getString(R.string.battery_meter_very_low_overlay_symbol);
mCriticalLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mButtonHeightFraction = context.getResources().getFraction(
R.fraction.battery_button_height_fraction, 1, 1);
mSubpixelSmoothingLeft = context.getResources().getFraction(
R.fraction.battery_subpixel_smoothing_left, 1, 1);
mSubpixelSmoothingRight = context.getResources().getFraction(
R.fraction.battery_subpixel_smoothing_right, 1, 1);
mFramePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mFramePaint.setColor(frameColor);
mFramePaint.setDither(true);
mFramePaint.setStrokeWidth(0);
mFramePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mBatteryPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBatteryPaint.setDither(true);
mBatteryPaint.setStrokeWidth(0);
mBatteryPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
Typeface font = Typeface.create("sans-serif-condensed", Typeface.BOLD);
mTextPaint.setTypeface(font);
mTextPaint.setTextAlign(Paint.Align.CENTER);
mWarningTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
font = Typeface.create("sans-serif", Typeface.BOLD);
mWarningTextPaint.setTypeface(font);
mWarningTextPaint.setTextAlign(Paint.Align.CENTER);
if (mColors.length > 1) {
mWarningTextPaint.setColor(mColors[1]);
}
mChargeColor = Utils.getDefaultColor(mContext, android.R.color.holo_red_dark);
mBoltPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBoltPaint.setColor(Utils.getDefaultColor(mContext, android.R.color.holo_green_dark));
mBoltPoints = loadPoints(res, R.array.batterymeter_bolt_points);
mPlusPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPlusPaint.setColor(Utils.getDefaultColor(mContext, R.color.batterymeter_plus_color));
mPlusPoints = loadPoints(res, R.array.batterymeter_plus_points);
mPowersavePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPowersavePaint.setColor(mPlusPaint.getColor());
mPowersavePaint.setStyle(Style.STROKE);
mPowersavePaint.setStrokeWidth(context.getResources()
.getDimensionPixelSize(R.dimen.battery_powersave_outline_thickness));
mIntrinsicWidth = context.getResources().getDimensionPixelSize(R.dimen.battery_width);
mIntrinsicHeight = context.getResources().getDimensionPixelSize(R.dimen.battery_height);
}
// 画电池
@Override
public void draw(Canvas c) {
final int level = mLevel;
final Rect bounds = getBounds();
if (level == -1) return;
float drawFrac = (float) level / 100f;
final int height = mHeight;
final int width = (int) (getAspectRatio() * mHeight);
final int px = (mWidth - width) / 2;
final int buttonHeight = Math.round(height * mButtonHeightFraction);
final int left = mPadding.left + bounds.left;
final int top = bounds.bottom - mPadding.bottom - height;
mFrame.set(left, top, width + left, height + top);
mFrame.offset(px, 0);
// button-frame: area above the battery body
mButtonFrame.set(
mFrame.left + Math.round(width * 0.28f),
mFrame.top,
mFrame.right - Math.round(width * 0.28f),
mFrame.top + buttonHeight);
// frame: battery body area
mFrame.top += buttonHeight;
// set the battery charging color
mBatteryPaint.setColor(batteryColorForLevel(level));
if (level >= FULL) {
drawFrac = 1f;
} else if (level <= mCriticalLevel) {
drawFrac = 0f;
}
final float levelTop = drawFrac == 1f ? mButtonFrame.top
: (mFrame.top + (mFrame.height() * (1f - drawFrac)));
// define the battery shape
mShapePath.reset();
mOutlinePath.reset();
final float radius = getRadiusRatio() * (mFrame.height() + buttonHeight);
mShapePath.setFillType(FillType.WINDING);
mShapePath.addRoundRect(mFrame, radius, radius, Direction.CW);
mShapePath.addRect(mButtonFrame, Direction.CW);
mOutlinePath.addRoundRect(mFrame, radius, radius, Direction.CW);
Path p = new Path();
p.addRect(mButtonFrame, Direction.CW);
mOutlinePath.op(p, Op.XOR);
if (mCharging) {
// define the bolt shape
// Shift right by 1px for maximal bolt-goodness
final float bl = mFrame.left + mFrame.width() / 4f + 1;
final float bt = mFrame.top + mFrame.height() / 6f;
final float br = mFrame.right - mFrame.width() / 4f + 1;
final float bb = mFrame.bottom - mFrame.height() / 10f;
if (mBoltFrame.left != bl || mBoltFrame.top != bt
|| mBoltFrame.right != br || mBoltFrame.bottom != bb) {
mBoltFrame.set(bl, bt, br, bb);
mBoltPath.reset();
mBoltPath.moveTo(
mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
for (int i = 2; i < mBoltPoints.length; i += 2) {
mBoltPath.lineTo(
mBoltFrame.left + mBoltPoints[i] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[i + 1] * mBoltFrame.height());
}
mBoltPath.lineTo(
mBoltFrame.left + mBoltPoints[0] * mBoltFrame.width(),
mBoltFrame.top + mBoltPoints[1] * mBoltFrame.height());
}
float boltPct = (mBoltFrame.bottom - levelTop) / (mBoltFrame.bottom - mBoltFrame.top);
boltPct = Math.min(Math.max(boltPct, 0), 1);
if (boltPct <= BOLT_LEVEL_THRESHOLD) {
// draw the bolt if opaque
c.drawPath(mBoltPath, mBoltPaint);
} else {
// otherwise cut the bolt out of the overall shape
mShapePath.op(mBoltPath, Path.Op.DIFFERENCE);
}
} else if (mPowerSaveEnabled) {
// define the plus shape
final float pw = mFrame.width() * 2 / 3;
final float pl = mFrame.left + (mFrame.width() - pw) / 2;
final float pt = mFrame.top + (mFrame.height() - pw) / 2;
final float pr = mFrame.right - (mFrame.width() - pw) / 2;
final float pb = mFrame.bottom - (mFrame.height() - pw) / 2;
if (mPlusFrame.left != pl || mPlusFrame.top != pt
|| mPlusFrame.right != pr || mPlusFrame.bottom != pb) {
mPlusFrame.set(pl, pt, pr, pb);
mPlusPath.reset();
mPlusPath.moveTo(
mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
for (int i = 2; i < mPlusPoints.length; i += 2) {
mPlusPath.lineTo(
mPlusFrame.left + mPlusPoints[i] * mPlusFrame.width(),
mPlusFrame.top + mPlusPoints[i + 1] * mPlusFrame.height());
}
mPlusPath.lineTo(
mPlusFrame.left + mPlusPoints[0] * mPlusFrame.width(),
mPlusFrame.top + mPlusPoints[1] * mPlusFrame.height());
}
// Always cut out of the whole shape, and sometimes filled colorError
mShapePath.op(mPlusPath, Path.Op.DIFFERENCE);
if (mPowerSaveAsColorError) {
c.drawPath(mPlusPath, mPlusPaint);
}
}
// compute percentage text
boolean pctOpaque = false;
float pctX = 0, pctY = 0;
String pctText = null;
if (!mCharging && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {
mTextPaint.setColor(getColorForLevel(level));
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
: (mLevel == 100 ? 0.38f : 0.5f)));
mTextHeight = -mTextPaint.getFontMetrics().ascent;
pctText = String.valueOf(SINGLE_DIGIT_PERCENT ? (level / 10) : level);
pctX = mWidth * 0.5f + left;
pctY = (mHeight + mTextHeight) * 0.47f + top;
pctOpaque = levelTop > pctY;
if (!pctOpaque) {
mTextPath.reset();
mTextPaint.getTextPath(pctText, 0, pctText.length(), pctX, pctY, mTextPath);
// cut the percentage text out of the overall shape
mShapePath.op(mTextPath, Path.Op.DIFFERENCE);
}
}
// draw the battery shape background
c.drawPath(mShapePath, mFramePaint);
// draw the battery shape, clipped to charging level
mFrame.top = levelTop;
c.save();
c.clipRect(mFrame);
c.drawPath(mShapePath, mBatteryPaint);
c.restore();
if (!mCharging && !mPowerSaveEnabled) {
if (level <= mCriticalLevel) {
// draw the warning text
final float x = mWidth * 0.5f + left;
final float y = (mHeight + mWarningTextHeight) * 0.48f + top;
c.drawText(mWarningString, x, y, mWarningTextPaint);
} else if (pctOpaque) {
// draw the percentage text
c.drawText(pctText, pctX, pctY, mTextPaint);
}
}
// Draw the powersave outline last
if (!mCharging && mPowerSaveEnabled && mPowerSaveAsColorError) {
c.drawPath(mOutlinePath, mPowersavePaint);
}
}