Android 向系统日历添加日程提醒事件

Android 向系统日历添加日程提醒事件

1.权限申请

<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />

注意:6.0 以上需要申请权限才可以使用哦

2.日历相关uri

if (Build.VERSION.SDK_INT >= 8) {calanderURL = "content://com.android.calendar/calendars";calanderEventURL = "content://com.android.calendar/events";calanderRemiderURL = "content://com.android.calendar/reminders";
} else {calanderURL = "content://calendar/calendars";calanderEventURL = "content://calendar/events";calanderRemiderURL = "content://calendar/reminders";
}

3.工具类

public class CalendarUtils {private static String calanderURL;private static String calanderEventURL;private static String calanderRemiderURL;
​private static String CALENDARS_NAME = "smartTime";private static String CALENDARS_ACCOUNT_NAME = "smartTime@smartTime.com";private static String CALENDARS_ACCOUNT_TYPE = "com.zg.smartTime";private static String CALENDARS_DISPLAY_NAME = "smartTime账号";
​/*** 初始化uri*/static {if (Build.VERSION.SDK_INT >= 8) {calanderURL = "content://com.android.calendar/calendars";calanderEventURL = "content://com.android.calendar/events";calanderRemiderURL = "content://com.android.calendar/reminders";} else {calanderURL = "content://calendar/calendars";calanderEventURL = "content://calendar/events";calanderRemiderURL = "content://calendar/reminders";}}
​/*** 获取日历ID** @param context* @return 日历ID*/public static int checkAndAddCalendarAccounts(Context context) {int oldId = checkCalendarAccounts(context);if (oldId >= 0) {return oldId;} else {long addId = addCalendarAccount(context);if (addId >= 0) {return checkCalendarAccounts(context);} else {return -1;}}}
​/*** 检查是否存在日历账户** @param context* @return*/private static int checkCalendarAccounts(Context context) {
​Cursor userCursor = context.getContentResolver().query(Uri.parse(calanderURL), null, null, null, CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL + " ASC ");try {if (userCursor == null)//查询返回空值return -1;int count = userCursor.getCount();if (count > 0) {//存在现有账户,取第一个账户的id返回userCursor.moveToLast();return userCursor.getInt(userCursor.getColumnIndex(CalendarContract.Calendars._ID));} else {return -1;}} finally {if (userCursor != null) {userCursor.close();}}}
​/*** 添加一个日历账户** @param context* @return*/private static long addCalendarAccount(Context context) {TimeZone timeZone = TimeZone.getDefault();ContentValues value = new ContentValues();value.put(CalendarContract.Calendars.NAME, CALENDARS_NAME);
​value.put(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME);value.put(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE);value.put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, CALENDARS_DISPLAY_NAME);value.put(CalendarContract.Calendars.VISIBLE, 1);value.put(CalendarContract.Calendars.CALENDAR_COLOR, Color.BLUE);value.put(CalendarContract.Calendars.CALENDAR_ACCESS_LEVEL, CalendarContract.Calendars.CAL_ACCESS_OWNER);value.put(CalendarContract.Calendars.SYNC_EVENTS, 1);value.put(CalendarContract.Calendars.CALENDAR_TIME_ZONE, timeZone.getID());value.put(CalendarContract.Calendars.OWNER_ACCOUNT, CALENDARS_ACCOUNT_NAME);value.put(CalendarContract.Calendars.CAN_ORGANIZER_RESPOND, 0);
​Uri calendarUri = Uri.parse(calanderURL);calendarUri = calendarUri.buildUpon().appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true").appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, CALENDARS_ACCOUNT_NAME).appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, CALENDARS_ACCOUNT_TYPE).build();
​Uri result = context.getContentResolver().insert(calendarUri, value);long id = result == null ? -1 : ContentUris.parseId(result);return id;}
​/*** 向日历中添加一个事件** @param context* @param calendar_id (必须参数)* @param title* @param description* @param begintime   事件开始时间,以从公元纪年开始计算的协调世界时毫秒数表示。 (必须参数)* @param endtime     事件结束时间,以从公元纪年开始计算的协调世界时毫秒数表示。(非重复事件:必须参数)* @return*/private static Uri insertCalendarEvent(Context context, long calendar_id, String title, String description, long begintime, long endtime) {ContentValues event = new ContentValues();event.put("title", title);event.put("description", description);// 插入账户的idevent.put("calendar_id", calendar_id);event.put(CalendarContract.Events.DTSTART, begintime);//必须有event.put(CalendarContract.Events.DTEND, endtime);//非重复事件:必须有event.put(CalendarContract.Events.HAS_ALARM, 1);//设置有闹钟提醒event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());//这个是时区,必须有,//添加事件Uri newEvent = context.getContentResolver().insert(Uri.parse(calanderEventURL), event);return newEvent;}
​/*** 查询日历事件** @param context* @param title   事件标题* @return 事件id, 查询不到则返回""*/public static String queryCalendarEvent(Context context, long calendar_id, String title, String description, long start_time, long end_time) {// 根据日期范围构造查询Uri.Builder builder = CalendarContract.Instances.CONTENT_URI.buildUpon();ContentUris.appendId(builder, start_time);ContentUris.appendId(builder, end_time);Cursor cursor = context.getContentResolver().query(builder.build(), null, null, null, null);String tmp_title;String tmp_desc;long temp_calendar_id;if (cursor.moveToFirst()) {do {tmp_title = cursor.getString(cursor.getColumnIndex("title"));tmp_desc = cursor.getString(cursor.getColumnIndex("description"));temp_calendar_id = cursor.getLong(cursor.getColumnIndex("calendar_id"));long dtstart = cursor.getLong(cursor.getColumnIndex("dtstart"));if (TextUtils.equals(title, tmp_title) && TextUtils.equals(description, tmp_desc) && calendar_id == temp_calendar_id && dtstart == start_time) {String eventId = cursor.getString(cursor.getColumnIndex("event_id"));return eventId;}} while (cursor.moveToNext());}return "";}
​/*** 添加日历提醒:标题、描述、开始时间共同标定一个单独的提醒事件** @param context* @param title          日历提醒的标题,不允许为空* @param description    日历的描述(备注)信息* @param begintime      事件开始时间,以从公元纪年开始计算的协调世界时毫秒数表示。* @param endtime        事件结束时间,以从公元纪年开始计算的协调世界时毫秒数表示。* @param remind_minutes 提前remind_minutes分钟发出提醒* @param callback       添加提醒是否成功结果监听*/public static void addCalendarEventRemind(Context context, @NonNull String title, String description, long begintime, long endtime, int remind_minutes, onCalendarRemindListener callback) {long calendar_id = checkAndAddCalendarAccounts(context);if (calendar_id < 0) {// 获取日历失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._CALENDAR_ERROR);}return;}//根据标题、描述、开始时间查看提醒事件是否已经存在String event_id = queryCalendarEvent(context, calendar_id, title, description, begintime, endtime);//如果提醒事件不存在,则新建事件if (TextUtils.isEmpty(event_id)) {Uri newEvent = insertCalendarEvent(context, calendar_id, title, description, begintime, endtime);if (newEvent == null) {// 添加日历事件失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._EVENT_ERROR);}return;}event_id = ContentUris.parseId(newEvent) + "";}//为事件设定提醒ContentValues values = new ContentValues();values.put(CalendarContract.Reminders.EVENT_ID, event_id);// 提前remind_minutes分钟有提醒values.put(CalendarContract.Reminders.MINUTES, remind_minutes);values.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);Uri uri = context.getContentResolver().insert(Uri.parse(calanderRemiderURL), values);if (uri == null) {// 添加提醒失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._REMIND_ERROR);}return;}
​//添加提醒成功if (null != callback) {callback.onSuccess();}}
​/*** 添加日历提醒:标题、描述、开始时间共同标定一个单独的提醒事件** @param context* @param title          日历提醒的标题,不允许为空* @param description    日历的描述(备注)信息* @param begintime      事件开始时间,以从公元纪年开始计算的协调世界时毫秒数表示。* @param endtime        事件结束时间,以从公元纪年开始计算的协调世界时毫秒数表示。* @param remind_minutes 提前remind_minutes分钟发出提醒* @param callback       添加提醒是否成功结果监听*/public static void addCalendarEventRemind(Context context, @NonNull String title, String description, long begintime, long endtime, int remind_minutes, String rules, onCalendarRemindListener callback) {long calendar_id = checkAndAddCalendarAccounts(context);if (calendar_id < 0) {// 获取日历失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._CALENDAR_ERROR);}return;}//根据标题、描述、开始时间查看提醒事件是否已经存在String event_id = queryCalendarEvent(context, calendar_id, title, description, begintime, endtime);//如果提醒事件不存在,则新建事件if (TextUtils.isEmpty(event_id)) {ContentValues event = new ContentValues();event.put("title", title);event.put("description", description);// 插入账户的idevent.put("calendar_id", calendar_id);event.put(CalendarContract.Events.RRULE, rules);event.put(CalendarContract.Events.DTSTART, begintime);//必须有event.put(CalendarContract.Events.DTEND, endtime);//非重复事件:必须有event.put(CalendarContract.Events.HAS_ALARM, 1);//设置有闹钟提醒event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());//这个是时区,必须有,//添加事件Uri newEvent = context.getContentResolver().insert(Uri.parse(calanderEventURL), event);if (newEvent == null) {// 添加日历事件失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._EVENT_ERROR);}return;}event_id = ContentUris.parseId(newEvent) + "";}//为事件设定提醒ContentValues values = new ContentValues();values.put(CalendarContract.Reminders.EVENT_ID, event_id);// 提前remind_minutes分钟有提醒values.put(CalendarContract.Reminders.MINUTES, remind_minutes);values.put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT);Uri uri = context.getContentResolver().insert(Uri.parse(calanderRemiderURL), values);if (uri == null) {// 添加提醒失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._REMIND_ERROR);}return;}
​//添加提醒成功if (null != callback) {callback.onSuccess();}}
​/*** 删除日历提醒事件:根据标题、描述和开始时间来定位日历事件** @param context* @param title       提醒的标题* @param description 提醒的描述:deeplink URI* @param startTime   事件的开始时间* @param callback    删除成功与否的监听回调*/public static void deleteCalendarEventRemind(Context context, String title, String description, long startTime, onCalendarRemindListener callback) {Cursor eventCursor = context.getContentResolver().query(Uri.parse(calanderEventURL), null, null, null, null);Log.i("zxd", "deleteCalendarEventRemind: " + (eventCursor == null));try {if (eventCursor == null)//查询返回空值return;if (eventCursor.getCount() > 0) {//遍历所有事件,找到title、description、startTime跟需要查询的title、descriptio、dtstart一样的项for (eventCursor.moveToFirst(); !eventCursor.isAfterLast(); eventCursor.moveToNext()) {String eventTitle = eventCursor.getString(eventCursor.getColumnIndex("title"));String eventDescription = eventCursor.getString(eventCursor.getColumnIndex("description"));long dtstart = eventCursor.getLong(eventCursor.getColumnIndex("dtstart"));if (!TextUtils.isEmpty(title) && title.equals(eventTitle) && !TextUtils.isEmpty(description) && description.equals(eventDescription) && dtstart == startTime) {int id = eventCursor.getInt(eventCursor.getColumnIndex(CalendarContract.Calendars._ID));//取得idUri deleteUri = ContentUris.withAppendedId(Uri.parse(calanderEventURL), id);int rows = context.getContentResolver().delete(deleteUri, null, null);if (rows == -1) {// 删除提醒失败直接返回if (null != callback) {callback.onFailed(onCalendarRemindListener.Status._REMIND_ERROR);}return;}//删除提醒成功if (null != callback) {callback.onSuccess();}}}}} finally {if (eventCursor != null) {eventCursor.close();}}}
​/*** 日历提醒添加成功与否监控器*/public static interface onCalendarRemindListener {enum Status {_CALENDAR_ERROR,_EVENT_ERROR,_REMIND_ERROR}
​void onFailed(Status error_code);
​void onSuccess();}
​/*** 辅助方法:获取设置时间起止时间的对应毫秒数** @param year* @param month  1-12* @param day    1-31* @param hour   0-23* @param minute 0-59* @return*/public static long remindTimeCalculator(int year, int month, int day, int hour, int minute) {Calendar calendar = Calendar.getInstance();calendar.set(year, month - 1, day, hour, minute, 0);calendar.set(Calendar.MILLISECOND, 0);//要保持数据一致。如果你的毫秒数不一样,即使日历事件的标题和内容一样也是不能删除的return calendar.getTimeInMillis();}
}

4.使用

在大约13.26的时候,添加一个13.28分的日程,延迟3分钟取消日程。

public void cancelCalendar(View view) {//设置上午10点0分到10点10分long times = System.currentTimeMillis();final Calendar cal = Calendar.getInstance();cal.setTimeInMillis(times);cal.set(Calendar.HOUR_OF_DAY, 13);cal.set(Calendar.MINUTE, 28);
​Calendar cal2 = Calendar.getInstance();cal2.setTimeInMillis(times);cal2.set(Calendar.HOUR_OF_DAY, 13);cal2.set(Calendar.MINUTE, 30);
​CalendarUtils.addCalendarEventRemind(this, "今天啦!", "每日10点",CalendarUtils.remindTimeCalculator(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH),cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)),CalendarUtils.remindTimeCalculator(cal2.get(Calendar.YEAR), cal2.get(Calendar.MONTH) + 1, cal2.get(Calendar.DAY_OF_MONTH),cal2.get(Calendar.HOUR_OF_DAY), cal2.get(Calendar.MINUTE)), 0, new CalendarUtils.onCalendarRemindListener() {@Overridepublic void onFailed(Status error_code) {Log.e("zxd", "onFailed: " + error_code);}
​@Overridepublic void onSuccess() {Log.i("zxd", "onSuccess: 添加日程成功!");}});
​view.postDelayed(new Runnable() {@Overridepublic void run() {CalendarUtils.deleteCalendarEventRemind(SystemCalendarActivity.this, "今天啦!", "每日10点",CalendarUtils.remindTimeCalculator(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH),cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)), new CalendarUtils.onCalendarRemindListener() {@Overridepublic void onFailed(Status error_code) {Log.e("zxd", "onFailed: " + error_code);}
​@Overridepublic void onSuccess() {Log.i("zxd", "onSuccess: 删除日程成功!");}});}}, 1000 * 60 * 3);
}

5.重复规则

iCalendar Recurrence Rule 规范翻译

规范原文链接:RFC 5545

Recurrence Rule

重复规则 rrule(Recurrence Rule) 属于 icalendar 属性中的一个,配合 dtstart 可以完整描述一个事件的重复行为并计算出重复事件的具体发生 (Occurence)。

重复规则包含多个属性, 每个属性以 NAME = VALUE 对的形式存在, 属性与属性之间用分号区分, 属性之间没有特定的顺序要求,在同一个重复规则中每个属性最多只能出现一次。


FREQ

FREQ 属性表示重复规则的类型, 是重复规则中必须定义的一条属性。 可选的 VALUE 有:

SECONDLY, 表示以秒为间隔单位进行重复。 MINUTELY, 表示以分钟为间隔单位进行重复。 HOURLY, 表示以小时为间隔单位进行重复。 DAILY, 表示以天为间隔单位进行重复。 WEEKLY, 表示以周为间隔单位进行重复。 MONTHLY, 表示以月为间隔单位进行重复。 YEARLY, 表示以年为间隔单位进行重复。

INTERVAL

INTERVAL 属性表示重复规则的间隔, 必须为正整数。 默认值为1, 对应上述不同的 FREQ 值分别表示每一秒,每一分钟, 每一小时, 每一天, 每一周, 每一月, 每一年。

UNTIL

UNTIL 属性定义了一个日期-时间值,用以限制重复规则。 这个日期-时间值表示这个重复规则的最后一次事件的发生时间。 如果重复规则中未包含 UNTIL 和 COUNT 属性, 则表示该重复规则无限重复。

COUNT

COUNT 属性通过定义重复事件的发生次数来限制重复规则。 正整数。

BYSECOND, BYMINUTE, BYHOUR

BYSECOND 取值范围 0 - 59, 可以理解为 “…… 的 n 秒”。 BYMINUTE 取值范围 0 - 59, 可以理解为 “…… 的 n 分”。 BYHOUR 取值范围 0 - 23, 可以理解为 “…… 的 n 时”。

BYDAY

BYDAY 取值范围: MO(周一), TU(周二), WE(周三), TU(周四), FR(周五), SA(周六), SU(周日)。可以有多个值,用逗号分隔。

每个值可以在前面加上一个正整数(+n)或者负整数(-n),用以在 MONTHLY 或者 YEARLY 的重复类型中表示第 n 个周几。 例如,在一个 MONTHLY 类型的重复规则中, +1MO(或者1MO)表示这个月的第1个周一,如果是 -1MO 则表示这个月的最后1个周一。

如果前面没有数字,则表示在这个重复类型中的所有的周几, 比如在一个 MONTHLY 的重复类型中, MO 表示这个月里所有的周一。

BYMONTHDAY

BYMONTHDAY 取值范围 1 - 31 或者 -31 - -1,表示一个月的第几天。 比如, -10 表示一个月的倒数第10天。可以有多个值,用逗号分隔。

BYYEARDAY

BYYEARDAY 取值范围 1 - 366 或者 -366 - -1, 表示一年的第几天。 比如, -1 表示一年的最后一天, 306 表示一年的第306天。可以有多个值,用逗号分隔。

BYWEEKNO

BYWEEKNO 取值范围 1 - 53 或者 -53 - -1, 表示一年的第几周, 只在 YEARLY 类型的重复规则中有效。 比如, 3 表示一年的第 3 周。可以有多个值,用逗号分隔。(注:一年的第一周是指第一个至少包含该年4天时间的那一周)

BYMONTY

BYMONTH 取值范围 1 - 12, 表示一年的第几个月。可以有多个值,用逗号分隔。

WKST

WKST 取值范围 MO, TU, WE, TH, FR, SA, SU。 默认值为 MO。 当一个 WEEKLY 类型的重复规则, INTERVAL 大于 1, 且带有 BYDAY 属性时, 则必须带有 WKST 属性。 当一个 YEARLY 类型的重复规则带有 BYWEEKNO 属性时, 也必须带有 WKST 属性。

BYSETPOS

BYSETPOS 取值范围 1 - 366 或者 -366 - -1, 表示规则指定的事件集合中的第n个事件, 必须与另外的 BYxxx 属性共同使用。 比如,每月的最后一组工作日可以表示为: RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1


如果一个 BYxxx 属性的值超过了它对应的范围,则该属性会被忽略。

当有多个 BYxxx 属性存在的时候, 在代入了 FREQ 和 INTEVAL 属性后,按照以下顺序代入到已有规则上:BYMONTH, BYWEEKNO, BYYEARDAY, BYMONTHDAY, BYDAY, BYHOUR, BYMINUTE, BYSECOND,BYSETPOS

例如: RRULE:FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9; BYMINUTE=30

首先,将 INTERVAL=2 代入到 FREQ=YEARLY 上,得到“每2年”, 然后在这基础上代入 BYMONTH=1 得到 “每2年的1月”, 再代入 BYDAY=SU, 得到“每2年的1月的所有周日”, 再代入 BYHOUR=8,9, 得到 “每2年的1月的所有周日的8点9点”(注意是8点和9点,不是8点到9点), 最后代入 BYMINUTE=30, 得到“每2年的1月的所有周日的8点30分9点30分”。

规则中未注明的时间信息,以开始时间(dtstart)为准。


Examples

每天发生一次,重复10次: RRULE:FREQ=DAILY;COUNT=10

每天发生一次,直到1997年12月24日: RRULE:FREQ=DAILY;UNTIL=19971224T000000Z

每2天发生一次,直到永远: RRULE:FREQ=DAILY;INTERVAL=2

每10天发生一次,重复5次: RRULE:FREQ=DAILY;INTERVAL=10;COUNT=5

当前日期为1998年1月1日9点0分0秒,之后的3年里每年的1月每天发生一次: RRULE:FREQ=YEARLY;UNTIL=20000131T090000Z;BYMONTH=1;BYDAY=SU,MO,TU,WE,TH,FR,SA 或者: RRULE:FREQ=DAILY;UNTIL=20000131T090000Z;BYMONTH=1

每周一次,共发生10次: RRULE:FREQ=WEEKLY;COUNT=10

每周一次,直到1997年12月24日: RRULE:FREQ=WEEKLY;UNTIL=19971224T000000Z

每2周一次, 直到永远: RRULE:FREQ=WEEKLY;INTERVAL=2;WKST=SU

当前时间为1997年9月2日9点0分0秒,每周二和周四各发生一次,持续5周: RRULE:FREQ=WEEKLY;UNTIL=19971007T000000Z;WKST=SU;BYDAY=TU,TH 或者: RRULE:FREQ=WEEKLY;COUNT=10;WKST=SU;BYDAY=TU,TH

每周一, 周三, 周五各一次,直到1997年12月24日: RRULE:FREQ=WEEKLY;INTERVAL=2;UNTIL=19971224T000000Z;WKST=SU;BYDAY=MO,WE,FR

每2周的周二和周四各发生一次,共发生8次: RRULE:FREQ=WEEKLY;INTERVAL=2;COUNT=8;WKST=SU;BYDAY=TU,TH

每月的第一个周五发生一次,共发生10次: RRULE:FREQ=MONTHLY;COUNT=10;BYDAY=1FR

每月的第一个周五发生一次,直到1997年12月24日: RRULE:FREQ=MONTHLY;UNTIL=19971224T000000Z;BYDAY=1FR

每2个月的第一个周日和最后一个周日个发生一次,共发生10次: RRULE:FREQ=MONTHLY;INTERVAL=2;COUNT=10;BYDAY=1SU,-1SU

每月的倒数第二个周一发生一次,共发生6次: RRULE:FREQ=MONTHLY;COUNT=6;BYDAY=-2MO

每月的倒数第三天发生一次,直到永远: RRULE:FREQ=MONTHLY;BYMONTHDAY=-3

每月的第2天和第15天各发生一次,共发生10次: RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=2,15

每月的第1天和最后1天各发生一次,共发生10次: RRULE:FREQ=MONTHLY;COUNT=10;BYMONTHDAY=1,-1

每个18个月的1号至15号每天发生一次,共发生10次: RRULE:FREQ=MONTHLY;INTERVAL=18;COUNT=10;BYMONTHDAY=10,11,12,13,14,15

每2个月的所有周二每天发生一次: RRULE:FREQ=MONTHLY;INTERVAL=2;BYDAY=TU

每年6月和7月各发生一次,共发生10次: RRULE:FREQ=YEARLY;COUNT=10;BYMONTH=6,7

每2年的一月,二月,三月各发生一次,共10次: RRULE:FREQ=YEARLY;INTERVAL=2;COUNT=10;BYMONTH=1,2,3

每3年的第一天,第100天和第200天各发生一次,共10次: RRULE:FREQ=YEARLY;INTERVAL=3;COUNT=10;BYYEARDAY=1,100,200

每年的第20个周一发生一次,直到永远: RRULE:FREQ=YEARLY;BYDAY=20MO

每年的第20周的周一(以周一为一周起始日)发生一次,直到永远: RRULE:FREQ=YEARLY;BYWEEKNO=20;BYDAY=MO

每年3月的所有周四,直到永远: RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=TH

每年6月,7月,8月的所有周四,直到永远: RRULE:FREQ=YEARLY;BYDAY=TH;BYMONTH=6,7,8

每一个黑色星期五(13号那天为周五)发生一次,直到永远: RRULE:FREQ=MONTHLY;BYDAY=FR;BYMONTHDAY=13

每月第一个周日之后那一周的周六发生一次,直到永远: RRULE:FREQ=MONTHLY;BYDAY=SA;BYMONTHDAY=7,8,9,10,11,12,13

每4年的11月的第一个周一之后的那个周二发生一次,直到永远 RRULE:FREQ=YEARLY;INTERVAL=4;BYMONTH=11;BYDAY=TU;BYMONTHDAY=2,3,4,5,6,7,8

The 3rd instance into the month of one of Tuesday, Wednesday or Thursday, for the next 3 months(没法翻译,自己理解): RRULE:FREQ=MONTHLY;COUNT=3;BYDAY=TU,WE,TH;BYSETPOS=3

每月的倒数第2个工作日,直到永远: RRULE:FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-2


怎么使用?在哪使用?

FREQ=DAILY;INTERVAL=1

创建ContentValues数据的时候使用

ContentValues event = new ContentValues();
event.put("title", title);
event.put("description", description);
// 插入账户的id
event.put("calendar_id", calendar_id);
event.put(CalendarContract.Events.RRULE, rules);
event.put(CalendarContract.Events.DTSTART, begintime);//必须有
event.put(CalendarContract.Events.DTEND, endtime);//非重复事件:必须有
event.put(CalendarContract.Events.HAS_ALARM, 1);//设置有闹钟提醒
event.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());//这个是时区,必须有,
//添加事件
Uri newEvent = context.getContentResolver().insert(Uri.parse(calanderEventURL), event);

6.参考

Android向系统日历添加日程提醒事件

Android--日历事件查看、添加、删除

Android添加系统日历提醒,并从项目日历查看添加的事件<日历 111>(带效果图)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/20204.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业既要用u盘又要防止u盘泄密怎么办?

企业在日常生产生活过程中&#xff0c;使用u盘交换数据是最企业最常用也是最便携的方式&#xff0c;但是在使用u盘的同时&#xff0c;也给企业的数据保密工作带来了很大的挑战&#xff0c;往往很多情况下企业的是通过u盘进行数据泄漏的。很多企业采用一刀切的方式&#xff0c;直…

【Kubernetes】

目录 一、Kubernetes 概述1、K8S 是什么&#xff1f;2、为什么要用 K8S?3、Kubernetes 集群架构与组件 二、核心组件1、Master 组件2、Node 组件3、K8S创建Pod的工作流程&#xff1f;&#xff08;重点&#xff09;4、K8S资源对象&#xff08;重点&#xff09;5、Kubernetes 核…

iOS数字转为图片

根据数字&#xff0c;转成对应的图片 - (void)viewDidLoad {[super viewDidLoad];[self testNum2String:10086]; }/// 根据数字&#xff0c;显示对应的图片 数字用特定的图片显示 - (void)testNum2String:(NSInteger)num {UIView *numContentView [[UIView alloc] initWithFr…

flask------请求拓展

flask中也有类似与django中的中间件&#xff0c;只不过是另一种写法&#xff0c;但是他们的作用是一样的&#xff0c;下面我们就一一介绍&#xff1a; 1.before_request 作用 : before_request 相当于 django 中的 process_request&#xff0c;每一个请求在被处理前都会经…

JAVA环境变量配置(windows)

windows配置环境变量&#xff08;大小写不区分&#xff09;&#xff1a; 新建JAVA_HOME&#xff1a;jdk的根目录 D:\Java\jdk1.8.0_71Path&#xff08;必须&#xff09;%JAVA_HOME%\bin新建&#xff08;类路径&#xff09;CLASSPATH: .;D:\Java\jdk1.8.0_71\lib(或者.;%JAVA_HO…

【外卖系统】套餐管理

新增套餐 需求分析 后台可以管理套餐信息&#xff0c;通过新增套餐功能来添加一个新的套餐&#xff0c;在添加套餐时需要选择当前套餐所属的套餐分类和包含的菜品&#xff0c;并需要上传套餐对应的图片。 页面发送ajax请求&#xff0c;请求服务端获取套餐分类数据并展示到下…

最细致讲解yolov8模型推理完整代码--(前处理,后处理)

研究yolov8时&#xff0c;一直苦寻不到Yolov8完整的模型推理代码演示&#xff0c;大部分人都是基于Yolo已经封装好的函数调用&#xff0c;这个网上教程很多&#xff0c;本文就不赘述这方面的内容了&#xff0c;接下来将细致全面的讲解yolov8模型推理代码&#xff0c;也就是yolo…

卡片的点击事件通过点击进行路由传参

下面是详情页 通过 接收 <template><div class"detail"><img :src"row.imgUrl"><van-icon name"arrow-left" click"back" /></div> </template><script> export default {created() {let …

Webpack 安装教程

Webpack 是一个前端资源加载/打包工具。 安装 Webpack 使用 cnpm 安装 webpack&#xff1a; cnpm install webpack -g 创建项目 接下来我们创建一个目录 app&#xff1a; mkdir app 在 app 目录下添加 runoob1.js 文件&#xff0c;代码如下&#xff1a; app/runoob1.js 文件…

LeetCode每日一题Day4——26. 删除有序数组中的重复项

✨博主&#xff1a;命运之光 &#x1f984;专栏&#xff1a;算法修炼之练气篇&#xff08;C\C版&#xff09; &#x1f353;专栏&#xff1a;算法修炼之筑基篇&#xff08;C\C版&#xff09; &#x1f433;专栏&#xff1a;算法修炼之练气篇&#xff08;Python版&#xff09; …

【分布式任务调度平台 XXL-JOB 急速入门】从零开始将 XXL-JOB 接入到自己的项目

&#x1f4a7; 分布式任务调度平台 X X L − J O B 急速入门&#xff1a;从零开始将 X X L − J O B 接入到自己的项目 \color{#FF1493}{分布式任务调度平台 XXL-JOB 急速入门&#xff1a;从零开始将 XXL-JOB 接入到自己的项目} 分布式任务调度平台XXL−JOB急速入门&#xff1a…

增强知识保护和知识管理:PDM系统的知识库特色

在现代竞争激烈的商业环境中&#xff0c;知识保护和知识管理对企业的发展至关重要。PDM系统&#xff08;Product Data Management&#xff0c;产品数据管理&#xff09;作为一款强大的数字化工具&#xff0c;具备丰富的知识库特色&#xff0c;帮助企业增强知识保护和知识管理的…

《TCP IP 网络编程》第十五章

第 15 章 套接字和标准I/O 15.1 标准 I/O 的优点 标准 I/O 函数的两个优点&#xff1a; 除了使用 read 和 write 函数收发数据外&#xff0c;还能使用标准 I/O 函数收发数据。下面是标准 I/O 函数的两个优点&#xff1a; 标准 I/O 函数具有良好的移植性标准 I/O 函数可以利用…

FPGA学习——蜂鸣器实现音乐播放器并播放两只老虎

文章目录 一、蜂鸣器简介1.1 蜂鸣器分类1.2 PWM 二、C4开发板原理图三、如何产生不同的音调四、代码实现及分析五、总结 一、蜂鸣器简介 1.1 蜂鸣器分类 蜂鸣器一般分为有源蜂鸣器和无源蜂鸣器。二者的区别在于&#xff0c;有源蜂鸣器内部含有振动源和功放电路&#xff0c;只…

前端如何打开钉钉(如何唤起注册表中路径与软件路径不关联的软件)

在前端唤起本地应用时&#xff0c;我查询了资料&#xff0c;在注册表中找到腾讯视频会议的注册表情况&#xff0c;如下&#xff1a; 在前端代码中加入 window.location.href"wemeet:"; 就可以直接唤起腾讯视频会议&#xff0c;但是我无法唤起钉钉 之所以会这样&…

【Android】使用 CameraX 实现基础拍照功能

目录 1. 基础开发环境 2. 添加相关依赖 3. APP 布局 4. 主流程逻辑 5. 调试或安装 APK 6. 项目完整代码 1. 基础开发环境 JDK&#xff1a;JDK17 Android Studio&#xff1a;Android Studio Giraffe | 2022.3.1 Android SDK&#xff1a;Android API 34 Gradle: gradle-7.2…

前端如何并发控制

本文节选自我的博客&#xff1a;前端如何并发控制 &#x1f496; 作者简介&#xff1a;大家好&#xff0c;我是MilesChen&#xff0c;偏前端的全栈开发者。&#x1f4dd; CSDN主页&#xff1a;爱吃糖的猫&#x1f525;&#x1f4e3; 我的博客&#xff1a;爱吃糖的猫&#x1f4d…

2023年人工智能技术与智慧城市发展白皮书

人工智能与智慧城市是当前热门的话题和概念&#xff0c;通过将人工智能技术应用在城市管理和服务中&#xff0c;利用自动化、智能化和数据化的方式提高城市运行效率和人民生活质量&#xff0c;最终实现城市发展的智慧化&#xff0c;提升城市居民的幸福感。 AI技术在城市中的应…

QT中使用ffmpeg的api进行视频的播放

在了解ffmpeg使用api进行视频的播放之前&#xff0c;我们首先了解一下视频的播放流程。 一、视频的播放流程 首先是我们最常见的视频文件&#xff0c;在播放流程中首先是要打开视频文件&#xff0c;将视频文件中的数据进行解封装&#xff0c;之后再将解封装之后的视频进行解码…

【C#学习笔记】引用类型(2)

文章目录 ObjectEqualsGetTypeToStringGetHashCode string逐字文本复合格式字符串字符串内插 StringBuilderStringBuilder 的工作原理StringBuilder提供的方法访问字符迭代字符查询字符 dynamic Object 支持 .NET 类层次结构中的所有类&#xff0c;并为派生类提供低级别服务。…