blob: 4a3c0cbd88ceaa465ca042166563b603f233e4de [file] [log] [blame]
/*
**
** 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();
}
}