OpenHarmony 源码解析之SystemUi—Statusbar(TS)

249 篇文章 7 订阅
213 篇文章 3 订阅

作者:董伟

简介

SystemUI应用是OpenHarmony中预置的系统应用,为用户提供系统相关信息展示及交互界面,包括系统状态、系统提示、系统提醒等,例如系统时间、电量信息。

本文主要分析batterycomponent、clockcomponent、wificomponent三大组件:

  1. 导入batteryInfo模块,监听系统电池事件,实时获取电池电量状态
  2. 导入时间模块,调用JS内置函数,实时获取系统时间、日期、星期信息
  3. 导入Wifi模块,监听设备Wlan通信与连接事件,实时获取Wifi开关、供电、连接名等信息

架构图

目录

                    /applications/standard/systemui
                        ├── build.gradle                    # 全局编译配置文件
                        ├── settings.gradle                 # 编译模块配置文件
                        ├── LICENSE                         # 许可文件
                        ├── common                          # 通用工具类目录
                        ├── entry                           # entry模块目录
                        ├── signature                       # 证书文件目录
                        ├── features                        # 子组件目录
                        │   ├── batterycomponent            # 电池组件
                        │   ├── clockcomponent              # 时间组件
                        │   ├── control                     # 控制中心组件
                        │   ├── navigationservice           # 导航栏服务组件
                        │   ├── noticeitem                  # 通知子组件
                        │   ├── notificationservice         # 通知服务组件
                        │   ├── signalcomponent             # sim卡信号组件
                        │   ├── wificomponent               # wifi组件
                        ├── product                         # SystemUI总体功能目录
                            ├── navigationBar               # 导航栏模块目录
                            ├── statusbar                   # 状态栏模块目录
                            ├── systemDialog                # 系统弹框模块目录

batterycomponent

简介

电池组件只需监听‘usual.event.BATTERY_CHANGED’即电池变化事件即可,监听到电池变化,便获取一次电池的状态信息,实时获取电池的状态信息(是否在充电,电量百分比),以实现以下功能:

1.自动在充电时展示更醒目的图标,比如填充绿色、带闪电等

2.动态展示电量百分比变化

官方接口文档

https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-battery-info-0000001100730486

源码地址

https://gitee.com/nicklaus0602/applications_systemui/tree/master/features/batterycomponent

导入模块

import BatteryInfo from '@ohos.batteryInfo';			
import BatterySubscriber from '@ohos.commonEvent';		

申明变量与初始化

var mBatterySoc;
var mBatteryCharging;

let mProgress = Constants.DEFAULT_PROGRESS;
let mBatteryEventSubscriber = null;
let mBatteryEventSubscribeInfo = {
  events: ['usual.event.BATTERY_CHANGED']
}

export class BatteryStatus {
  /**
    * 自定义页面初始化函数,由此函数开始一层一层调用下一个函数,将当前类导出后,只需调用此初始化函数即可完成对Battery模块的使用
    * 这里使用AppStorage.SetAndLink('batterySoc', 0)来跨页面关联数据,只需要在另一个页面中导入当前自定义组件, 	
 	* 并使用@StorageLink('batterySoc')batterySoc: number = 1,即可关联变量mBatterySoc与batterySoc,然后在本页面修改mBatterySoc的值,
 	* 另一个页面的变量batterySoc值也会跟着变动。
 	* 
 	* 页面初始化 即注册订阅事件,监听mBatteryEventSubscribeInfo中的事件
 	*
 	*/
    initBatteryStatus() {
      Log.showInfo(TAG, 'initBatteryStatus');
      mBatterySoc = AppStorage.SetAndLink('batterySoc', 0);
      mBatteryCharging = AppStorage.SetAndLink('batteryCharging', false);
      if (mBatteryEventSubscriber == null) {
        this.registerBatteryListener();
      }
      this.getBatteryStatus();
    }

    uninitBatteryStatus() {
      Log.showInfo(TAG, 'uninitBatteryModel');
      this.unregisterBatteryListener();
    }

}

订阅与回调

/**
   * Subscribe Battery events   创建订阅
   */
private registerBatteryListener() {
  Log.showInfo(TAG, 'registerBatteryListener start');
  BatterySubscriber.createSubscriber(
    mBatteryEventSubscribeInfo,
    this.createBatterySubscriberCallBack.bind(this)
  );
}

/**
 * Unsubscribe Battery events  取消订阅
 *
 */
private unregisterBatteryListener() {
  Log.showInfo(TAG, 'unregisterBatteryListener');
  BatterySubscriber.unsubscribe(mBatteryEventSubscriber, () => {
    Log.showInfo(TAG, `unregister Battery Status Listener ===============`);
  });
}

/**
   * Callback of the subscriber 订阅回调,输出日志
   *
   * @param {Object} err - error returns from the caller
   * @param {Object} data - data returns from the caller
   */

private createBatterySubscriberCallBack(err, data) {
  Log.showInfo(TAG, `Subscriberregister createBatterySubscriberCallBack err: ${JSON.stringify(err)} data: ${JSON.stringify(data)}`);
  mBatteryEventSubscriber = data;
  BatterySubscriber.subscribe(mBatteryEventSubscriber, this.batterySubscriberCallBack.bind(this));
}

/**
   * Callback of the events  事件回调
   若err.code == 0,表示函数执行正常,此时判断事件是否为'usual.event.BATTERY_CHANGED',如果该事件发生,即调用电池模块内的函数来获取电池状态信息
   *
   * @param {Object} err - error returns from the caller
   * @param {Object} data - data returns from the caller
   */

private batterySubscriberCallBack(err, data) {
  Log.showInfo(TAG, `batterySubscriberCallBack err: ${JSON.stringify(err)} data: ${JSON.stringify(data)}`);
  if (err.code == 0) {
    if (data.event == 'usual.event.BATTERY_CHANGED') {
      this.getBatteryStatus();
    }
  } else {
    Log.showInfo(TAG, 'Subscriberregister error when subscribing ========');
  }
}

获取电池状态、电量

/**
   *  获取电池状态和剩余电量
   */
private getBatteryStatus() {
  Log.showInfo(TAG,'getBatteryStatus')

  // 调用BatteryInfo.batterySOC获取电量值
  let batterySoc = BatteryInfo.batterySOC;

  // 调用BatteryInfo.chargingStatus获取充电状态枚举值
  let batteryCharging = BatteryInfo.chargingStatus;

  if (null == batterySoc) {
    // Set the battery Soc as full when there is no battery hardware 设备无电池硬件时,电量默认为满电
    batterySoc = mProgress;
  }
  if (batterySoc <= 0) {
    // 确保电量值不为负数
    batterySoc = Math.abs(batterySoc) * Constants.PERCENT_NUMBER;
  }

  this.checkBatteryStatus(batteryCharging, (result) => {               
    let batteryStatus = result;
    // 检查电池的充电状态 将电量的值赋给mBatterySoc,
    mBatterySoc.set(batterySoc);
    // 电池状态赋给mBatteryCharging
    // 0是false,1、2、3是true
    mBatteryCharging.set(batteryStatus);
  });
}

/**
   * 传入参数charging,根据charging的值来赋boolean值给batteryStatus,charging为0时表示电池未充电
   表示电池充电状态的枚举:
   NONE 	0  	表示电池充电状态未知
   ENABLE 	1 	表示电池充电状态为使能状态
   DISABLE	2	表示电池充电状态为停止状态
   FULL	    3	表示电池充电状态为已充满状态
   *
   * @param {number} charging - the battery charging status
   * @param {object} callback - Function callback
   */
private checkBatteryStatus(charging, callback) {
  Log.showInfo(TAG, `checkBatteryStatus charging: ${charging}`);
  let batteryStatus;
  switch (charging) {
    case Constants.NONE:
      batteryStatus = false;
      break;
    case Constants.DISABLE:
    case Constants.ENABLE:
    case Constants.FULL:
      batteryStatus = true;
      break;
    default:
      batteryStatus = false;
      break;
  }
  callback(batteryStatus);
}

Wificomponent

注:官方暂无WifiInfo模块的接口文档,只有“import wifi from ‘@ohos.wifi’”模块的相关文档,二者部分属性的枚举值相同,可以稍做参考。

wifi 模块地址:https://developer.harmonyos.com/cn/docs/documentation/doc-references/js-apis-wlan-0000001121342036

简介

wifi模块需要监听的事件较多,需要监听以下事件:

1.连接事件’usual.event.wifi.CONN_STATE’,

2.供电事件’usual.event.wifi.POWER_STATE’(常用于路由器等设备)

3.连接名事件’WIFI_CONNECT_NAME’,

通过监听wifi的连接状态变化来判断wifi的开关状态,通过监听wifi的供电状态变化来判断wifi是否可用,通过监听wifi连接的名称变化来判断当前连接的是哪一个wifi网络。

通过对这三个事件的监听,我们可以实时获取wifi的开关状态,供电状态,连接名称,借此可以实现以下功能:

1.自动根据wifi开关状态设置不同图标

2.自动展示连接的wifi名称

源码地址

https://gitee.com/openharmony/applications_systemui/tree/master/features/wificomponent

导入模块

import WifiInfo from '@ohos.wifi_native_js';
import Subscriber from '@ohos.commonEvent';

申明变量与初始化

var mCommonEventSubscribeInfo = {
  events: [Constants.EVENT_CONN_STATE,
  Constants.EVENT_POWER_STATE, Constants.Event_CONN_NAME]
};

var mCommonEventSubscriber = null;
var mWifiInfo;
var mWifiStatus;
var mWifiOpenStatus;

 export class WifiModel {
 	/**
 	* 自定义页面初始化函数,由此函数开始一层一层调用下一个函数,将当前类导出后,只需调用此初始化函数即可完成对Wifi模块的使用
 	* 页面初始化,这里使用AppStorage.SetAndLink('wifiInfo', 0)来跨页面关联数据,只需要在另一个页面中导入当前自定义组件, 	
 	* 并使用@StorageLink('wifiInfo')wifiInfo: number = 1,即可关联变量mWifiInfo与wifiInfo,然后再本页面修改mWifiInfo的值,
 	* 另一个页面的变量wifiInfo值也会跟着变动。
 	* 
 	* 页面初始化 即注册订阅事件,监听wifi事件变化,并获取wifi当前状态信息
 	*
 	*/
    initWifiModel() {
        Log.showInfo(TAG, `initWifiModel`)
        mWifiInfo = AppStorage.SetAndLink("wifiInfo", 0);
        mWifiStatus = AppStorage.SetAndLink("wifiStatus", false);
        mWifiOpenStatus = AppStorage.SetAndLink("wifiOpenStatus", false);
        if(mCommonEventSubscriber == null){
          this.registerWiFiStatusListener();
        }
        this.getWifiMessage();
      }

    /**
     * 取消初始化时取消订阅
     *
     */
     uninitWifiModel() {
       Log.showInfo(TAG, `uninitWifiModel`)
       this.unregisterWiFiStatusListener()
     }
}

订阅与回调

通过创建对wifi事件的订阅,来监听mCommonEventSubscribeInfo中的三个事件,‘usual.event.wifi.CONN_STATE’,‘usual.event.wifi.POWER_STATE’,‘WIFI_CONNECT_NAME’,若事件发生,则触发回调,

/**
 * Subscribe wifi events  订阅wifi事件,无返回值,创建订阅,监听mCommonEventSubscribeInfo中的事件
 *
 */
registerWiFiStatusListener() : void {
  Log.showInfo(TAG, `register Wifi status listener ===========`);
  Subscriber.createSubscriber(
    mCommonEventSubscribeInfo,
    this.createWifiStatusSubscriberCallBack.bind(this)
  );
}

/**
 * Callback of the subscriber    订阅回调
 *
 * @param {Object} err - error returns from the caller
 * @param {Object} data - data returns from the caller
 */
createWifiStatusSubscriberCallBack(err, data) {
  Log.showInfo(TAG, `createWifiStatusSubscriberCallBack start err: ${JSON.stringify(err)} data: ${JSON.stringify(data)}`);
  mCommonEventSubscriber = data;
  Subscriber.subscribe(mCommonEventSubscriber, this.wifiStatusSubscriberCallBack.bind(this));
}

/**
 * Callback of the events     事件回调
 *
 * @param {Object} err - error returns from the caller
 * @param {Object} data - data returns from the caller
 */
private wifiStatusSubscriberCallBack(err, data) {
  Log.showInfo(TAG, `wifiStatusSubscriberCallBack start err:${ JSON.stringify(err)} data: ${ JSON.stringify(data) }`);
  // 默认枚举值 DEFAULT_ERR_CODE: number = 0,代表函数正常运行
  if (err.code == Constants.DEFAULT_ERR_CODE) {
    Log.showInfo(TAG, `wifi data == ${JSON.stringify(data)}`);
    // 枚举值 EVENT_CONN_STATE: string = 'usual.event.wifi.CONN_STATE',表示wifi连接状态变化
    if (data.event == Constants.EVENT_CONN_STATE) {
    /**
     * 枚举值 
     * WIFI_STATE_AP_CONNECTING: number = 1;     正在建立WLAN连接
     * WIFI_STATE_NETWORK_ENABLED: number = 3;	 网络可用
     * WIFI_STATE_NO_NETWORK: number = 4;		 无网络连接
     * 如果正在建立WLAN连接||网络可用||无网络连接,则调用changeWifiStatus()方法,设置mWifiStatus为true,表示当前wifi已激活,否则即设置为false,表	  * 示当前wifi未打开
     */
      if(data.code == Constants.WIFI_STATE_AP_CONNECTING ||
      data.code == Constants.WIFI_STATE_NETWORK_ENABLED ||
      data.code == Constants.WIFI_STATE_NO_NETWORK){
        this.updateWifiInfo();
        this.changeWifiStatus(true);
      }else {
        this.updateWifiInfo();
        this.changeWifiStatus(false);
        mWifiName.set('WLAN');
      }
    }
    // WIFI_POWER_OFF: number = 0;  WIFI_POWER_ON: number = 4;
    // data.code == 0,wifi不可用,mWifiOpenStatus.set(false),
    // data.code == 4,wifi可用,mWifiOpenStatus.set(true)

    // 枚举值 EVENT_POWER_STATE: string = 'usual.event.wifi.POWER_STATE',表示wifi供电状态变化(常用于路由器等设备)
    if (data.event == Constants.EVENT_POWER_STATE) {
      if( data.code == Constants.WIFI_POWER_OFF){
        this.updateWifiInfo();
        this.changeWifiStatus(false);
        mWifiOpenStatus.set(false);
        mWifiName.set('WLAN');
      }else if(data.code == Constants.WIFI_POWER_ON){
        mWifiOpenStatus.set(true);
      }
    }
    // 枚举值 Event_CONN_NAME: string = 'WIFI_CONNECT_NAME',表示连接名称变化,意味着连接到了wifi网络(或者切换到了其他的wifi网络),可以获取当前wifi的信号强度了,更新wifi名称,并设置wifiStatus为true,表示已连接
    if(data.event == 'WIFI_CONNECT_NAME'){
      this.updateWifiName(JSON.parse(data.data).name)
      this.changeWifiStatus(true)

      // rssi是热点的信号强度(dBm),类型为number,取值范围为[0, 4],rssi为4时wifi信号满格
      // band是WLAN接入点的频段,类型为number
      // 将rssi,band传入函数updateWifiInfo()
      let rssi = JSON.parse(data.data).rssi
      let band = JSON.parse(data.data).band
      Log.showInfo(TAG, `WIFI_CONNECT_NAME ================ ${rssi}`)
      this.updateWifiInfo(rssi,band);
    }
  } else {
    Log.showError(TAG, `error when subscribing ========`);
  }
}

/**
 * Unsubscribe wifi events    取消wifi事件订阅(退订)
 *
 */
private unregisterWiFiStatusListener() {
  Subscriber.unsubscribe(mCommonEventSubscriber, () => {
    Log.showInfo(TAG, `unregister Wifi Status Listener ===============`);
  });
}

获取Wifi状态

/**
 * Check the connection status of Wifi  检查wifi的连接状态
 *
 * @param {boolean} status - if display wifi icon
 */
private changeWifiStatus(status) {
  Log.showInfo(TAG, `enter changeWifiStatus ================ ${status}`);
  mWifiStatus.set(status)
}

// 更新wifi信息,传入参数rssi,band
private updateWifiInfo(rssi=Constants.UPDATE_WIFI_INFO_DEFAULT_PARAM,band=Constants.SIGN_LEVEL) {
  Log.showInfo(TAG, `enter changeWifiInfos ================ ${rssi}+${band}`);
  mWifiInfo.set(this.getWifiInfo(rssi,band))
  Log.showInfo(TAG, `mWifiInfo wifiInfo ${mWifiInfo.get()}`);
}
// 更新wifi名称
private updateWifiName(name) {
  Log.showInfo(TAG, `enter changeWifiNames ================ ${name}`);
  mWifiName.set(name);
}

/**
 * Update the image of wifi status   更新wifi状态的图标(根据level,即信号的强度,取值范围为[0, 4]),信号4代表信号满格
 *
 * @param {number} rssi - the rssi number of the connected wifi
 * @return {string} image used to show wifi status
 */
private getWifiInfo(rssi,band) {
  //The current version keeps return 0 as result, set the level as 4(full level) by hand.
  Log.showInfo(TAG, `getWifiImage enter =========`);
  //Fake number of band and rssi for wifi signal level temporarily
  let level = WifiInfo.getSignalLevel(rssi, band);
  Log.showInfo(TAG, `wifi level = ${level}`);
  return level;
}
// 启用wifi(暂不可用)
enableWifi() {
  Log.showInfo(TAG, 'enableWifi ing');
  WifiInfo.enableWifi();
}
// 关闭wifi(暂不可用)
disableWifi() {
  Log.showInfo(TAG, 'disableWifi ing');
  WifiInfo.disableWifi();
}

Clockcomponent

简介

对于系统时间的获取较为简单,只需在页面初始化时调用JS的内置函数来获取系统时间即可,需要注意的是因为要保持所展示的时间实时更新,需要使用以下函数来设置时间间隔,确保每隔一定的时间就调用一次函数,保证时间的实时更新。

setInterval(() => {
    函数名;
  }, 时间间隔);

本文中通过每隔3秒获取一次时间来展示系统时间,满足设备展示“xx:xx”即“小时-分钟“的需要,如果需要精度更高,就需要调整LOOP_TIME,使得调用函数的间隔更短。

源码地址

https://gitee.com/openharmony/applications_systemui/tree/master/features/clockcomponent

获取时间数据

const LOOP_TIME = 3000; 
var mTimeLink;
var mDayLink;
var mWeekDayLink;
var mMonthLink;
/**
  * 获取当前日期和时间 调用getDate()函数,实例化Date对象,获取系统当前时间,调用JS内置函数,每隔三秒将date对象传入
  *	this.updateTime(date);
  *	this.updateDay(date);
  *	this.updateWeekDay(date);
  *	this.updateMonth(date);
  *	函数,获取时间、日、周、月数据
  *
  */
private getCurrentDate() {
  Log.showInfo(TAG, 'getCurrentDate');
  this.getDate();
  timeInterval = setInterval(() => {
    this.getDate();
  }, LOOP_TIME);
}

// 实例化Date对象,获取系统当前时间,并作为参数传入JS内置函数获取想要的时分秒、日、周、月等值
private getDate(){
  Log.showInfo(TAG, 'getDate');
  let date = new Date();
  this.updateTime(date);
  this.updateDay(date);
  this.updateWeekDay(date);
  this.updateMonth(date);
}

/**
 * Update Time.
 * date.toTimeString()返回一个表示该Date对象时分秒部分的时间字符串
   substring(0, 5) 截取下标第0个到第5个字符,即 xx:xx:xx
 * @param {Object} date - Object of Date.
 */
private updateTime(date) {
  let time = date.toTimeString().substring(0, 5);
  mTimeLink.set(time);
}

/**
 * Update Day.
 * date.getDate() 获取每个月的第几号-number
 * @param {Object} date - Object of Date.
 */
private updateDay(date) {
  let day = date.getDate();
  mDayLink.set(day);
}

/**
 * Update WeekDay.
 * date.getDay() 返回每周中的第几天-number,想获得'星期一'这样的value,还需要定义数组来获取,例如:
 var weekdaylist=new Array(7)
	weekdaylist[0]="周日"
	weekdaylist[1]="周一"
	weekdaylist[2]="周二"
	weekdaylist[3]="周三"
	weekdaylist[4]="周四"
	weekdaylist[5]="周五"
	weekdaylist[6]="周六"
	weekdaylist[this.mWeekDay]
 * @param {Object} date - Object of Date.
 */
private updateWeekDay(date) {
  let weekDay = date.getDay();
  mWeekDayLink.set(weekDay);
}

/**
 * Update Month.
 * date.getMonth() 返回月份-number,月份取值范围【0,11】,这里需要加1
 * @param {Object} date - Object of Date.
 */
private updateMonth(date) {
  let month = (date.getMonth() + 1);
  mMonthLink.set(month);
}

结语

本文对源码的方法进行了细致的解读,目标是节省广大开发者分析源码的时间,希望大家阅读本文后,可以将源码中从订阅监听事件到调用方法获得枚举值的整个模块直接拿来使用,以此助力鸿蒙OS生态搭建!

为了能让大家更好的学习鸿蒙(HarmonyOS NEXT)开发技术,这边特意整理了《鸿蒙开发学习手册》(共计890页),希望对大家有所帮助:https://qr21.cn/FV7h05

《鸿蒙开发学习手册》:

如何快速入门:https://qr21.cn/FV7h05

  1. 基本概念
  2. 构建第一个ArkTS应用
  3. ……

开发基础知识:https://qr21.cn/FV7h05

  1. 应用基础知识
  2. 配置文件
  3. 应用数据管理
  4. 应用安全管理
  5. 应用隐私保护
  6. 三方应用调用管控机制
  7. 资源分类与访问
  8. 学习ArkTS语言
  9. ……

基于ArkTS 开发:https://qr21.cn/FV7h05

  1. Ability开发
  2. UI开发
  3. 公共事件与通知
  4. 窗口管理
  5. 媒体
  6. 安全
  7. 网络与链接
  8. 电话服务
  9. 数据管理
  10. 后台任务(Background Task)管理
  11. 设备管理
  12. 设备使用信息统计
  13. DFX
  14. 国际化开发
  15. 折叠屏系列
  16. ……

鸿蒙开发面试真题(含参考答案):https://qr18.cn/F781PH

鸿蒙开发面试大盘集篇(共计319页):https://qr18.cn/F781PH

1.项目开发必备面试题
2.性能优化方向
3.架构方向
4.鸿蒙开发系统底层方向
5.鸿蒙音视频开发方向
6.鸿蒙车载开发方向
7.鸿蒙南向开发方向

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值