סקירה כללית של ספק היומן

ספק היומן הוא מאגר של אירועי היומן של המשתמש. ממשק ה-API של ספק היומן מאפשר לבצע שאילתות, להוסיף, לעדכן ולמחוק פעולות על יומנים, אירועים, משתתפים, תזכורות וכו'.

אפליקציות ומתאמי סנכרון יכולים להשתמש בממשק ה-API של ספק היומן. הכללים משתנים בהתאם לסוג התוכנית שמבצעת את הקריאות. המסמך הזה מתמקד בעיקר בשימוש ב- Calendar Provider API כאפליקציה. במאמר מתאמי סנכרון מוסבר על ההבדלים בין מתאמי הסנכרון.

בדרך כלל, כדי לקרוא או לכתוב נתוני יומן, המניפסט של אפליקציה לכלול את ההרשאות המתאימות, שמתוארות בקטע הרשאות. כדי לבצע פעולות נפוצות בקלות רבה יותר, ספק יומן Google מציע קבוצה של כוונות, כפי שמתואר במאמר כוונות ביומן Google. הכוונות האלה מפנות את המשתמשים לאפליקציית יומן Google כדי להוסיף, להציג ולערוך אירועים. המשתמש יוצר אינטראקציה עם אפליקציית יומן Google ואז חוזר לאפליקציה המקורית. לכן האפליקציה שלכם לא צריכה לבקש הרשאות, והיא לא צריכה לספק ממשק משתמש כדי לצפות באירועים או ליצור אירועים.

יסודות

ספקי תוכן שומרים נתונים ומאפשרים לאפליקציות לגשת אליהם. ספקי התוכן שמוצעים על ידי פלטפורמת Android (כולל ספק היומן) חושפים בדרך כלל נתונים כקבוצה של טבלאות שמבוססות על מודל של מסד נתונים יחסיים, שבו כל שורה היא רשומה וכל עמודה היא נתונים מסוג ומשמעות מסוימים. באמצעות ה-API של ספק היומן, אפליקציות מתאמי סנכרון יכולים לקבל גישה לקריאה/כתיבה לטבלאות של מסדי הנתונים נתוני היומן של המשתמש.

כל ספק תוכן חושף URI ציבורי (עטוף כאובייקט Uri) שמזהה באופן ייחודי את קבוצת הנתונים שלו. ספק תוכן ששולט מספר קבוצות נתונים (מספר טבלאות) חושף URI נפרד לכל אחת מהן. הכול מזהי URI של ספקים מתחילים במחרוזת 'content:// '. הזה מזהה את הנתונים כנשלטים על ידי ספק תוכן. ספק היומן מגדיר קבועים למזהי ה-URI של כל אחת מהכיתות (טבלאות) שלו. מזהי ה-URI האלה הם בפורמט <class>.CONTENT_URI. עבור לדוגמה, Events.CONTENT_URI.

באיור 1 מוצג ייצוג גרפי של מודל הנתונים של ספק יומן Google. מוצגות בה הטבלאות הראשיות והשדות שמקשרים ביניהן.

מודל הנתונים של ספק היומן

איור 1. מודל הנתונים של ספק היומן.

למשתמש יכולים להיות כמה יומנים, וניתן לשייך יומנים שונים לסוגים שונים של חשבונות (יומן Google, Exchange וכן הלאה).

ה-CalendarContract מגדיר את מודל הנתונים של מידע שקשור ליומן ולאירועים. הנתונים האלה מאוחסנים במספר טבלאות, שמפורטות בהמשך.

טבלה (מחלקה) תיאור

CalendarContract.Calendars

בטבלה הזו מאוחסנים הפרטים הספציפיים ליומן. כל שורה בטבלה הזו כוללת את הפרטים של יומן יחיד, כמו שם, צבע, מידע לסנכרון וכן הלאה.
CalendarContract.Events בטבלה הזו נשמרים הפרטים הספציפיים לאירוע. כל שורה בטבלה הזו מכילה את המידע על אירוע אחד – לדוגמה, שם האירוע, המיקום, שעת ההתחלה, שעת הסיום וכו'. האירוע יכול להתרחש פעם אחת או לחזור על עצמו כמה פעמים. המשתתפים, התזכורות והמאפיינים המורחבים מאוחסנים בטבלאות נפרדות. לכל אחד מהם יש EVENT_ID שמפנה אל _ID בטבלת האירועים.
CalendarContract.Instances בטבלה הזו מופיעים שעת ההתחלה ושעת הסיום של כל אירוע. כל שורה בטבלה הזו מייצג אירוע אחד של אירוע. לאירועים חד-פעמיים יש מיפוי של 1:1 של מופעים לאירועים. במקרה של אירועים חוזרים, המערכת מסמנת באופן אוטומטי כמה שורות שנוצרו בהתאם למספר חזרות של אותו אירוע.
CalendarContract.Attendees בטבלה הזו שמורים הפרטים של המשתתפים (האורחים) באירוע. כל שורה מייצגת אורח אחד באירוע. היא מציינת את סוג האורח ואת תגובת הנוכחות שלו לאירוע.
CalendarContract.Reminders בטבלה הזו נשמרים נתוני ההתראות. כל שורה מייצגת התראה אחת לגבי אירוע. לאירוע הזה יכולות להיות מספר תזכורות. המספר המקסימלי של תזכורות לכל אירוע מצוין בשדה MAX_REMINDERS, שמוגדר על ידי מתאם הסנכרון שבבעלותו היומן הנתון. התזכורות מפורטות לפי מספר הדקות לפני האירוע, ויש להן שיטה שקובעת איך המשתמש יקבל התראה.

ממשק ה-API של ספק יומן Google תוכנן להיות גמיש וחזק. ב חשוב לספק חוויית משתמש טובה להגן על תקינות היומן והנתונים שבו. לכן, ריכזנו כאן כמה דברים שכדאי לזכור כשמשתמשים ב-API:

  • הוספה, עדכון והצגה של אירועים ביומן. כדי להוסיף, לשנות ולקרוא אירועים ישירות מספק היומן, צריך את ההרשאות המתאימות. עם זאת, אם אתם לא מפתחים אפליקציית יומן או מתאם סנכרון מלאים, אין צורך לבקש את ההרשאות האלה. במקום זאת, אפשר להשתמש בכוונות (intents) שנתמכות באפליקציית יומן Google ל-Android כדי להעביר את פעולות הקריאה והכתיבה לאפליקציה הזו. כשמשתמשים בכוונות, האפליקציה שולחת את המשתמשים לאפליקציית היומן כדי לבצע את הפעולה הרצויה בטופס שמולא מראש. אחרי שמסיימים, הם מוחזרים לבקשה. על ידי תכנון האפליקציה שלכם לביצוע פעולות נפוצות באמצעות יומן Google, אתם מספקים למשתמשים ממשק משתמש עקבי וחזק. זו הגישה המומלצת. למידע נוסף, ראה יומן Google Intents.
  • מתאמי סנכרון. מתאם סנכרון מסנכרן את נתוני היומן במכשיר של המשתמש באמצעות שרת אחר או מקור נתונים אחר. ב CalendarContract.Calendars והקבוצה CalendarContract.Events טבלאות, יש עמודות ששמורות לשימוש במתאמי הסנכרון. הספק והאפליקציות לא אמורים לשנות אותם. למעשה, הם לא גלויים אלא אם ניגשים אליהם כמתאם סנכרון. מידע נוסף על מתאמי סנכרון זמין במאמר מתאמי סנכרון.

הרשאות משתמשים

כדי לקרוא נתוני יומן, האפליקציה צריכה לכלול את ההרשאה READ_CALENDAR בקובץ המניפסט שלה. הוא חייב לכלול את ההרשאה WRITE_CALENDAR למחיקה, להוספה או לעדכון של נתוני היומן:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    ...
</manifest>

טבלת היומנים

הטבלה CalendarContract.Calendars מכילה פרטים של יומנים ספציפיים. אפשר לכתוב בעמודות הבאות של יומנים גם באפליקציה וגם במתאם סנכרון. לרשימה המלאה של השדות הנתמכים: מסמך עזר של CalendarContract.Calendars.

קבוע תיאור
NAME שם היומן.
CALENDAR_DISPLAY_NAME השם של היומן הזה שיוצג למשתמש.
VISIBLE ערך בוליאני שמציין אם היומן נבחר להצגה. הערך 0 מציין שלא צריך להציג אירועים שמשויכים ליומן הזה. הערך 1 מציין שאירועים שמשויכים ליומן הזה צריכים להופיע. הערך הזה משפיע על יצירת השורות בטבלה CalendarContract.Instances.
SYNC_EVENTS ערך בוליאני שמציין אם יש לסנכרן את היומן ולוודא שהוא מכיל אירועים שמאוחסנים במכשיר. הערך 0 מציין שלא מסנכרנים את היומן הזה ולא שומרים את האירועים שלו במכשיר. הערך 1 מציין שצריך לסנכרן את האירועים ביומן הזה ולאחסן את האירועים במכשיר.

הכללת סוג חשבון לכל הפעולות

אם שולחים שאילתה על Calendars.ACCOUNT_NAME, צריך לכלול גם Calendars.ACCOUNT_TYPE בבחירה. הסיבה לכך היא שחשבון נתון נחשב לייחודי רק בהינתן ACCOUNT_NAME ACCOUNT_TYPE. ACCOUNT_TYPE הוא המחרוזת שתואמת ל- לאימות החשבון שנעשה בו שימוש כשהחשבון נרשם אצל AccountManager. יש גם חשבון מסוג מיוחד נקרא ACCOUNT_TYPE_LOCAL למשך יומנים שלא משויכים לחשבון של המכשיר. חשבונות ACCOUNT_TYPE_LOCAL לא מסתנכרנים.

שליחת שאילתה ליומן

הנה דוגמה שמראה איך לאחזר את היומנים שבבעלות אדם מסוים משתמש. כדי לפשט את הדוגמה, פעולת השאילתה מוצגת בשרשור של ממשק המשתמש ('השרשור הראשי'). בפועל, יש לעשות זאת באופן אסינכרוני במקום בשרשור הראשי. לדיון נוסף, אפשר להיכנס Loaders (מטענים). אם אתם לא רק קוראים את הנתונים אלא משנים אותם, תוכלו לעיין במאמר AsyncQueryHandler.

Kotlin

// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
private val EVENT_PROJECTION: Array<String> = arrayOf(
        CalendarContract.Calendars._ID,                     // 0
        CalendarContract.Calendars.ACCOUNT_NAME,            // 1
        CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,   // 2
        CalendarContract.Calendars.OWNER_ACCOUNT            // 3
)

// The indices for the projection array above.
private const val PROJECTION_ID_INDEX: Int = 0
private const val PROJECTION_ACCOUNT_NAME_INDEX: Int = 1
private const val PROJECTION_DISPLAY_NAME_INDEX: Int = 2
private const val PROJECTION_OWNER_ACCOUNT_INDEX: Int = 3

Java

// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
    Calendars._ID,                           // 0
    Calendars.ACCOUNT_NAME,                  // 1
    Calendars.CALENDAR_DISPLAY_NAME,         // 2
    Calendars.OWNER_ACCOUNT                  // 3
};

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;

בחלק הבא של הדוגמה, נרכיב את השאילתה. הבחירה קובעת את הקריטריונים לשאילתה. בדוגמה הזו, השאילתה מחפשת יומנים שכוללים את ACCOUNT_NAME "hera@example.com", אל ACCOUNT_TYPE 'com.example', וגם OWNER_ACCOUNT "hera@example.com". אם אתם רוצים לראות את כל היומנים של המשתמש צפה, ולא רק ביומנים שבבעלות המשתמש, יש להשמיט את OWNER_ACCOUNT. השאילתה מחזירה אובייקט Cursor שאפשר להשתמש בו כדי לעבור על קבוצת התוצאות שהוחזרה על ידי שאילתת מסד הנתונים. לדיון נוסף לגבי שימוש בשאילתות בספקי תוכן, למידע נוסף, ראו ספקי תוכן.

Kotlin

// Run query
val uri: Uri = CalendarContract.Calendars.CONTENT_URI
val selection: String = "((${CalendarContract.Calendars.ACCOUNT_NAME} = ?) AND (" +
        "${CalendarContract.Calendars.ACCOUNT_TYPE} = ?) AND (" +
        "${CalendarContract.Calendars.OWNER_ACCOUNT} = ?))"
val selectionArgs: Array<String> = arrayOf("hera@example.com", "com.example", "hera@example.com")
val cur: Cursor = contentResolver.query(uri, EVENT_PROJECTION, selection, selectionArgs, null)

Java

// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
                        + Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"hera@example.com", "com.example",
        "hera@example.com"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

בקטע הבא משתמשים בסמן כדי לעבור דרך קבוצת התוצאות. הפונקציה משתמשת בערכי הקבועים שהוגדרו בתחילת הדוגמה כדי להחזיר את הערכים של כל שדה.

Kotlin

// Use the cursor to step through the returned records
while (cur.moveToNext()) {
    // Get the field values
    val calID: Long = cur.getLong(PROJECTION_ID_INDEX)
    val displayName: String = cur.getString(PROJECTION_DISPLAY_NAME_INDEX)
    val accountName: String = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX)
    val ownerName: String = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX)
    // Do something with the values...
}

Java

// Use the cursor to step through the returned records
while (cur.moveToNext()) {
    long calID = 0;
    String displayName = null;
    String accountName = null;
    String ownerName = null;

    // Get the field values
    calID = cur.getLong(PROJECTION_ID_INDEX);
    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);

    // Do something with the values...

   ...
}

שינוי יומן

כדי לבצע עדכון של יומן, אפשר לספק את _ID של היומן כמזהה שמצורף ל-Uri‏ (withAppendedId()) או כפריט הבחירה הראשון. הבחירה צריך להתחיל ב-"_id=?", selectionArg צריך להיות _ID של היומן. אפשר לבצע עדכונים גם על ידי קידוד המזהה ב-URI. בדוגמה הזו משנים את שם התצוגה של לוח שנה באמצעות הגישה (withAppendedId()):

Kotlin

const val DEBUG_TAG: String = "MyActivity"
...
val calID: Long = 2
val values = ContentValues().apply {
    // The new display name for the calendar
    put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar")
}
val updateUri: Uri = ContentUris.withAppendedId(CalendarContract.Calendars.CONTENT_URI, calID)
val rows: Int = contentResolver.update(updateUri, values, null, null)
Log.i(DEBUG_TAG, "Rows updated: $rows")

Java

private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

הוספת יומן

יומנים מנוהלים בעיקר על ידי מתאם סנכרון, צריך להוסיף יומנים חדשים רק כמתאם סנכרון. ברוב המקרים, אפליקציות יכולות לבצע רק שינויים שטחיים בלוחות השנה, כמו שינוי השם המוצג. אם אפליקציה צריכה ליצור יומן מקומי, היא יכולה לעשות זאת על ידי ביצוע ההוספה של היומן כמתאם סנכרון, באמצעות ACCOUNT_TYPE של ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL הוא סוג חשבון מיוחד של יומנים שלא משויכים לחשבון מכשיר. יומנים מהסוג הזה לא מסונכרנים עם שרת. במאמר מתאמי סנכרון מוסבר על מתאמי סנכרון.

טבלת אירועים

הטבלה CalendarContract.Events מכילה פרטים לאירועים נפרדים. כדי להוסיף, לעדכן או למחוק אירועים, יש צורך באפליקציה לכלול את ההרשאה WRITE_CALENDAR קובץ מניפסט.

אפשר לכתוב בעמודות הבאות של Events גם באפליקציה וגם במתאם לסנכרון. רשימה מלאה של השדות הנתמכים מופיעה בחומר העזר בנושא CalendarContract.Events.

קבוע תיאור
CALENDAR_ID _ID של היומן שאליו האירוע שייך.
ORGANIZER כתובת האימייל של מארגן האירוע (הבעלים) של האירוע.
TITLE שם האירוע.
EVENT_LOCATION המיקום שבו מתקיים האירוע.
DESCRIPTION תיאור האירוע.
DTSTART השעה שבה האירוע מתחיל, באלפיות השנייה לפי שעון UTC, מאז תחילת התקופה של זמן המערכת.
DTEND השעה שבה האירוע מסתיים באלפיות שנייה (UTC) מאז תחילת התקופה.
EVENT_TIMEZONE אזור הזמן של האירוע.
EVENT_END_TIMEZONE אזור הזמן של שעת הסיום של האירוע.
DURATION משך האירוע בפורמט RFC5545. לדוגמה, ערך של "PT1H" מציין שהאירוע אמור להימשך שעה אחת, וערך של "P2W" מציין של שבועיים.
ALL_DAY הערך 1 מציין שהאירוע הזה נמשך כל היום, כפי שמוגדר על ידי אזור הזמן המקומי. הערך 0 מציין שמדובר באירוע רגיל שיכול להתחיל ולהסתיים בכל שלב במהלך היום.
RRULE כלל החזרה של פורמט האירוע. לדוגמה, "FREQ=WEEKLY;COUNT=10;WKST=SU". טיפים נוספים לאופטימיזציה מפורטים דוגמאות נוספות כאן.
RDATE תאריכי החזרה של האירוע. בדרך כלל משתמשים ב-RDATE יחד עם RRULE כדי להגדיר קבוצה מצטברת של מופעים חוזרים. מידע נוסף זמין במפרט RFC5545.
AVAILABILITY אם האירוע נספר כזמן תפוס או כזמן פנוי שאפשר לתזמן מחדש.
GUESTS_CAN_MODIFY האם האורחים יכולים לשנות את האירוע.
GUESTS_CAN_INVITE_OTHERS האם האורחים יכולים להזמין אורחים אחרים.
GUESTS_CAN_SEE_GUESTS אם האורחים יוכלו לראות את רשימת הנוכחים.

הוספת אירועים

כשהאפליקציה מוסיפה אירוע חדש, מומלץ להשתמש ב-Intent‏ INSERT, כפי שמתואר במאמר שימוש ב-Intent להוספת אירוע. עם זאת, אם צריך, אפשר להוסיף אירועים ישירות. בקטע הזה נסביר איך לבצע הזה.

אלו הכללים להוספת אירוע חדש:

  • חובה לכלול את CALENDAR_ID ו-DTSTART.
  • חובה לכלול EVENT_TIMEZONE. כדי לקבל רשימה של מזהי אזורי הזמן המותקנים במערכת, משתמשים ב-getAvailableIDs(). חשוב לזכור שהכלל הזה לא חל אם מוסיפים אירוע באמצעות הכוונה INSERT, כפי שמתואר בקטע שימוש בכוונה כדי להוסיף אירוע. בתרחיש כזה, מערכת GA4 מספקת אזור זמן שמוגדר כברירת מחדל.
  • באירועים לא חוזרים, צריך לכלול את הערך DTEND.
  • באירועים חוזרים, עליך לכלול DURATION בנוסף ל-RRULE או ל-RDATE. שימו לב שהכלל הזה לא חל אם מוסיפים אירוע באמצעות הכוונה INSERT, כפי שמתואר במאמר שימוש בכוונה כדי להוסיף אירוע. בתרחיש כזה, אפשר להשתמש ב-RRULE בשילוב עם DTSTART ו-DTEND, ואפליקציית יומן Google תמיר אותו לאורך זמן באופן אוטומטי.

דוגמה להוספת אירוע. הפעולה הזו מתבצעת בשרשור של ממשק המשתמש כדי לפשט את התהליך. בפועל, כדאי לבצע הוספות ועדכונים בשרשור אסינכרוני כדי להעביר את הפעולה לשרשור ברקע. למידע נוסף, ראו AsyncQueryHandler.

Kotlin

val calID: Long = 3
val startMillis: Long = Calendar.getInstance().run {
    set(2012, 9, 14, 7, 30)
    timeInMillis
}
val endMillis: Long = Calendar.getInstance().run {
    set(2012, 9, 14, 8, 45)
    timeInMillis
}
...

val values = ContentValues().apply {
    put(CalendarContract.Events.DTSTART, startMillis)
    put(CalendarContract.Events.DTEND, endMillis)
    put(CalendarContract.Events.TITLE, "Jazzercise")
    put(CalendarContract.Events.DESCRIPTION, "Group workout")
    put(CalendarContract.Events.CALENDAR_ID, calID)
    put(CalendarContract.Events.EVENT_TIMEZONE, "America/Los_Angeles")
}
val uri: Uri = contentResolver.insert(CalendarContract.Events.CONTENT_URI, values)

// get the event ID that is the last element in the Uri
val eventID: Long = uri.lastPathSegment.toLong()
//
// ... do something with event ID
//
//

Java

long calID = 3;
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, "Jazzercise");
values.put(Events.DESCRIPTION, "Group workout");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
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());
//
// ... do something with event ID
//
//

הערה: בדוגמה הזו אפשר לראות איך מתבצע תיעוד מזהה האירוע אחרי היצירה שלו. זו הדרך הקלה ביותר לקבל מזהה אירוע. לעיתים קרובות צריך את מזהה האירוע כדי לבצע פעולות אחרות ביומן – לדוגמה, כדי להוסיף משתתפים או תזכורות לאירוע.

אירועי עדכון

אם האפליקציה מבקשת לאפשר למשתמש לערוך אירוע, מומלץ שמשתמשים ב-Intent EDIT, כמו שמתואר במאמר שימוש בכוונת רכישה כדי לערוך אירוע. עם זאת, אם צריך, אפשר לערוך אירועים ישירות. כדי לבצע עדכון של אירוע, ניתן לספק את _ID של את האירוע כמזהה מצורף ל-URI (withAppendedId()) או כפריט הבחירה הראשון. הבחירה צריכה להתחיל ב-"_id=?", והראשון הערך selectionArg צריך להיות _ID של האירוע. אפשר גם לבצע עדכונים באמצעות בחירה ללא מזהה. כאן יש דוגמה לעדכון אירוע. שם האירוע ישתנה באמצעות withAppendedId() גישה:

Kotlin

val DEBUG_TAG = "MyActivity"
...
val eventID: Long = 188
...
val values = ContentValues().apply {
    // The new title for the event
    put(CalendarContract.Events.TITLE, "Kickboxing")
}
val updateUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val rows: Int = contentResolver.update(updateUri, values, null, null)
Log.i(DEBUG_TAG, "Rows updated: $rows")

Java

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values.put(Events.TITLE, "Kickboxing");
updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = cr.update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

מחיקת אירועים

אפשר למחוק אירוע לפי _ID שלו כמזהה מצורף ב-URI, או באמצעות בחירה רגילה. אם משתמשים במזהה שצורף, אי אפשר גם לבחור אפשרות. יש שתי גרסאות של המחיקה: כאפליקציה וכמתאם סנכרון. מחיקה באמצעות אפליקציה מגדירה את העמודה נמחקה ל-1. הדגל הזה שמציין את מתאם הסנכרון שבו השורה נמחקה ושצריך למחוק את הפעולה הזו הופץ לשרת. מחיקה של מתאם סנכרון תסיר את האירוע את מסד הנתונים ואת כל הנתונים שמשויכים אליו. הנה דוגמה לאפליקציה מחיקת אירוע דרך _ID שלו:

Kotlin

val DEBUG_TAG = "MyActivity"
...
val eventID: Long = 201
...
val deleteUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val rows: Int = contentResolver.delete(deleteUri, null, null)
Log.i(DEBUG_TAG, "Rows deleted: $rows")

Java

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
Uri deleteUri = null;
deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = cr.delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);

טבלת המשתתפים

כל שורה בטבלה CalendarContract.Attendees מייצג משתתף יחיד או אורח באירוע. קריאה ל-query() מחזירה רשימה של המשתתפים באירוע עם EVENT_ID הנתון. הערך של EVENT_ID חייב להתאים ל-_ID של אירוע מסוים.

בטבלה הבאה מפורטים השדות שאפשר לכתוב בהם. כשמוסיפים משתתפים חדשים, צריך לכלול את כולם חוץ מ-ATTENDEE_NAME.

קבוע תיאור
EVENT_ID מזהה האירוע.
ATTENDEE_NAME השם של המשתתף.
ATTENDEE_EMAIL כתובת האימייל של המשתתף.
ATTENDEE_RELATIONSHIP

הקשר של המשתתף לאירוע. אחת מהאפשרויות:

ATTENDEE_TYPE

סוג המשתתף. אחת מהאפשרויות:

ATTENDEE_STATUS

סטטוס ההשתתפות של המשתתף. אחת מהאפשרויות:

הוספת משתתפים

בדוגמה הבאה אפשר להוסיף משתתף יחיד לאירוע. חשוב לזכור שהשדה EVENT_ID הוא שדה חובה:

Kotlin

val eventID: Long = 202
...
val values = ContentValues().apply {
    put(CalendarContract.Attendees.ATTENDEE_NAME, "Trevor")
    put(CalendarContract.Attendees.ATTENDEE_EMAIL, "trevor@example.com")
    put(
        CalendarContract.Attendees.ATTENDEE_RELATIONSHIP,
        CalendarContract.Attendees.RELATIONSHIP_ATTENDEE
    )
    put(CalendarContract.Attendees.ATTENDEE_TYPE, CalendarContract.Attendees.TYPE_OPTIONAL)
    put(
        CalendarContract.Attendees.ATTENDEE_STATUS,
        CalendarContract.Attendees.ATTENDEE_STATUS_INVITED
    )
    put(CalendarContract.Attendees.EVENT_ID, eventID)
}
val uri: Uri = contentResolver.insert(CalendarContract.Attendees.CONTENT_URI, values)

Java

long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);

טבלת תזכורות

כל שורה בטבלה CalendarContract.Reminders מייצגת תזכורת אחת לאירוע. קריאה ל-query() מחזירה רשימה של תזכורות לאירוע עם הערך של EVENT_ID.

בטבלה הבאה מפורטים השדות שאפשר לשנות בהתראות. צריך לכלול את כולם כשמוסיפים תזכורת חדשה. חשוב לזכור שהמתאמים לסנכרון מציינים את סוגי התזכורות שהם תומכים בהם בטבלה CalendarContract.Calendars. צפייה ALLOWED_REMINDERS אפשר לקבל פרטים נוספים.

קבוע תיאור
EVENT_ID מזהה האירוע.
MINUTES הדקות לפני האירוע שבו התזכורת צריכה לפעול.
METHOD

שיטת ההתראה, כפי שהוגדר בשרת. אחת מהאפשרויות:

הוספת תזכורות

הדוגמה הזו מוסיפה תזכורת לאירוע. התזכורת מופעלת 15 דקות לפני האירוע.

Kotlin

val eventID: Long = 221
...
val values = ContentValues().apply {
    put(CalendarContract.Reminders.MINUTES, 15)
    put(CalendarContract.Reminders.EVENT_ID, eventID)
    put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT)
}
val uri: Uri = contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, values)

Java

long eventID = 221;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Reminders.MINUTES, 15);
values.put(Reminders.EVENT_ID, eventID);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);

טבלת המכונות

הטבלה CalendarContract.Instances מכילה את הערך את שעת ההתחלה והסיום של האירוע. כל שורה בטבלה הזו מייצגת אירוע יחיד. לא ניתן לכתוב בטבלת המופעים מאפשרים להריץ שאילתות על אירועי אירועים.

בטבלה הבאה מפורטים כמה מהשדות שאפשר להריץ עליהם שאילתות למכונה מסוימת. הערה: אזור הזמן מוגדר לפי KEY_TIMEZONE_TYPE ו-KEY_TIMEZONE_INSTANCES.

קבוע תיאור
BEGIN שעת ההתחלה של המכונה, באלפיות השנייה לפי שעון UTC.
END שעת הסיום של המכונה, באלפיות השנייה לפי שעון UTC.
END_DAY יום הסיום היוליאני של המכונה, ביחס לזמן ביומן תחום.
END_MINUTE הדקה האחרונה של המופע, נמדדת מחצות באזור הזמן של היומן.
EVENT_ID ה-_ID של האירוע עבור המופע הזה.
START_DAY יום ההתחלה היוליאני של המכונה, ביחס לאזור הזמן של היומן.
START_MINUTE דקת ההתחלה של המכונה, נמדדת מחצות, ביחס לאזור הזמן של יומן Google.

שליחת שאילתה לטבלת המכונות

כדי להריץ שאילתה בטבלה Instances, צריך לציין טווח זמן לשאילתה ב-URI. בדוגמה הזו, ל-CalendarContract.Instances יש גישה לשדה TITLE דרך הטמעת הממשק CalendarContract.EventsColumns. במילים אחרות, הערך של TITLE מוחזר דרך תצוגה של מסד נתונים, ולא באמצעות שאילתה לטבלה הגולמית CalendarContract.Instances.

Kotlin

const val DEBUG_TAG: String = "MyActivity"
val INSTANCE_PROJECTION: Array<String> = arrayOf(
        CalendarContract.Instances.EVENT_ID, // 0
        CalendarContract.Instances.BEGIN, // 1
        CalendarContract.Instances.TITLE // 2
)

// The indices for the projection array above.
const val PROJECTION_ID_INDEX: Int = 0
const val PROJECTION_BEGIN_INDEX: Int = 1
const val PROJECTION_TITLE_INDEX: Int = 2

// Specify the date range you want to search for recurring
// event instances
val startMillis: Long = Calendar.getInstance().run {
    set(2011, 9, 23, 8, 0)
    timeInMillis
}
val endMillis: Long = Calendar.getInstance().run {
    set(2011, 10, 24, 8, 0)
    timeInMillis
}

// The ID of the recurring event whose instances you are searching
// for in the Instances table
val selection: String = "${CalendarContract.Instances.EVENT_ID} = ?"
val selectionArgs: Array<String> = arrayOf("207")

// Construct the query with the desired date range.
val builder: Uri.Builder = CalendarContract.Instances.CONTENT_URI.buildUpon()
ContentUris.appendId(builder, startMillis)
ContentUris.appendId(builder, endMillis)

// Submit the query
val cur: Cursor = contentResolver.query(
        builder.build(),
        INSTANCE_PROJECTION,
        selection,
        selectionArgs, null
)
while (cur.moveToNext()) {
    // Get the field values
    val eventID: Long = cur.getLong(PROJECTION_ID_INDEX)
    val beginVal: Long = cur.getLong(PROJECTION_BEGIN_INDEX)
    val title: String = cur.getString(PROJECTION_TITLE_INDEX)

    // Do something with the values.
    Log.i(DEBUG_TAG, "Event: $title")
    val calendar = Calendar.getInstance().apply {
        timeInMillis = beginVal
    }
    val formatter = SimpleDateFormat("MM/dd/yyyy")
    Log.i(DEBUG_TAG, "Date: ${formatter.format(calendar.time)}")
}

Java

private static final String DEBUG_TAG = "MyActivity";
public static final String[] INSTANCE_PROJECTION = new String[] {
    Instances.EVENT_ID,      // 0
    Instances.BEGIN,         // 1
    Instances.TITLE          // 2
  };

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...

// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();

Cursor cur = null;
ContentResolver cr = getContentResolver();

// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};

// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);

// Submit the query
cur =  cr.query(builder.build(),
    INSTANCE_PROJECTION,
    selection,
    selectionArgs,
    null);

while (cur.moveToNext()) {
    String title = null;
    long eventID = 0;
    long beginVal = 0;

    // Get the field values
    eventID = cur.getLong(PROJECTION_ID_INDEX);
    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
    title = cur.getString(PROJECTION_TITLE_INDEX);

    // Do something with the values.
    Log.i(DEBUG_TAG, "Event:  " + title);
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(beginVal);
    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
    }
 }

כוונות ביומן

לאפליקציה שלכם לא נדרשות הרשאות כדי לקרוא ולכתוב נתוני יומן. האפליקציה יכולה להשתמש באובייקטים של Intent שנתמכים על ידי אפליקציית יומן Google ב-Android כדי לבצע פעולות קריאה וכתיבה באפליקציה הזו. בטבלה הבאה מפורטים ה-Intents שנתמכים על ידי ספק היומן:

פעולה URI תיאור תוספות

VIEW

content://com.android.calendar/time/<ms_since_epoch>

אפשר גם להפנות ל-URI באמצעות CalendarContract.CONTENT_URI לדוגמה, לשימוש ב-Intent הזה, ראו שימוש בכוונות להצגת נתוני יומן.
פותחים את היומן לשעה שצוינה ב-<ms_since_epoch>. ללא.

VIEW

content://com.android.calendar/events/<event_id>

אפשר גם להפנות ל-URI באמצעות Events.CONTENT_URI לדוגמה, לשימוש ב-Intent הזה, ראו שימוש בכוונות להצגת נתוני יומן.
הצגת האירוע שצוין על ידי <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

content://com.android.calendar/events/<event_id>

אפשר גם להפנות ל-URI באמצעות Events.CONTENT_URI דוגמה לשימוש ב-Intent הזה מופיעה במאמר שימוש בכוונת רכישה כדי לערוך אירוע.
עורכים את האירוע שצוין ב-<event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

אפשר גם להפנות ל-URI באמצעות Events.CONTENT_URI. דוגמה לשימוש בכוונה הזו מופיעה במאמר שימוש בכוונה כדי להוסיף אירוע.
יוצרים אירוע. כל אחת מהתוספות המפורטות בטבלה שבהמשך.

בטבלה הבאה מפורטים הנתונים הנוספים של הכוונה שנתמכים על ידי ספק היומן:

Intent Extra תיאור
Events.TITLE שם לאירוע.
CalendarContract.EXTRA_EVENT_BEGIN_TIME שעת ההתחלה של האירוע באלפיות השנייה מתחילת התקופה של זמן המערכת.
CalendarContract.EXTRA_EVENT_END_TIME שעת הסיום של האירוע, במיליוניות השנייה מתחילת התקופה של זמן המערכת.
CalendarContract.EXTRA_EVENT_ALL_DAY ערך בוליאני שמציין שאירוע הוא כל היום. הערך יכול להיות true או false.
Events.EVENT_LOCATION המיקום של האירוע.
Events.DESCRIPTION תיאור האירוע.
Intent.EXTRA_EMAIL כתובות אימייל של האנשים שברצונך להזמין כרשימה מופרדת בפסיקים.
Events.RRULE כלל החזר האירוע.
Events.ACCESS_LEVEL האם האירוע הוא פרטי או ציבורי.
Events.AVAILABILITY אם האירוע נחשב לזמן תפוס או לזמן פנוי שאפשר לתזמן בו פגישה.

בסעיפים הבאים מוסבר איך להשתמש בכוונות האלה.

שימוש בכוונה (intent) להוספת אירוע

שימוש ב-Intent‏ INSERT מאפשר לאפליקציה להעביר את המשימה של הוספת האירוע ליומן Google עצמו. בשיטה הזו, האפליקציה שלך לא צריכה אפילו לכלול את ההרשאה WRITE_CALENDAR בקובץ המניפסט.

כשמשתמשים מריצים אפליקציה שמשתמשת בגישה הזו, האפליקציה שולחת אותם ליומן Google כדי לסיים את הוספת האירוע. ב-Intent INSERT משתמשים בשדות נוספים כדי לאכלס מראש בטופס את פרטי האירוע ביומן. לאחר מכן, המשתמשים יוכלו לבטל את האירוע, לערוך את הטופס לפי הצורך או לשמור את האירוע ביומן שלהם.

הנה קטע קוד שמתזמנ אירוע ב-19 בינואר 2012 מ-7:30 עד 8:30. חשוב לשים לב לפרטים הבאים לגבי קטע הקוד:

Kotlin

val startMillis: Long = Calendar.getInstance().run {
    set(2012, 0, 19, 7, 30)
    timeInMillis
}
val endMillis: Long = Calendar.getInstance().run {
    set(2012, 0, 19, 8, 30)
    timeInMillis
}
val intent = Intent(Intent.ACTION_INSERT)
        .setData(CalendarContract.Events.CONTENT_URI)
        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis)
        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis)
        .putExtra(CalendarContract.Events.TITLE, "Yoga")
        .putExtra(CalendarContract.Events.DESCRIPTION, "Group class")
        .putExtra(CalendarContract.Events.EVENT_LOCATION, "The gym")
        .putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY)
        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com")
startActivity(intent)

Java

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);

שימוש בכוונה לערוך אירוע

אפשר לעדכן אירוע ישירות, כפי שמתואר בקטע עדכון אירועים. עם זאת, שימוש ב-Intent‏ EDIT מאפשר לאפליקציה שאין לה הרשאה להעביר את עריכת האירועים לאפליקציית יומן Google. כשהמשתמשים מסיימים לערוך את האירוע ביומן, הם מוחזרים לאפליקציה המקורית.

הנה דוגמה ל-Intent שמגדיר כותרת לאירוע מסוים ומאפשרת למשתמשים לערוך את האירוע ביומן.

Kotlin

val eventID: Long = 208
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val intent = Intent(Intent.ACTION_EDIT)
        .setData(uri)
        .putExtra(CalendarContract.Events.TITLE, "My New Title")
startActivity(intent)

Java

long eventID = 208;
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
    .setData(uri)
    .putExtra(Events.TITLE, "My New Title");
startActivity(intent);

שימוש בכוונות כדי להציג נתונים מהיומן

לספק היומן יש שתי דרכים שונות להשתמש ב-Intent VIEW:

  • כדי לפתוח את יומן Google בתאריך מסוים.
  • כדי להציג אירוע.

דוגמה לאופן שבו פותחים את היומן לתאריך מסוים:

Kotlin

val startMillis: Long
...
val builder: Uri.Builder = CalendarContract.CONTENT_URI.buildUpon()
        .appendPath("time")
ContentUris.appendId(builder, startMillis)
val intent = Intent(Intent.ACTION_VIEW)
        .setData(builder.build())
startActivity(intent)

Java

// A date-time specified in milliseconds since the epoch.
long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
    .setData(builder.build());
startActivity(intent);

הדוגמה הבאה מראה איך לפתוח אירוע לצפייה:

Kotlin

val eventID: Long = 208
...
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
startActivity(intent)

Java

long eventID = 208;
...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
   .setData(uri);
startActivity(intent);

מתאמי סנכרון

יש הבדלים קלים בלבד באופן שבו אפליקציה ומתאמת סנכרון ניגשים לספק היומן:

  • מתאם סנכרון צריך לציין שהוא מתאם סנכרון על ידי הגדרה של CALLER_IS_SYNCADAPTER לערך true.
  • מתאם סנכרון צריך לספק ACCOUNT_NAME ו-ACCOUNT_TYPE כפרמטרים של שאילתות ב-URI.
  • למתאם סנכרון יש גישת כתיבה ליותר עמודות מאשר לאפליקציה או לווידג'ט. לדוגמה, אפליקציה יכולה לשנות רק כמה מאפיינים של יומן, כמו השם, שם התצוגה, הגדרת החשיפה והאפשרות לסנכרן את היומן. לשם השוואה, מתאם סנכרון יכול לגשת לא רק לעמודות האלה, אלא לעמודות רבות אחרות, כגון צבע היומן, אזור זמן, רמת גישה, מיקום וכן הלאה. עם זאת, מתאם סנכרון מוגבל ל-ACCOUNT_NAME ול-ACCOUNT_TYPE שצוינו בו.

הנה שיטה עוזרת שניתן להשתמש בה כדי להחזיר URI לשימוש עם מתאם סנכרון:

Kotlin

fun asSyncAdapter(uri: Uri, account: String, accountType: String): Uri {
    return uri.buildUpon()
            .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
            .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, account)
            .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, accountType).build()
}

Java

static Uri asSyncAdapter(Uri uri, String account, String accountType) {
    return uri.buildUpon()
        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
 }