更新记录

1.4.0(2025-09-26)

添加异步调用,同时将方法挂载在 uni 上,通过 uni.* 进行使用;从而达到良好的提示

1.3.1(2025-09-26)

更新 readme.md ,优化插件使用方法

1.3.0(2025-09-26)

日历事项查询、删除、新增功能

查看更多

平台兼容性

uni-app(4.76)

Vue2 Vue3 Chrome Safari app-vue app-nvue Android iOS 鸿蒙
- - - - - - 5.0 - -
微信小程序 支付宝小程序 抖音小程序 百度小程序 快手小程序 京东小程序 鸿蒙元服务 QQ小程序 飞书小程序 快应用-华为 快应用-联盟
- - - - - - - - - - -

uni-app x(4.76)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

多语言 暗黑模式 宽屏模式

uni-calendar

一个基于 UTS 开发的 uni-app 日历插件,支持在 Android 平台获取系统日历信息。

功能特性

  • 📅 获取系统日历账户信息
  • 🔐 支持访问级别查询
  • 🎨 支持日历颜色和显示名称
  • ⚙️ 支持同步和可见性设置
  • 📝 新增日历事项(支持提醒、位置、访问级别等)
  • 🔍 查询日历事项(支持时间范围、关键词搜索)
  • 🗑️ 删除日历事项(支持按时间范围批量删除)
  • 📱 专为 Android 平台优化

安装

  1. 在 uni-app 项目中,通过 uni_modules 安装
  2. 确保 HBuilderX 版本 >= 4.7.0

挂载在 uni 上

// #ifdef APP-PLUS
import {
  getCalendarInfo,
  setCalendarInfo,
  deleteCalendarInfo,
  queryCalendarEvents,
  createCalendarEvent,
  deleteCalendarEvent,
  getCalendarInfoAsync,
  setCalendarInfoAsync,
  deleteCalendarInfoAsync,
  queryCalendarEventsAsync,
  createCalendarEventAsync,
  deleteCalendarEventAsync,
} from "@/uni_modules/xl-calendar-info";

const u: any = uni;
u.getCalendarInfo ??= getCalendarInfo as any;
u.setCalendarInfo ??= setCalendarInfo as any;
u.deleteCalendarInfo ??= deleteCalendarInfo as any;
u.queryCalendarEvents ??= queryCalendarEvents as any;
u.createCalendarEvent ??= createCalendarEvent as any;
u.deleteCalendarEvent ??= deleteCalendarEvent as any;
u.getCalendarInfoAsync ??= getCalendarInfoAsync as any;
u.setCalendarInfoAsync ??= setCalendarInfoAsync as any;
u.deleteCalendarInfoAsync ??= deleteCalendarInfoAsync as any;
u.queryCalendarEventsAsync ??= queryCalendarEventsAsync as any;
u.createCalendarEventAsync ??= createCalendarEventAsync as any;
u.deleteCalendarEventAsync ??= deleteCalendarEventAsync as any;
// #endif

// #ifndef APP-PLUS
const notSupport = (name: string) => (options?: any) => {
  const err = { errMsg: `${name}:fail not support on current platform` };
  options?.fail?.(err);
  options?.complete?.(err);
};
const u2: any = uni;
u2.getCalendarInfo ??= notSupport("getCalendarInfo");
u2.setCalendarInfo ??= notSupport("setCalendarInfo");
u2.deleteCalendarInfo ??= notSupport("deleteCalendarInfo");
u2.queryCalendarEvents ??= notSupport("queryCalendarEvents");
u2.createCalendarEvent ??= notSupport("createCalendarEvent");
u2.deleteCalendarEvent ??= notSupport("deleteCalendarEvent");
u2.getCalendarInfoAsync ??= notSupport("getCalendarInfoAsync");
u2.setCalendarInfoAsync ??= notSupport("setCalendarInfoAsync");
u2.deleteCalendarInfoAsync ??= notSupport("deleteCalendarInfoAsync");
u2.queryCalendarEventsAsync ??= notSupport("queryCalendarEventsAsync");
u2.createCalendarEventAsync ??= notSupport("createCalendarEventAsync");
u2.deleteCalendarEventAsync ??= notSupport("deleteCalendarEventAsync");
// #endif
  • 然后在 main.ts 引入使用即可
import "./plugins/calendar-bridge";

使用方法

获取日历账户信息

uni.getCalendarInfo({
  success: (res) => {
    console.log("获取日历信息成功:", res);
    // res.Calendars 包含所有日历账户信息
    res.Calendars.forEach((calendar) => {
      console.log("日历ID:", calendar.calendarId);
      console.log("账户名:", calendar.accountName);
      console.log("显示名称:", calendar.displayName);
      console.log("是否可见:", calendar.visible);
      console.log("访问级别:", calendar.accessLevel);
    });
  },
  fail: (err) => {
    console.error("获取日历信息失败:", err);
  },
  complete: (res) => {
    console.log("操作完成:", res);
  },
});

设置日历账户

uni.setCalendarInfo(
  {
    ACCOUNT_NAME: "com.maimai.international",
    ACCOUNT_TYPE: "com.maimai",
    OWNER_ACCOUNT: "com.maimai.international",
    NAME: "MaiMai International",
    CALENDAR_DISPLAY_NAME: "买买国际",
    CALENDAR_TIME_ZONE: "Asia/Shanghai",
    VISIBLE: 1,
    SYNC_EVENTS: 1,
    CALENDAR_ACCESS_LEVEL: 700,
  },
  {
    success: (res) => {
      console.log("设置成功:", res.Calendars);
    },
    fail: (err) => {
      console.error("设置失败:", err);
    },
    complete: (res) => {
      console.log("操作完成:", res);
    },
  }
);

删除日历账户(deleteCalendarInfo)

uni.deleteCalendarInfo(
  { calendarId: 12 },
  {
    success: (res) => {
      console.log("删除成功", res.deletedCount);
    },
    fail: (err) => {
      console.error("删除失败", err);
    },
    complete: (res) => {
      console.log("操作完成", res);
    },
  }
);

// 按账户名批量删除(遍历该 ACCOUNT_NAME 下的所有 ACCOUNT_TYPE)
uni.deleteCalendarInfo(
  { ACCOUNT_NAME: "your_account_name" },
  {
    success: (res) => {
      console.log("按账户名删除完成", res.deletedCount);
    },
    fail: (err) => {
      console.error("删除失败", err);
    },
  }
);

配置项 DeleteConfigOptions

  • calendarId?: number:指定要删除的日历 ID。
  • ACCOUNT_NAME?: string:按账户名删除该账户名下的所有类型(会遍历匹配到的 ACCOUNT_TYPE 并逐个删除)。

返回数据结构

interface DeleteCalendarResult {
  errMsg: string; // 'deleteCalendarInfo:ok' | 错误信息
  deletedCount: number; // 实际删除的条目数
}

错误码(删除)

  • 1001:getAppContext 为 null。
  • 1002:参数不合法或删除失败。

重要注意事项

  • 类型要求:calendarId 必须是数字类型(例如 12),不要传字符串(例如 '12')。
  • 可删除范围:Android 的日历账户有所有权/适配器限制。通常只有“你通过本插件 setCalendarInfo 创建的日历”(或你 App 自己的 SyncAdapter 管理的账户)才允许删除。第三方/系统日历(如厂商/Google/其他 App 创建的,如 BOSS直聘 账户)通常不可由你的 App 删除,resolver.delete(...) 会返回 0
  • accessLevel=700 表示该日历对其“所属账户”的权限级别,不是你 App 的系统权限,不代表你的 App 可以删除它。
  • 运行时权限:执行删除前需授予 WRITE_CALENDAR 权限;仅读取需 READ_CALENDAR
  • 设备差异:不同 ROM/设备对第三方删除系统/他人账户的策略可能更严格,出现 deletedCount=0 属正常受限行为。

常见问题与排查

  • deletedCount = 0
    • 检查该日历是否由你 App 创建;第三方/系统账户通常不可删。
    • 确认已授权 WRITE_CALENDAR;未授权会失败或返回 0。
    • 若你本地定制过删除实现,请确认删除逻辑已启用并正确执行(未被注释、已重新打包到 App)。

配置项 ConfigOptions

  • ACCOUNT_NAME: 账户名。标识日历所属账户。
  • ACCOUNT_TYPE: 账户类型。标识账户的类型。
  • OWNER_ACCOUNT: 所有者账户。通常与 ACCOUNT_NAME 相同。
  • NAME: 日历名称。内部名称。
  • CALENDAR_DISPLAY_NAME: 日历显示名。展示给用户看的名称。
  • CALENDAR_TIME_ZONE: 日历时区。如 Asia/Shanghai
  • VISIBLE: 是否可见。1 可见,0 不可见。
  • SYNC_EVENTS: 是否同步事件。1 同步,0 不同步。
  • CALENDAR_ACCESS_LEVEL: 访问级别。见下表。

访问级别(CALENDAR_ACCESS_LEVEL

  • 0: CAL_ACCESS_NONE(无)
  • 100: CAL_ACCESS_FREEBUSY(仅忙闲)
  • 200: CAL_ACCESS_READ(只读)
  • 300: CAL_ACCESS_RESPOND(可响应)
  • 400: CAL_ACCESS_OVERRIDE(可覆盖)
  • 500: CAL_ACCESS_CONTRIBUTOR(贡献者)
  • 600: CAL_ACCESS_EDITOR(编辑)
  • 700: CAL_ACCESS_OWNER(所有者,默认)
  • 800: CAL_ACCESS_ROOT(Root)

返回数据结构

interface InsertCalendarResult {
  calendarId: number;
  accountName: string;
  accountType: string;
  ownerAccount: string | null;
  name: string | null;
  displayName: string | null;
  visible: boolean;
  syncEvents: boolean | null;
  accessLevel: number | null;
  timeZone: string | null;
}

interface SetCalendarResult {
  errMsg: string;
  Calendars: InsertCalendarResult;
}

错误码

  • 1001: getAppContext 为 null。
  • 1002: 参数不合法或插入失败(如账号/类型为空、系统限制等)。

新增日历事项(createCalendarEvent)

// 创建国庆节事项
uni.createCalendarEvent(
  {
    calendarId: 11,
    title: "国庆节",
    description: "国庆节放假内容",
    dtStart: 1759276800000, // 开始时间(毫秒时间戳)
    dtEnd: 1759327200000, // 结束时间(毫秒时间戳)
    eventTimeZone: "Asia/Shanghai",
    location: "天安门广场",
    hasAlarm: true,
    reminderMinutes: 90, // 提前90分钟提醒
    accessLevel: 0, // 访问级别:0=公开, 1=机密, 2=私密
    availability: 0, // 忙闲状态:0=忙碌, 1=空闲, 2=待定
  },
  {
    success: (res) => {
      console.log("新增事项成功", res);
      console.log("事项ID:", res.eventId);
    },
    fail: (err) => {
      console.error("新增事项失败", err);
    },
    complete: (res) => {
      console.log("操作完成", res);
    },
  }
);

配置项 CreateEventConfig

  • calendarId: number - 日历 ID(必填)
  • title: string - 事项标题(可选)
  • description: string - 事项描述(可选)
  • dtStart: number - 开始时间,毫秒时间戳(必填)
  • dtEnd: number - 结束时间,毫秒时间戳(必填)
  • allDay: boolean - 是否全天事件(可选,默认 false)
  • eventTimeZone: string - 时区(可选,默认设备时区)
  • location: string - 地点(可选)
  • hasAlarm: boolean - 是否设置提醒(可选)
  • reminderMinutes: number - 提醒提前分钟数(可选,默认 10 分钟)
  • accessLevel: number - 访问级别(可选,默认 0)
    • -1: 默认
    • 0: 公开
    • 1: 机密
    • 2: 私密
  • availability: number - 忙闲状态(可选,默认 0)
    • 0: 忙碌
    • 1: 空闲
    • 2: 待定

返回数据结构

interface CreateEventResult {
  errMsg: string; // 'createCalendarEvent:ok' | 错误信息
  eventId: number; // 创建的事项ID
}

错误码(新增事项)

  • 2001: getAppContext 为 null
  • 2002: 参数不合法或创建失败

查询日历事项(queryCalendarEvents)

// 查询指定日历的所有事项
uni.queryCalendarEvents(
  {
    calendarId: 11,
  },
  {
    success: (res) => {
      console.log("获取事项成功", res);
      res.events.forEach((event) => {
        console.log("事项ID:", event.eventId);
        console.log("标题:", event.title);
        console.log("开始时间:", new Date(event.dtStart));
        console.log("结束时间:", new Date(event.dtEnd));
        console.log("地点:", event.location);
        console.log("提醒分钟:", event.reminderMinutes);
      });
    },
    fail: (err) => {
      console.error("获取事项失败", err);
    },
    complete: (res) => {
      console.log("操作完成", res);
    },
  }
);

// 按时间范围查询
uni.queryCalendarEvents(
  {
    calendarId: 11,
    start: 1759276800000, // 开始时间戳
    end: 1759327200000, // 结束时间戳
  },
  {
    success: (res) => {
      console.log("时间范围内的事项:", res.events);
    },
    fail: (err) => {
      console.error("查询失败", err);
    },
  }
);

// 按关键词搜索
uni.queryCalendarEvents(
  {
    calendarId: 11,
    search: "国庆", // 搜索标题或描述中包含"国庆"的事项
  },
  {
    success: (res) => {
      console.log("搜索结果:", res.events);
    },
    fail: (err) => {
      console.error("搜索失败", err);
    },
  }
);

配置项 QueryEventConfig

  • calendarId: number - 日历 ID(可选,不指定则查询所有日历)
  • start: number - 开始时间戳(可选)
  • end: number - 结束时间戳(可选)
  • search: string - 搜索关键词(可选,在标题和描述中搜索)

返回数据结构

interface QueryEventResult {
  errMsg: string; // 'queryCalendarEvents:ok' | 错误信息
  events: CalendarEvent[]; // 事项列表
}

interface CalendarEvent {
  eventId: number; // 事项ID
  calendarId: number; // 日历ID
  title: string | null; // 标题
  description: string | null; // 描述
  dtStart: number; // 开始时间(毫秒时间戳)
  dtEnd: number; // 结束时间(毫秒时间戳)
  allDay: boolean; // 是否全天
  eventTimeZone: string | null; // 开始时区
  eventEndTimeZone: string | null; // 结束时区
  location: string | null; // 地点
  hasAlarm: boolean | null; // 是否有提醒
  availability: number | null; // 忙闲状态
  accessLevel: number | null; // 访问级别
  reminderMinutes?: number | null; // 提醒分钟数
}

错误码(查询事项)

  • 2001: getAppContext 为 null
  • 2002: 查询失败

删除日历事项(deleteCalendarEvent)

// 按时间范围删除事项
uni.deleteCalendarEvent(
  {
    calendarId: 11,
    start: 1759276800000, // 开始时间戳
    end: 1759327200000, // 结束时间戳
  },
  {
    success: (res) => {
      console.log("删除事项成功", res);
      console.log("删除数量:", res.deletedCount);
    },
    fail: (err) => {
      console.error("删除事项失败", err);
    },
    complete: (res) => {
      console.log("操作完成", res);
    },
  }
);

配置项 DeleteEventConfig

  • calendarId: number - 日历 ID(必填)
  • start: number - 开始时间戳(必填)
  • end: number - 结束时间戳(必填)

注意: 删除功能目前仅支持按时间范围批量删除,不支持按单个 eventId 删除。

返回数据结构

interface DeleteEventResult {
  errMsg: string; // 'deleteCalendarEvent:ok' | 错误信息
  deletedCount: number; // 实际删除的事项数量
}

错误码(删除事项)

  • 2001: getAppContext 为 null
  • 2002: 参数不合法或删除失败

完整使用示例

以下是一个完整的日历事项管理示例,展示如何组合使用所有功能:

// 1. 首先获取现有日历信息
uni.getCalendarInfo({
  success: (res) => {
    console.log("现有日历:", res.Calendars);

    // 如果没有合适的日历,创建一个新的
    if (res.Calendars.length === 0) {
      createNewCalendar();
    } else {
      // 使用第一个日历
      const calendarId = res.Calendars[0].calendarId;
      manageEvents(calendarId);
    }
  },
  fail: (err) => {
    console.error("获取日历失败:", err);
  },
});

// 2. 创建新日历
function createNewCalendar() {
  uni.setCalendarInfo(
    {
      ACCOUNT_NAME: "com.example.myapp",
      ACCOUNT_TYPE: "com.example",
      OWNER_ACCOUNT: "com.example.myapp",
      NAME: "MyApp Calendar",
      CALENDAR_DISPLAY_NAME: "我的应用日历",
      CALENDAR_TIME_ZONE: "Asia/Shanghai",
      VISIBLE: 1,
      SYNC_EVENTS: 1,
      CALENDAR_ACCESS_LEVEL: 700,
    },
    {
      success: (res) => {
        console.log("创建日历成功:", res.Calendars);
        manageEvents(res.Calendars.calendarId);
      },
      fail: (err) => {
        console.error("创建日历失败:", err);
      },
    }
  );
}

// 3. 管理日历事项
function manageEvents(calendarId) {
  // 创建事项
  const now = new Date();
  const startTime = now.getTime();
  const endTime = now.getTime() + 2 * 60 * 60 * 1000; // 2小时后

  uni.createCalendarEvent(
    {
      calendarId: calendarId,
      title: "重要会议",
      description: "与客户的重要会议",
      dtStart: startTime,
      dtEnd: endTime,
      eventTimeZone: "Asia/Shanghai",
      location: "会议室A",
      hasAlarm: true,
      reminderMinutes: 30,
      accessLevel: 0,
      availability: 0,
    },
    {
      success: (res) => {
        console.log("创建事项成功:", res);

        // 查询所有事项
        queryAllEvents(calendarId);
      },
      fail: (err) => {
        console.error("创建事项失败:", err);
      },
    }
  );
}

// 4. 查询所有事项
function queryAllEvents(calendarId) {
  uni.queryCalendarEvents(
    { calendarId: calendarId },
    {
      success: (res) => {
        console.log("所有事项:", res.events);

        // 按时间范围查询
        const today = new Date();
        const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);

        uni.queryCalendarEvents(
          {
            calendarId: calendarId,
            start: today.getTime(),
            end: tomorrow.getTime(),
          },
          {
            success: (res) => {
              console.log("今天的事项:", res.events);

              // 搜索特定事项
              searchEvents(calendarId);
            },
            fail: (err) => {
              console.error("查询今天事项失败:", err);
            },
          }
        );
      },
      fail: (err) => {
        console.error("查询事项失败:", err);
      },
    }
  );
}

// 5. 搜索事项
function searchEvents(calendarId) {
  uni.queryCalendarEvents(
    {
      calendarId: calendarId,
      search: "会议",
    },
    {
      success: (res) => {
        console.log("搜索结果:", res.events);

        // 删除事项(示例:删除今天的所有事项)
        deleteTodayEvents(calendarId);
      },
      fail: (err) => {
        console.error("搜索失败:", err);
      },
    }
  );
}

// 6. 删除事项
function deleteTodayEvents(calendarId) {
  const today = new Date();
  const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);

  uni.deleteCalendarEvent(
    {
      calendarId: calendarId,
      start: today.getTime(),
      end: tomorrow.getTime(),
    },
    {
      success: (res) => {
        console.log("删除事项成功,删除数量:", res.deletedCount);
      },
      fail: (err) => {
        console.error("删除事项失败:", err);
      },
    }
  );
}

Async 方法使用

以下演示六个 Async 方法(Promise 风格)的调用:

// 1) 获取日历账户(Promise)
uni
  .getCalendarInfoAsync()
  .then((res) => {
    console.log("获取日历信息成功:", res);
  })
  .catch((err) => {
    console.error("获取日历信息失败:", err);
  });

// 2) 设置/创建日历账户(Promise)
uni
  .setCalendarInfoAsync({
    ACCOUNT_NAME: "com.example.myapp",
    ACCOUNT_TYPE: "com.example",
    OWNER_ACCOUNT: "com.example.myapp",
    NAME: "MyApp Calendar",
    CALENDAR_DISPLAY_NAME: "我的应用日历",
    CALENDAR_TIME_ZONE: "Asia/Shanghai",
    VISIBLE: 1,
    SYNC_EVENTS: 1,
    CALENDAR_ACCESS_LEVEL: 700,
  })
  .then((res) => {
    console.log("设置成功:", res.Calendars);
  })
  .catch((err) => {
    console.error("设置失败:", err);
  });

// 3) 按ID或账户名删除日历账户(Promise)
uni
  .deleteCalendarInfoAsync({ calendarId: 12 })
  .then((res) => {
    console.log("删除成功:", res.deletedCount);
  })
  .catch((err) => {
    console.error("删除失败:", err);
  });

// 4) 查询事项(可带条件)(Promise)
uni
  .queryCalendarEventsAsync({ calendarId: 11, search: "会议" })
  .then((res) => {
    console.log("查询成功:", res.events);
  })
  .catch((err) => {
    console.error("查询失败:", err);
  });

// 5) 新增事项(Promise)
uni
  .createCalendarEventAsync({
    calendarId: 11,
    title: "重要会议",
    description: "与客户的重要会议",
    dtStart: Date.now(),
    dtEnd: Date.now() + 2 * 60 * 60 * 1000,
    eventTimeZone: "Asia/Shanghai",
    hasAlarm: true,
    reminderMinutes: 30,
  })
  .then((res) => {
    console.log("新增事项成功, 事项ID:", res.eventId);
  })
  .catch((err) => {
    console.error("新增事项失败:", err);
  });

// 6) 按时间范围删除事项(Promise)
const today = new Date();
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
uni
  .deleteCalendarEventAsync({
    calendarId: 11,
    start: today.getTime(),
    end: tomorrow.getTime(),
  })
  .then((res) => {
    console.log("删除事项成功, 删除数量:", res.deletedCount);
  })
  .catch((err) => {
    console.error("删除事项失败:", err);
  });

返回数据结构

interface Calendar {
  calendarId: number; // 日历ID
  accountName: string; // 账户名称
  accountType: string; // 账户类型
  ownerAccount: string | null; // 所有者账户
  name: string | null; // 日历名称
  displayName: string | null; // 显示名称
  visible: boolean; // 是否可见
  syncEvents: boolean; // 是否同步事件
  isPrimary: boolean | null; // 是否为主日历
  accessLevel: number | null; // 访问级别
  timeZone: string | null; // 时区
  color: number | null; // 颜色
  deleted: boolean | null; // 是否已删除
  allowedReminders: string | null; // 允许的提醒方式
  allowedAvailability: string | null; // 允许的可用性
  allowedAttendeeTypes: string | null; // 允许的参与者类型
}

权限说明

本插件需要以下 Android 权限:

  • READ_CALENDAR - 读取日历数据(用于 getCalendarInfoqueryCalendarEvents
  • WRITE_CALENDAR - 写入/创建日历(用于 setCalendarInfocreateCalendarEventdeleteCalendarEvent

平台支持

  • ✅ Android (API 21+)
  • ❌ iOS (暂不支持)
  • ❌ H5 (暂不支持)
  • ❌ 小程序 (暂不支持)

注意事项

  1. 仅支持 Android 平台
  2. 使用 getCalendarInfoqueryCalendarEvents 需授权读取权限;使用 setCalendarInfocreateCalendarEventdeleteCalendarEvent 需授权读写权限
  3. 建议在用户交互后调用,避免权限弹窗被拦截
  4. 写入日历时需要提供有效的 ACCOUNT_NAMEACCOUNT_TYPE。部分 ROM 对无对应同步适配器(SyncAdapter)的账户类型可能拒绝创建日历,属于系统行为差异
  5. 若未指定时区,将使用设备默认时区;VISIBLE/SYNC_EVENTS 取值为 0/1
  6. 时间戳使用毫秒级时间戳,可通过 new Date().getTime() 获取当前时间戳
  7. 删除事项功能目前仅支持按时间范围批量删除,不支持按单个 eventId 删除
  8. 提醒功能需要设备支持,部分 ROM 可能不支持自定义提醒时间

更新日志

v1.1.0

  • 新增日历事项管理功能
  • 支持创建日历事项(createCalendarEvent)
  • 支持查询日历事项(queryCalendarEvents)
  • 支持删除日历事项(deleteCalendarEvent)
  • 支持提醒、位置、访问级别等高级功能
  • 支持按时间范围和关键词搜索

v1.0.0

  • 初始版本发布
  • 支持获取 Android 系统日历信息
  • 完整的类型定义和错误处理

许可证

MIT License

隐私、权限声明

1. 本插件需要申请的系统权限列表:

需要 READ_CALENDAR 权限用于读取系统日历数据

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据,仅读取本地日历信息

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: