HarmonyOS 通知实践

背景

HarmonyOS 论坛中有研发人员求助,反馈通知没有没有声音,因此在真机上验证了一下,果不其然,没有通知的提示音,后来解决办法也非常简单,在手机设置应用中,将可以打开的通知提示统统改为铃声。

问题是解决了,但有必要分享一下HarmonyOS中的通知问题

实践总结

  1. slotType的设置,一定要放在 NotificationRequest 结构体中,使用NotificationManager.addSlot 这个函数是不起作用的
  2. NOTIFICATION_CONTENT_PICTURE 这个类型的通知,并不会展示图片
  3. NOTIFICATION_CONTENT_CONVERSATION 这个类型其实是不支持的
  4. 官方指导 图片类型 通知样例参数在 HarmonyOS SDK API 9上边使用错误

错误参数:pixelFormat:"ARGB_8888"

arduino 复制代码
let opts = { editable:true, pixelFormat:"ARGB_8888", size: {height:100, width : 150}};

更正:pixelFormat: image.PixelMapFormat.RGBA_8888, 即 枚举类型

arduino 复制代码
let opts = { editable:true, pixelFormat: image.PixelMapFormat.RGBA_8888, size: {height:100, width : 150}};

准备条件

  1. HarmonyOS 4.0
  2. HarmonyOS SDK API 9

实践功能介绍

备注: 参照官网指导

  1. NOTIFICATION_CONTENT_BASIC_TEXT(普通文本类型)
  2. NOTIFICATION_CONTENT_LONG_TEXT (长文本类型)
  3. NOTIFICATION_CONTENT_MULTILINE (多行文本类型)
  4. NOTIFICATION_CONTENT_PICTURE (图片类型)
  5. NOTIFICATION_CONTENT_CONVERSATION (会话类型)
  6. 进度条类型 (个人观点:这个不是一个类型,仅仅是基于基础通知类型从而实现的一个功能)

注意: NOTIFICATION_CONTENT_CONVERSATION 在SDK中出现了,官方指导中没有说明

样式说明

NOTIFICATION_CONTENT_BASIC_TEXT(普通文本类型)

NOTIFICATION_CONTENT_LONG_TEXT (长文本类型)

  • 默认状态
  • 展开状态

NOTIFICATION_CONTENT_MULTILINE (多行文本类型)

默认状态

展开状态

NOTIFICATION_CONTENT_PICTURE (图片类型)

官网指导的样子

进度条通知

  • 准备下载
  • 下载中
  • 下载完成
  • 下载失败

SlotType

这个属性功能真正使用时间,是今天才开始。

这个属性功能,根据官方指导,有两种设置方式

  1. 第一种
csharp 复制代码
let notificationRequest = {
   id: 1, //可以按照自己应用实际的情况,生成其它数字
   slotType: NotificationManager.SlotType.SOCIAL_COMMUNICATION //可以选择其它类型
}

NotificationManager.publish(notificationRequest)
  1. 第二种
javascript 复制代码
let notificationRequest = {
  ......
}

function addSlotCallBack(err) {
  if (err) {
    console.info("addSlot failed " + JSON.stringify(err));
  } else {
    console.info("addSlot success");
  }
}
NotificationManager.addSlot(NotificationManager.SlotType.SOCIAL_COMMUNICATION, addSlotCallBack);

NotificationManager.publish(notificationRequest)

其中第2种方式,实际是不生效的,虽然可以在控制台看到日志输出

SlotType 具体功能现象

根据第1种实现方式,如果没有slotType这个字段,默认值其实是其它类型,在代码层面没有表现出来,但是通过用户设置交互层面,可以看到表象

场景设定

"普通文本", "长文本", "多行类型" 统一指定slotType为NotificationManager.SlotType.SOCIAL_COMMUNICATION

"带图片类型" 不设置slotType

真机场景查看

设置 -> 通知和状态栏 -> 选择自己的应用(实践应用:HarmonyLearn)-> 找一下有没有类别项,如果没有,可以根据本实践主页面进一下相关操作

实践主页样式

验证过程

  1. 点击"带图片类型", 按照"真机场景查看" 的步骤操作,应该可以看到 "类别" 菜单了,但子菜单只有一个 "社交通讯"
  1. 点击"普通文本" / "长文本" / "多行类型", 按照"真机场景查看" 的步骤操作,应该可以看到 "类别" 菜单中出现了一个"其他"子菜单

铃声行为设定

通过"验证过程",已可以了解到,如果要设置通知铃声,可以进入"社交通讯" & "其它" 里边去详细设置

横幅样式

在阅读官方指导过程中,"横幅通知" 这个词应该见到过,它的具体表现是什么样子呢?

效果图

当点击了"长文本",在当前页面即可看到通知

如何设置

设置 -> 通知和状态栏 -> 选择自己的应用(实践应用:HarmonyLearn -> 提醒方式 -> "横幅通知"

源码解析

功能

  1. 基础类型样式验证
  2. 一键消除通知
  3. 下载通知进度条样式
  4. SlotType 功能
typescript 复制代码
import NotificationManager from '@ohos.notificationManager';
import image from '@ohos.multimedia.image';
import { CommonConstants } from '../common/CommonConstants';
import promptAction from '@ohos.promptAction'
import wantAgent from '@ohos.app.ability.wantAgent';
import request from '@ohos.request';
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';

@Entry
@Component
struct TestNotificationIndex {
  private context = getContext(this) as common.UIAbilityContext;

  @State currentProgress: number = 0
  @State totalProgress: number = 100
  @State downloadStatus: string = ''

  build(){
    Column({space: 20}){
       this.typeOne('普通文本', NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT)
       this.typeOne('长文本', NotificationManager.ContentType.NOTIFICATION_CONTENT_LONG_TEXT)
       this.typeOne('带图片类型', NotificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE)
       this.typeOne('会话类型', NotificationManager.ContentType.NOTIFICATION_CONTENT_CONVERSATION)
       this.typeOne('多行类型', NotificationManager.ContentType.NOTIFICATION_CONTENT_MULTILINE)
       Button('进度条类型').onClick(()=>{
          this.downloadStatus = '准备下载'
          this.downloadFile()
          this.notificationProgressStyle()
       })

       Button('取消所有通知').onClick(()=>{
         NotificationManager.cancelAll()
       })
    }
    .width('100%')
    .height('100%')
    .padding({top: AppStorage.Get(CommonConstants.StatusBarHeight)})
  }

  notificationProgressStyle(){
    let template = {
      name:'downloadTemplate',
      data: {
        title: '标题:',
        fileName: 'mqtt-v5.0.pdf',
        progressValue: this.currentProgress,
        progressMaxValue:  this.totalProgress,
      }
    }
    //构造NotificationRequest对象
    let notificationRquest = {
      id: 999,
      slotType: NotificationManager.SlotType.OTHER_TYPES,
      template: template,
      content: {
        contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: template.data.title + template.data.fileName,
          text: this.downloadStatus,
          additionalText: ((100*this.currentProgress)/this.totalProgress + '%')
        }
      }

    }
    NotificationManager.publish(notificationRquest).then(() => {
      console.info(`[ANS] publish success `);
    }).catch((err) => {
      console.error(`[ANS] failed to publish, error[${err}]`);
    });

  }

  downloadFile(){

    //1. 判定文件是否存在
    if(fs.accessSync(this.context.filesDir + '/mqtt-v5.0.pdf')){
      //2. 删除文件
      fs.unlinkSync(this.context.filesDir + '/mqtt-v5.0.pdf')
    }

    try {
      request.downloadFile(this.context, {
        url: 'https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.pdf',
        filePath: this.context.filesDir + '/mqtt-v5.0.pdf'
      }).then((downloadTask) => {

        downloadTask.getTaskMimeType((error, fileType)=>{
          console.log(fileType)
        })

        downloadTask.on('progress', (receive, totalSize) => {
          if(receive != undefined){
            this.currentProgress = receive
          } else {
            this.currentProgress = 0
          }
          this.totalProgress = totalSize
          console.log('当前进度' + receive + '-' + totalSize)

          if(receive != totalSize){
            this.downloadStatus = '下载中......'
          } else {
            this.downloadStatus = '下载完成'
          }
          this.notificationProgressStyle()
        })

        downloadTask.on('complete', () => {
          console.info('download complete');
        })

      }).catch((err) => {
        console.error(`Invoke downloadTask failed, code is ${err.code}, message is ${err.message}`);
        this.downloadStatus = '下载失败'
        this.notificationProgressStyle()
      });
    } catch (err) {
      console.error(`Invoke downloadFile failed, code is ${err.code}, message is ${err.message}`);
      this.downloadStatus = '下载失败'
      this.notificationProgressStyle()
    }
  }

  @Builder typeOne(btnTxt, type){
    Button(btnTxt).onClick(async ()=>{

      if(type == NotificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE){

        // 获取resourceManager资源管理
        const resourceMgr = this.context.resourceManager;
        const fileData = await resourceMgr.getRawFileContent('fugui.png');
        // 获取图片的ArrayBuffer
        const buffer = fileData.buffer;
        const imageSource = image.createImageSource(buffer);
        const pixelMap = await imageSource.createPixelMap();

        let notificationRequest = {
          id: 2,
          content: {
            contentType: NotificationManager.ContentType.NOTIFICATION_CONTENT_PICTURE,
            picture: {
              title: '图片-标题',
              text: '图片-内容',
              briefText: 'test_briefText图片',
              expandedTitle: 'test_expandedTitle图片',
              picture: pixelMap
            }
          },
        }
        // 发送通知
        NotificationManager.publish(notificationRequest, (err) => {
          if (err) {
            console.error(`[ANS] failed to publish, error[${err}]`);
            return;
          }
          console.info(`[ANS] publish success `);
        });

      } else {

        if(type == NotificationManager.ContentType.NOTIFICATION_CONTENT_CONVERSATION){
          try {
            promptAction.showToast({
              message: '暂时不支持',
              duration: 2000,
            });
          } catch (error) {
            console.error(`showToast args error code is ${error.code}, message is ${error.message}`);
          };
          return
        }

        //匿名函数,自动执行
        ( async () => {

          // 创建WantAgent

          let wantAgentObj = null; // 用于保存创建成功的wantAgent对象,后续使用其完成触发的动作。

          // 通过WantAgentInfo的operationType设置动作类型。
          let wantAgentInfo = {
            wants: [
              {
                deviceId: '',
                bundleName: 'com.harvey.testharmony',
                abilityName: 'audioability',
                action: '',
                entities: [],
                uri: '',
                parameters: {}
              }
            ],
            operationType: wantAgent.OperationType.START_ABILITY,
            requestCode: 0,
            wantAgentFlags:[wantAgent.WantAgentFlags.CONSTANT_FLAG]
          }

          await wantAgent.getWantAgent(wantAgentInfo)
            .then((data)=>{
              console.info('[WantAgent]getWantAgent success');
              wantAgentObj = data;
            }).catch((err)=>{
              console.error('[WantAgent]getWantAgent err=' + JSON.stringify(err));
            });


          let notificationRequest = {
            id: 1,
            slotType: NotificationManager.SlotType.SOCIAL_COMMUNICATION,
            wantAgent: wantAgentObj,
            content: {
              contentType: type, // 普通文本类型通知
              normal: {
                title: '标题栏(普通文本)',
                text: '1.测试普通文本;2.测试普通文本,然后再做一些修正;3.测试普通文本,然后再做一些修正;4.测试普通文本',
                additionalText: '附加信息(普通文本)',
              },
              longText: {
                title: '标题栏(长文本)',
                text: '1.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正;2.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正;3.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正;4.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正',
                additionalText: '附加信息(长文本)',
                longText: '测试1测试2测试3测试4测试5测试6测试7测试8测试9测试10测试11测12试测13试测14试测15试测16试测17试测18试测19试测20试测21试测22试测23试测24试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试',
                briefText: '简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1',
                expandedTitle: '展开之后的标题(长文本)',
              },
              multiLine:{
                title: '标题栏(多行)',
                text: '1.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正;2.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正;3.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正;4.测试普通文本,想看看普通文本到底长什么样子,然后再做一些修正',
                additionalText: '附加信息(多行)',
                briefText: '简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1简要1',
                longTitle: '测试1测试2测试3测试4测试5测试6测试7测试8测试9测试10测试11测12试测13试测14试测15试测16试测17试测18试测19试测20试测21试测22试测23试测24试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试测试',
                expandedTitle:  '展开之后的标题(多行)',
                lines: ['line_01line_01line_01line_01line_01line_01line_01line_01line_01line_01line_01line_01',
                  'line_02line_02line_02line_02line_02line_02line_02line_02line_02line_02line_02line_02line_02',
                  'line_03line_03line_03line_03line_03line_03line_03line_03line_03line_03line_03line_03line_03line_03',
                  'line_04line_04line_04line_04line_04line_04line_04line_04line_04line_04line_04line_04line_04line_04'],
              },
            }
          }

          //这种方式,SlotType 实际情况是不会生效,请在 notificationRequest 中添加slotType
          // function addSlotCallBack(err) {
          //   if (err) {
          //     console.info("addSlot failed " + JSON.stringify(err));
          //   } else {
          //     console.info("addSlot success");
          //   }
          // }
          // NotificationManager.addSlot(NotificationManager.SlotType.SOCIAL_COMMUNICATION, addSlotCallBack);

          NotificationManager.publish(notificationRequest, (err) => {
            if (err) {
              console.error(`[ANS] failed to publish, error[${err}]`);
              return;
            }
            console.info(`[ANS] publish success`);
          });

        } ) ()

      }

    })
  }

}
相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax