背景介绍

前段时间上架的“智能带办”鸿蒙应用,应用亮点是根据用户输入要做的事情自动自动生成需要带的东西。在使用过程中发现有些东西不能立马拿到的需要在未来某个时刻拿的需要提醒。重新设计产品时发现,自己实现提醒功能还是挺复杂的,如果用云端方案接入通知方式不仅复杂而且可控性太差。这个时候想到接入日程,通过系统能力来实现功能。

接入后向左滑动待办物品,出现日历按钮,效果如图:
在这里插入图片描述

点击按钮弹出日程计划日期和时间:
在这里插入图片描述

选择完日期和事件自动自动完成日程创建。下面先介绍鸿蒙日历能力。

鸿蒙日历📅 能力介绍
简介

HarmonyOS 的 Calendar Kit(日历服务)是一套系统级的日程管理接口,旨在将出行、餐饮、运动等各类与时间相关的服务与系统日历无缝集成,实现统一的时间视图与提醒能力。核心能力如下:

  • 账户管理:支持创建、查询和删除日历账户。应用可创建专属账户(返回唯一 accountId),删除账户将同时清除其下所有日程。
  • 日程管理 (CRUD):在指定账户下,支持日程的全生命周期管理,包括创建(返回唯一 eventId)、删除、更新和查询。创建时可设置标题、时间、地点、提醒及重复规则等属性。
  • 一键服务:通过永久性授权,可将带 DeepLink 的“一键服务”写入日历。当日程临近或到期,系统会在日历、通知、卡片等位置展示服务按钮,用户点击即可直达服务页面,实现从“看到日程”到“完成服务”的闭环。
    我们的应用主要用到账户和日程管理,使用日历需要如下权限:
申请权限 支持的日历账户操作范围 支持的日程操作范围
ohos.permission.READ_CALENDAR 读取系统默认及当前应用创建的日历账户。 读取上述账户下当前应用创建的日程。
ohos.permission.WRITE_CALENDAR 增、删、改当前应用创建的日历账户。 增、删、改上述账户下当前应用创建的日程。
ohos.permission.READ_WHOLE_CALENDAR 读取设备上所有日历账户。 读取所有应用创建的日程。
ohos.permission.WRITE_WHOLE_CALENDAR 增、删、改设备上所有日历账户。 增、删、改所有应用创建的日程。
我们只需要读取我们自己应用创建的日历账号下日程即可,所以申请前两个权限即可。
账户管理

日历账户‌用于存储和管理个人或团队的日程,通过日历账户,用户可以方便地查看、编辑和共享日程信息。日历管理器CalendarManager用于管理日历账户Calendar。日历账户主要包含账户信息CalendarAccount和配置信息CalendarConfig。

我们可以创建属于应用特有的日历账户,还可以对日历账户进行新增、删除、更新和查询。此外,每个日程Event归属于某一个特定的日历账户,可以通过日历账户对账户下面的日程进行管理。

@ohos.calendarManager提供了日历账户管理的相关接口,常用到的接口如下表:

接口名称 描述
getCalendarManager(context: Context): CalendarManager 根据上下文获取日历管理器对象CalendarManager,用于管理日历。
createCalendar(calendarAccount: CalendarAccount): Promise 根据日历账户信息,创建一个Calendar对象,使用Promise异步回调。
getCalendar(calendarAccount?: CalendarAccount): Promise 获取默认Calendar对象或者指定Calendar对象,使用Promise异步回调。

默认Calendar是日历存储首次运行时创建的,若创建Event时不关注其Calendar归属,则无须通过createCalendar()创建Calendar,直接使用默认Calendar。
getAllCalendars(): Promise<Calendar[]> 获取当前应用所有创建的Calendar对象以及默认Calendar对象,使用Promise异步回调。
deleteCalendar(calendar: Calendar): Promise 删除指定Calendar对象,使用Promise异步回调。
getConfig(): CalendarConfig 获取日历配置信息。
setConfig(config: CalendarConfig): Promise 设置日历配置信息,使用Promise异步回调。
getAccount(): CalendarAccount 获取日历账户信息。
日程管理

日程指特定的事件或者活动安排,日程管理即对这些事件、活动进行规划和控制,能更有效地利用相关资源、提高生产力和效率,使人们更好地管理时间和任务。Calendar Kit中的日程Event归属于某个对应的日历账户Calendar,一个日历账户下可以有多个日程,一个日程只属于一个Calendar。取到日历账户对象之后,即可对该账户下的日程进行管理,包括日程的创建、删除、修改、查询等操作。在创建、修改日程时,支持对日程的标题、开始时间、结束时间、日程类型、日程地点、日程提醒时间、日程重复规则等相关信息进行设置,以便进行更丰富更有效的日程管理。

@ohos.calendarManager提供了日程管理的相关接口,常用到的接口如下表:

接口名称 描述
getCalendarManager(context: Context): CalendarManager 根据上下文获取CalendarManager对象,用于管理日历。
createCalendar(calendarAccount: CalendarAccount): Promise 根据日历账户信息,创建一个Calendar对象,使用Promise异步回调。
addEvent(event: Event): Promise 创建日程,入参Event不填日程id,使用Promise异步回调。
editEvent(event: Event): Promise 通过跳转到日程创建界面创建单个日程,入参Event不填日程id,使用Promise异步回调。
deleteEvent(id: number): Promise 删除指定日程id的日程,使用Promise异步回调。
updateEvent(event: Event): Promise 更新日程,使用Promise异步回调。
getEvents(eventFilter?: EventFilter, eventKey?: (keyof Event)[]): Promise<Event[]> 获取Calendar下符合查询条件的Event,使用Promise异步回调。
智能带办接入过程
1、导入依赖

接入账号管理,首先需要导入相关依赖:

import { abilityAccessCtrl, AbilityConstant, common, PermissionRequestResult, Permissions, UIAbility, Want } from '@kit.AbilityKit';
import { calendarManager } from '@kit.CalendarKit';
2、创建日历管理类
  
const TAG = 'CalendarService';  
  
/**  
 * Service to manage Calendar Kit operations. */
   export class CalendarService {  
  private static calendar: calendarManager.Calendar | undefined = undefined;  
  //账号信息
  private static readonly calendarAccount: calendarManager.CalendarAccount = {  
    name: 'IntelligentTodo',  
    type: calendarManager.CalendarType.LOCAL,  
    displayName: '智能带办'  
  };  
  
  /**  
   * 添加日程到日历
   * @param title The title of the todo.  
   * @param description The description of the todo.  
   * @param startTime The start time (milliseconds).   * @param endTime The end time (milliseconds).   * @returns The event ID.    */  
     public static async addEvent(title: string, description: string, startTime: number, endTime: number): Promise<number> {  
    const mgr = AppStorage.get<calendarManager.CalendarManager>('calendarMgr');  
    if (!mgr) {  
      Logger.e(TAG, 'calendarMgr is not initialized in AppStorage');  
      throw new Error('日历服务未准备就绪');  
    }  
  
    // Request permissions first  
    const context = AppStorage.get<common.UIAbilityContext>('abilityContext');  
    if (context) {  
      const granted = await CalendarService.checkAndRequestPermissions(context);  
      if (!granted) {  
        throw new Error('未获得日历权限');  
      }  
    }  
    try {  
      if (!CalendarService.calendar) {  
        CalendarService.calendar = await CalendarService.getOrCreateCalendar(mgr);  
      }  
  
      const event: calendarManager.Event = {  
        title: title,  
        description: description,  
        type: calendarManager.EventType.NORMAL,  
        startTime: startTime,  
        endTime: endTime,  
        reminderTime: [10] // Default 10 minutes reminder  
      };  
  
      if (!CalendarService.calendar) {  
        throw new Error('日历对象初始化失败');  
      }  
      const eventId = await CalendarService.calendar.addEvent(event);  
      Logger.i(TAG, `Succeeded in adding event, id -> ${eventId}`);  
      return eventId;  
    } catch (error) {  
      Logger.e(TAG, `Failed to add event: ${JSON.stringify(error)}`);  
      throw new Error(JSON.stringify(error));  
    }  
  }  
  /**  
   * 创建日历对象
   */  
     private static async getOrCreateCalendar(mgr: calendarManager.CalendarManager): Promise<calendarManager.Calendar> {  
    try {  
      // Try to find if our account already exists  
      const calendars = await mgr.getAllCalendars();  
      const existing = calendars.find(c => {  
        const acc = c.getAccount();  
        return acc.name === CalendarService.calendarAccount.name;  
      });  
      if (existing) {  
        return existing;  
      }  
  
      // Create new account if not exists  
      const newCalendar = await mgr.createCalendar(CalendarService.calendarAccount);  
      const config: calendarManager.CalendarConfig = {  
        enableReminder: true,  
        color: '#aabbcc'  
      };  
      await newCalendar.setConfig(config);  
      return newCalendar;  
    } catch (err) {  
      Logger.e(TAG, `getOrCreateCalendar error: ${JSON.stringify(err)}`);  
      // Fallback to default calendar if creation fails  
      return await mgr.getCalendar();  
    }  
  }  
  /**  
   * 动态获取权限
  */  
     private static async checkAndRequestPermissions(context: common.UIAbilityContext): Promise<boolean> {  
    const permissions: Array<Permissions> = ['ohos.permission.READ_CALENDAR', 'ohos.permission.WRITE_CALENDAR'];  
    const atManager = abilityAccessCtrl.createAtManager();  
    try {  
      const result = await atManager.requestPermissionsFromUser(context, permissions);  
      const grantStatus = result.authResults;  
      return grantStatus.every(s => s === 0);  
    } catch (err) {  
      Logger.e(TAG, `requestPermissionsFromUser error: ${JSON.stringify(err)}`);  
      return false;  
    }  
  }}

getOrCreateCalendar根据上下文获取日程管理器对象calendarMgr,用于对日历账户进行相关管理操作。官方推荐在EntryAbility.ets文件中进行操作,我们这里进行独立封装,对权限做到精准控制,在使用时再申请。接着根据日历账户信息,创建一个日历账户Calendar对象。创建日历账户之前,我们需要先根据账户信息进行查询,如果账户不存在则抛出异常信息,捕获到异常再进行日历账户的创建,否则可能会出现账户重复创建的问题。
日历账户创建之后,日历账户颜色默认为黑色,不指定日历账户颜色可能导致部分版本/设备深色模式下显示效果不佳。开发者需要调用setConfig()接口设置日历配置信息,包括是否打开日历账户下的日程提醒能力、设置日历账户颜色。

const calendarAccounts: calendarManager.CalendarAccount = {
  name: 'MyCalendar',
  type: calendarManager.CalendarType.LOCAL,
  displayName: 'MyCalendar'
};
// 日历配置信息
calendarMgr?.getCalendar(calendarAccounts, (err, data) => {
  //获取日历账户
  if (err) {
    hilog.error(DOMAIN, 'testTag', `Failed to get calendar, Code is ${err.code}, message is ${err.message}`);
  } else {
    const config: calendarManager.CalendarConfig = {
      // 打开日程提醒
      enableReminder: true,
      // 设置日历账户颜色
      color: '#aabbcc'
    };
    // 设置日历配置信息
    data.setConfig(config).then(() => {
      hilog.info(DOMAIN, 'testTag', '%{public}s', `Succeeded in setting config, data->${JSON.stringify(config)}`);
    }).catch((err: BusinessError) => {
      hilog.error(DOMAIN, 'testTag', `Failed to set config. Code: ${err.code}, message: ${err.message}`);
    })
  }
});

addEvent方法封装了在当前日历账户下添加日历日程,注意入参中不需要填写日程id。创建日程时,支持设置日程的标题、开始时间、结束时间、日程类型、日程地点、日程提醒时间、日程重复规则等相关信息。程创建成功后会返回一个日程id,作为日程的唯一标识,后续可按照日程id进行指定日程的更新或删除。
目前支持以下两种方式来创建日程。
方式一:可以在日历账户下通过addEvent()或addEvents()接口创建日程。其中可使用addEvent()接口创建单个日程,也可以使用addEvents()接口批量创建日程,此处以创建单个日程为例。
方式二:在获取到日历管理器对象后,可通过editEvent()接口创建单个日程。调用此接口创建日程时,会跳转到日程创建页面,在日程创建页面进行相关操作完成日程的创建, editEvent()不支持自定义周期性日程创建。

我们采用方式一,用户点击日程时弹窗日期时间选择器,用户选择时间后调用addEvent方法创建日程。

总结

本次功能迭代的核心思路是“借力系统能力,优化产品体验”。面对自建提醒功能的复杂性,我们选择接入鸿蒙Calendar Kit,将待办事项转化为系统日程。通过左滑待办项快速创建日程,并利用系统日历实现稳定、统一的提醒。这一方案通过封装CalendarService精准管理权限与账户,既大幅降低了开发复杂度和维护成本,又为用户提供了原生、可靠的提醒服务,是“站在系统肩膀上”高效解决通用需求的典型实践。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐