如何在Android 4中静默地向默认日历添加日历事件,而无需使用Intent?

31
我想在安卓4+版本中通过编程(直接)添加日历事件。是否可以在模拟器上测试?因为我没有安卓手机。如果可以提供一些示例代码,那就太好了。我阅读了安卓开发者的日历提供程序,但我感到困惑。我如何将事件添加到用户的默认日历中?我不需要同步。
编辑:我不想启动事件添加意图。相反,我希望完全从代码中添加它们,而不启动其他活动。我需要能够在模拟器上测试事件是否将添加到设备的默认用户的主日历中。我如何设置模拟器以查看用户的默认日历?

可能是如何在Android中添加日历事件?的重复问题。 - jww
1
@jww 他想要默默地完成它,与其他问题不同。 - VitorMM
不要忘记这行代码 <uses-permission android:name="android.permission.WRITE_SETTINGS"/> - Sirop4ik
7个回答

47

这里有一个我最终制作的工作示例:

ContentResolver cr = ctx.getContentResolver();
ContentValues values = new ContentValues();
    
values.put(CalendarContract.Events.DTSTART, dtstart);
values.put(CalendarContract.Events.TITLE, title);
values.put(CalendarContract.Events.DESCRIPTION, comment);

TimeZone timeZone = TimeZone.getDefault();
values.put(CalendarContract.Events.EVENT_TIMEZONE, timeZone.getID());

// Default calendar
values.put(CalendarContract.Events.CALENDAR_ID, 1);

values.put(CalendarContract.Events.RRULE, "FREQ=DAILY;UNTIL="
        + dtUntill);
// Set Period for 1 Hour
values.put(CalendarContract.Events.DURATION, "+P1H");

values.put(CalendarContract.Events.HAS_ALARM, 1);

// Insert event to calendar
Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI, values);

其中dtuntil是什么

SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
Calendar dt = Calendar.getInstance();

// Where untilDate is a date instance of your choice, for example 30/01/2012
dt.setTime(untilDate);

// If you want the event until 30/01/2012, you must add one day from our day because UNTIL in RRule sets events before the last day
dt.add(Calendar.DATE, 1);
String dtUntill = yyyyMMdd.format(dt.getTime());

参考: 重复规则


4
格式不应该是"yyyymmdd",而应该是"yyyyMMdd"。根据这里(http://docs.oracle.com/javase/6/docs/api/java/text/SimpleDateFormat.html),"m"代表分钟。 - silmeth
请查看此问题:https://dev59.com/7F4b5IYBdhLWcg3woi9g - Sun
我想捕捉提醒事件并调用 WebService 来处理该提醒。如何使用上述代码实现? - Suhas Bachewar
1
cr.insert(CalendarContract.Events.CONTENT_URI, values); 在logcat中没有任何崩溃信息。附注:我已按照Marshmallow的要求添加并授予权限。 - Pranav Mahajan
2
日历ID = 1 必须为2以用于事件,或者为3以用于生日事件...谢谢。 - avisper
显示剩余3条评论

23

我认为你要找的部分是使用意图插入事件。在此部分中,它描述了如何为要添加的事件创建一个意图,然后模拟器上的默认日历程序将会响应并添加该事件。如果您真的想看到它接收到正确的信息,您可能需要设置一个虚拟档案以使日历程序启动。


来自Android Dev网站的代码:

Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
    .setData(Events.CONTENT_URI)
    .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
    .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
    .putExtra(Events.TITLE, "Yoga")
    .putExtra(Events.DESCRIPTION, "Group class")
    .putExtra(Events.EVENT_LOCATION, "The gym")
    .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
    .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
startActivity(intent);

3
不,我想以编程的方式添加这些事件。就像这个链接中所述:https://dev59.com/_Gsz5IYBdhLWcg3wfn66 - oikonomopo
在那个链接中,他们也展示了上面的这种方法。我所描述的不是程序化的吗?你的意思是说你不想使用意图吗?你想要默默地添加事件,是这个意思吗? - Velocitas
1
是的,我想在默认日历中悄悄地添加事件。 - oikonomopo

6

不要忘记在清单文件中添加权限

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

代码源自:->Android 开发者网站

long calID = 3; // Make sure to which calender you want to add event 
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 9, 14, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 9, 14, 8, 45);
endMillis = endTime.getTimeInMillis();


ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, "Hackathon");
values.put(Events.DESCRIPTION, "do some code");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
Uri uri = cr.insert(Events.CONTENT_URI, values);

// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());

这里的日历ID是什么意思? - K Pradeep Kumar Reddy
@PradeepkumarReddy 我已经在上面声明了calID。calID是日历ID,您要在哪个日历提供程序中插入事件。根据文档,您应该添加日历ID。 - Nikunj Paradva
运行此代码后,我能在Google日历应用程序中看到事件吗? - K Pradeep Kumar Reddy
你尝试过使用Google日历API创建事件吗?GSuite文档指出,强烈建议从服务器环境而非移动环境调用此API。 - K Pradeep Kumar Reddy
是的,我尝试过使用 Google 日历,并且这段代码可以正常工作。 - Nikunj Paradva

3
使用此代码,您可以通过编程方式将事件添加到设备日历。我已在Marshmallow中进行了测试,对我来说运行良好。
private void addToDeviceCalendar(String startDate,String endDate, String title,String description, String location) {

        String stDate = startDate;
        String enDate = endDate;

        GregorianCalendar calDate = new GregorianCalendar();
        //GregorianCalendar calEndDate = new GregorianCalendar();

        SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        SimpleDateFormat targetFormat = new SimpleDateFormat("yyyy,MM,dd,HH,mm");
        Date date,edate;
        try {
            date = originalFormat.parse(startDate);
            stDate=targetFormat.format(date);

        } catch (ParseException ex) {}

        long startMillis = 0;
        long endMillis = 0;
        String dates[] = stDate.split(",");

        SD_YeaR = dates[0];
        SD_MontH = dates[1];
        SD_DaY = dates[2];
        SD_HouR = dates[3];
        SD_MinutE = dates[4];


        /*Log.e("YeaR ", SD_YeaR);
        Log.e("MontH ",SD_MontH );
        Log.e("DaY ", SD_DaY);
        Log.e(" HouR", SD_HouR);
        Log.e("MinutE ", SD_MinutE);*/

        calDate.set(Integer.parseInt(SD_YeaR), Integer.parseInt(SD_MontH)-1, Integer.parseInt(SD_DaY), Integer.parseInt(SD_HouR), Integer.parseInt(SD_MinutE));
        startMillis = calDate.getTimeInMillis();
/*
        try {
            edate = originalFormat.parse(endDate);
            enDate=targetFormat.format(edate);

        } catch (ParseException ex) {}


        String end_dates[] = endDate.split(",");

        String ED_YeaR = end_dates[0];
        String ED_MontH = end_dates[1];
        String ED_DaY = end_dates[2];

        String ED_HouR = end_dates[3];
        String ED_MinutE = end_dates[4];


        calEndDate.set(Integer.parseInt(ED_YeaR), Integer.parseInt(ED_MontH)-1, Integer.parseInt(ED_DaY), Integer.parseInt(ED_HouR), Integer.parseInt(ED_MinutE));
        endMillis = calEndDate.getTimeInMillis();*/

        try {
            ContentResolver cr = getActivity().getContentResolver();
            ContentValues values = new ContentValues();
            values.put(CalendarContract.Events.DTSTART, startMillis);
            values.put(CalendarContract.Events.DTEND, calDate.getTimeInMillis() + 60 * 60 * 1000);
            values.put(CalendarContract.Events.TITLE, title);
            values.put(CalendarContract.Events.DESCRIPTION, description);
            values.put(CalendarContract.Events.EVENT_LOCATION,location);
            values.put(CalendarContract.Events.HAS_ALARM,1);
            values.put(CalendarContract.Events.CALENDAR_ID, 1);
            values.put(CalendarContract.Events.EVENT_TIMEZONE, Calendar.getInstance()
                    .getTimeZone().getID());
            System.out.println(Calendar.getInstance().getTimeZone().getID());
            if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_CALENDAR) != PackageManager.PERMISSION_GRANTED) {

                return;
            }
            Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI, values);

            long eventId = Long.parseLong(uri.getLastPathSegment());
            Log.d("Ketan_Event_Id", String.valueOf(eventId));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

@Ketan Ramani:在Marshmallow设备上无法工作,但没有错误。 - Aniruddha

2

在阅读了几篇文章并尝试了几次后,我终于找到了一种方法,可以在Android 8和10上很好地工作。

我的代码:

    public void addEventToCalendar() {
    Context myContext = getContext();
    String[] projection = {"_id", "calendar_displayName"};
    Cursor calCursor = myContext.getContentResolver().query(CalendarContract.Calendars.CONTENT_URI, projection, CalendarContract.Calendars.VISIBLE + " = 1 AND "  + CalendarContract.Calendars.IS_PRIMARY + "=1", null, CalendarContract.Calendars._ID + " ASC");
    if(calCursor.getCount() <= 0){
        calCursor = myContext.getContentResolver().query(CalendarContract.Calendars.CONTENT_URI, projection, CalendarContract.Calendars.VISIBLE + " = 1", null, CalendarContract.Calendars._ID + " ASC");
    }

    while (calCursor.moveToNext()) {
        long id = calCursor.getLong(calCursor.getColumnIndexOrThrow(CalendarContract.Calendars._ID));
        long startMillis;
        long endMillis;
        Calendar beginTime = Calendar.getInstance();
        beginTime.set(2021, 9, 22, 15, 30);
        startMillis = beginTime.getTimeInMillis();
        Calendar endTime = Calendar.getInstance();
        endTime.set(2021, 9, 22, 16, 45);
        endMillis = endTime.getTimeInMillis();

        ContentResolver cr = Objects.requireNonNull(getActivity()).getContentResolver();
        ContentValues values = new ContentValues();
        values.put(CalendarContract.Events.DTSTART, startMillis);
        values.put(CalendarContract.Events.DTEND, endMillis);
        values.put(CalendarContract.Events.TITLE, "My event");
        values.put(CalendarContract.Events.DESCRIPTION, "Nice description");
        values.put(CalendarContract.Events.CALENDAR_ID, id);
        Log.i("ID","my Id"+ id);
        values.put(CalendarContract.Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
        Uri uri = cr.insert(CalendarContract.Events.CONTENT_URI, values);

        long eventID = Long.parseLong(uri.getLastPathSegment());
    }

}

我能够在不同的手机上进行测试,并且插入操作会同时在谷歌日历和基本的安卓日历中完成。
通常这种方法可以确保将事件插入到设备上所有可用的日历中。虽然我无法测试,但我对其有很高的期望。

2

以下是询问用户添加事件到哪个日历的方法。由于我的要求是这样的,而且没有在一个地方找到解决方案。我总结了一下并想出了这个解决方案,希望能帮助到某些人 :)

final ContentResolver cr = this.getContentResolver();
        Cursor cursor ;
        if (Integer.parseInt(Build.VERSION.SDK) >= 8 )
            cursor = cr.query(Uri.parse("content://com.android.calendar/calendars"), new String[]{ "_id", "calendar_displayName" }, null, null, null);
        else
            cursor = cr.query(Uri.parse("content://calendar/calendars"), new String[]{ "_id", "displayname" }, null, null, null);
        if (cursor != null && cursor.moveToFirst() ) {
            final String[] calNames = new String[cursor.getCount()];
            final int[] calIds = new int[cursor.getCount()];
            for (int i = 0; i < calNames.length; i++) {
                calIds[i] = cursor.getInt(0);
                calNames[i] = cursor.getString(1);
                cursor.moveToNext();
            }

            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            final long startDate = sdf.parse(slotData.getSlot_date() + " " + slotData.getSlot_from()).getTime();
            final long endDate = sdf.parse(slotData.getSlot_date() + " " + slotData.getSlot_to()).getTime();

            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle("Select any one");
            builder.setSingleChoiceItems(calNames, -1, new DialogInterface.OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    ContentValues cv = new ContentValues();
                    cv.put("calendar_id", calIds[which]);
                    cv.put("title", title);
                    cv.put("dtstart", startDate);
                    cv.put("hasAlarm", 1);
                    cv.put("dtend", endDate);
                    cv.put("eventTimezone", TimeZone.getDefault().getID());

                    Uri newEvent ;
                    if (Integer.parseInt(Build.VERSION.SDK) >= 8 )
                        newEvent = cr.insert(Uri.parse("content://com.android.calendar/events"), cv);
                    else
                        newEvent = cr.insert(Uri.parse("content://calendar/events"), cv);

                    if (newEvent != null) {
                        long id = Long.parseLong( newEvent.getLastPathSegment() );
                        ContentValues values = new ContentValues();
                        values.put( "event_id", id );
                        values.put( "method", 1 );
                        values.put( "minutes", 15 ); // 15 minutes
                        if (Integer.parseInt(Build.VERSION.SDK) >= 8 )
                            cr.insert( Uri.parse( "content://com.android.calendar/reminders" ), values );
                        else
                            cr.insert( Uri.parse( "content://calendar/reminders" ), values );

                    }
                    dialog.cancel();
                }

            });

            builder.create().show();
        }
        if (cursor != null) {
            cursor.close();
        }

1
同意以上所有答案,但需要注意的是导入的是日历ID。你不能使用1,因为三星手机将其用于其自己的日历(S Planner)。因此,日历ID是您想要事件的电子邮件的ID。您可以通过以下代码获取特定事件的日历ID。
int calenderId=-1;
        String calenderEmaillAddress="xxx@gmail.com";
        String[] projection = new String[]{
                CalendarContract.Calendars._ID,
                CalendarContract.Calendars.ACCOUNT_NAME};
        ContentResolver cr = activity.getContentResolver();
        Cursor cursor = cr.query(Uri.parse("content://com.android.calendar/calendars"), projection,
                CalendarContract.Calendars.ACCOUNT_NAME + "=? and (" +
                        CalendarContract.Calendars.NAME + "=? or " +
                        CalendarContract.Calendars.CALENDAR_DISPLAY_NAME + "=?)",
                new String[]{calenderEmaillAddress, calenderEmaillAddress,
                        calenderEmaillAddress}, null);

        if (cursor.moveToFirst()) {

            if (cursor.getString(1).equals(calenderEmaillAddress))
                 calenderId=cursor.getInt(0); //youre calender id to be insered in above 2 answer


        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接