| /* |
| ** |
| ** Copyright 2009, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** See the License for the specific language governing permissions and |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** limitations under the License. |
| */ |
| |
| package com.android.calendar; |
| |
| import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; |
| import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_ACCEPTED; |
| import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_DECLINED; |
| import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_NONE; |
| import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS_TENTATIVE; |
| import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; |
| import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; |
| |
| import android.app.Activity; |
| import android.content.ActivityNotFoundException; |
| import android.content.ContentUris; |
| import android.content.Intent; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.CalendarContract.Calendars; |
| import android.provider.CalendarContract.Events; |
| import android.text.TextUtils; |
| import android.util.Base64; |
| import android.util.Log; |
| |
| import com.android.calendarcommon2.DateException; |
| import com.android.calendarcommon2.Duration; |
| |
| public class GoogleCalendarUriIntentFilter extends Activity { |
| private static final String TAG = "GoogleCalendarUriIntentFilter"; |
| static final boolean debug = false; |
| |
| private static final int EVENT_INDEX_ID = 0; |
| private static final int EVENT_INDEX_START = 1; |
| private static final int EVENT_INDEX_END = 2; |
| private static final int EVENT_INDEX_DURATION = 3; |
| |
| private static final String[] EVENT_PROJECTION = new String[] { |
| Events._ID, // 0 |
| Events.DTSTART, // 1 |
| Events.DTEND, // 2 |
| Events.DURATION, // 3 |
| }; |
| |
| /** |
| * Extracts the ID and calendar email from the eid parameter of a URI. |
| * |
| * The URI contains an "eid" parameter, which is comprised of an ID, followed |
| * by a space, followed by the calendar email address. The domain is sometimes |
| * shortened. See the switch statement. This is Base64-encoded before being |
| * added to the URI. |
| * |
| * @param uri incoming request |
| * @return the decoded event ID and calendar email |
| */ |
| private String[] extractEidAndEmail(Uri uri) { |
| try { |
| String eidParam = uri.getQueryParameter("eid"); |
| if (debug) Log.d(TAG, "eid=" + eidParam ); |
| if (eidParam == null) { |
| return null; |
| } |
| |
| byte[] decodedBytes = Base64.decode(eidParam, Base64.DEFAULT); |
| if (debug) Log.d(TAG, "decoded eid=" + new String(decodedBytes) ); |
| |
| for (int spacePosn = 0; spacePosn < decodedBytes.length; spacePosn++) { |
| if (decodedBytes[spacePosn] == ' ') { |
| int emailLen = decodedBytes.length - spacePosn - 1; |
| if (spacePosn == 0 || emailLen < 3) { |
| break; |
| } |
| |
| String domain = null; |
| if (decodedBytes[decodedBytes.length - 2] == '@') { |
| // Drop the special one character domain |
| emailLen--; |
| |
| switch(decodedBytes[decodedBytes.length - 1]) { |
| case 'm': |
| domain = "gmail.com"; |
| break; |
| case 'g': |
| domain = "group.calendar.google.com"; |
| break; |
| case 'h': |
| domain = "holiday.calendar.google.com"; |
| break; |
| case 'i': |
| domain = "import.calendar.google.com"; |
| break; |
| case 'v': |
| domain = "group.v.calendar.google.com"; |
| break; |
| default: |
| Log.wtf(TAG, "Unexpected one letter domain: " |
| + decodedBytes[decodedBytes.length - 1]); |
| // Add sql wild card char to handle new cases |
| // that we don't know about. |
| domain = "%"; |
| break; |
| } |
| } |
| |
| String eid = new String(decodedBytes, 0, spacePosn); |
| String email = new String(decodedBytes, spacePosn + 1, emailLen); |
| if (debug) Log.d(TAG, "eid= " + eid ); |
| if (debug) Log.d(TAG, "email= " + email ); |
| if (debug) Log.d(TAG, "domain=" + domain ); |
| if (domain != null) { |
| email += domain; |
| } |
| |
| return new String[] { eid, email }; |
| } |
| } |
| } catch (RuntimeException e) { |
| Log.w(TAG, "Punting malformed URI " + uri); |
| } |
| return null; |
| } |
| |
| @Override |
| protected void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| |
| Intent intent = getIntent(); |
| if (intent != null) { |
| Uri uri = intent.getData(); |
| if (uri != null) { |
| String[] eidParts = extractEidAndEmail(uri); |
| if (debug) Log.d(TAG, "eidParts=" + eidParts ); |
| |
| if (eidParts != null) { |
| final String selection = Events._SYNC_ID + " LIKE \"%" + eidParts[0] |
| + "\" AND " + Calendars.OWNER_ACCOUNT + " LIKE \"" + eidParts[1] + "\""; |
| |
| if (debug) Log.d(TAG, "selection: " + selection); |
| Cursor eventCursor = getContentResolver().query(Events.CONTENT_URI, |
| EVENT_PROJECTION, selection, null, |
| Calendars.CALENDAR_ACCESS_LEVEL + " desc"); |
| if (debug) Log.d(TAG, "Found: " + eventCursor.getCount()); |
| |
| if (eventCursor != null && eventCursor.getCount() > 0) { |
| if (eventCursor.getCount() > 1) { |
| Log.i(TAG, "NOTE: found " + eventCursor.getCount() |
| + " matches on event with id='" + eidParts[0] + "'"); |
| // Don't print eidPart[1] as it contains the user's PII |
| } |
| |
| // Get info from Cursor |
| while (eventCursor.moveToNext()) { |
| int eventId = eventCursor.getInt(EVENT_INDEX_ID); |
| long startMillis = eventCursor.getLong(EVENT_INDEX_START); |
| long endMillis = eventCursor.getLong(EVENT_INDEX_END); |
| if (debug) Log.d(TAG, "_id: " + eventCursor.getLong(EVENT_INDEX_ID)); |
| if (debug) Log.d(TAG, "startMillis: " + startMillis); |
| if (debug) Log.d(TAG, "endMillis: " + endMillis); |
| |
| if (endMillis == 0) { |
| String duration = eventCursor.getString(EVENT_INDEX_DURATION); |
| if (debug) Log.d(TAG, "duration: " + duration); |
| if (TextUtils.isEmpty(duration)) { |
| continue; |
| } |
| |
| try { |
| Duration d = new Duration(); |
| d.parse(duration); |
| endMillis = startMillis + d.getMillis(); |
| if (debug) Log.d(TAG, "startMillis! " + startMillis); |
| if (debug) Log.d(TAG, "endMillis! " + endMillis); |
| if (endMillis < startMillis) { |
| continue; |
| } |
| } catch (DateException e) { |
| if (debug) Log.d(TAG, "duration:" + e.toString()); |
| continue; |
| } |
| } |
| |
| eventCursor.close(); |
| eventCursor = null; |
| |
| // Pick up attendee status action from uri clicked |
| int attendeeStatus = ATTENDEE_STATUS_NONE; |
| if ("RESPOND".equals(uri.getQueryParameter("action"))) { |
| try { |
| switch (Integer.parseInt(uri.getQueryParameter("rst"))) { |
| case 1: // Yes |
| attendeeStatus = ATTENDEE_STATUS_ACCEPTED; |
| break; |
| case 2: // No |
| attendeeStatus = ATTENDEE_STATUS_DECLINED; |
| break; |
| case 3: // Maybe |
| attendeeStatus = ATTENDEE_STATUS_TENTATIVE; |
| break; |
| } |
| } catch (NumberFormatException e) { |
| // ignore this error as if the response code |
| // wasn't in the uri. |
| } |
| } |
| |
| // Send intent to calendar app |
| Uri calendarUri = ContentUris.withAppendedId(Events.CONTENT_URI, |
| eventId); |
| intent = new Intent(Intent.ACTION_VIEW, calendarUri); |
| intent.setClass(this, EventInfoActivity.class); |
| intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis); |
| intent.putExtra(EXTRA_EVENT_END_TIME, endMillis); |
| if (attendeeStatus != ATTENDEE_STATUS_NONE) { |
| intent.putExtra(ATTENDEE_STATUS, attendeeStatus); |
| } |
| startActivity(intent); |
| finish(); |
| return; |
| } |
| } |
| if (eventCursor != null) eventCursor.close(); |
| } |
| } |
| |
| // Can't handle the intent. Pass it on to the next Activity. |
| try { |
| startNextMatchingActivity(intent); |
| } catch (ActivityNotFoundException ex) { |
| // no browser installed? Just drop it. |
| } |
| } |
| finish(); |
| } |
| } |