summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDanesh M <daneshm90@gmail.com>2014-12-16 17:20:05 -0800
committerDanesh Mondegarian <daneshm90@gmail.com>2014-12-16 22:14:04 -0800
commitce5b4a44e51afdaf73c08f499f55bbb96985cc71 (patch)
tree7eccc5f35e58f6b2dbd1fa656d496c822e30cdab
parent19c77e769ce84908ac598ba9f3f2aba90d7a427b (diff)
downloadandroid_packages_apps_Calendar-ce5b4a44e51afdaf73c08f499f55bbb96985cc71.tar.gz
android_packages_apps_Calendar-ce5b4a44e51afdaf73c08f499f55bbb96985cc71.tar.bz2
android_packages_apps_Calendar-ce5b4a44e51afdaf73c08f499f55bbb96985cc71.zip
Calendar : Add ability to import/export from sdcard
Change-Id: Ia2ae56b50bff764786cdd37b760eb6b94a63743f
-rw-r--r--AndroidManifest.xml14
-rw-r--r--res/drawable-hdpi/ic_menu_export.pngbin0 -> 359 bytes
-rw-r--r--res/drawable-mdpi/ic_menu_export.pngbin0 -> 235 bytes
-rw-r--r--res/drawable-xhdpi/ic_menu_export.pngbin0 -> 511 bytes
-rw-r--r--res/drawable-xxhdpi/ic_menu_export.pngbin0 -> 739 bytes
-rw-r--r--res/menu/all_in_one_title_bar.xml7
-rw-r--r--res/menu/event_info_title_bar.xml9
-rw-r--r--res/values/cm_strings.xml8
-rw-r--r--src/com/android/calendar/AllInOneActivity.java5
-rw-r--r--src/com/android/calendar/EventInfoFragment.java99
-rw-r--r--src/com/android/calendar/ImportActivity.java186
-rw-r--r--src/com/android/calendar/icalendar/Attendee.java10
-rw-r--r--src/com/android/calendar/icalendar/IcalendarUtils.java124
-rw-r--r--src/com/android/calendar/icalendar/Organizer.java8
-rw-r--r--src/com/android/calendar/icalendar/VCalendar.java27
-rw-r--r--src/com/android/calendar/icalendar/VEvent.java44
16 files changed, 499 insertions, 42 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3e31e412..bb4bf3e3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -27,6 +27,7 @@
-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<uses-permission android:name="android.permission.INTERNET" />
@@ -156,6 +157,19 @@
<meta-data android:name="android.app.searchable" android:resource="@xml/searchable"/>
</activity>
+ <activity android:name="ImportActivity"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="content" />
+ <data android:scheme="file" />
+ <data android:mimeType="text/x-vcalendar" />
+ <data android:mimeType="text/calendar" />
+ </intent-filter>
+ </activity>
+
<activity android:name="DeleteEventsActivity"
android:parentActivityName="com.android.calendar.AllInOneActivity"
android:theme="@style/CalendarTheme.WithActionBar" >
diff --git a/res/drawable-hdpi/ic_menu_export.png b/res/drawable-hdpi/ic_menu_export.png
new file mode 100644
index 00000000..3fa4c80b
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_export.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_export.png b/res/drawable-mdpi/ic_menu_export.png
new file mode 100644
index 00000000..1aa12976
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_export.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_menu_export.png b/res/drawable-xhdpi/ic_menu_export.png
new file mode 100644
index 00000000..ccbaa1ba
--- /dev/null
+++ b/res/drawable-xhdpi/ic_menu_export.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_menu_export.png b/res/drawable-xxhdpi/ic_menu_export.png
new file mode 100644
index 00000000..8c60c7e0
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_menu_export.png
Binary files differ
diff --git a/res/menu/all_in_one_title_bar.xml b/res/menu/all_in_one_title_bar.xml
index 118eb5d1..94aebaae 100644
--- a/res/menu/all_in_one_title_bar.xml
+++ b/res/menu/all_in_one_title_bar.xml
@@ -63,4 +63,11 @@
android:icon="@drawable/ic_menu_settings_holo_light"
android:showAsAction="never"
android:orderInCategory="8" />
+ <item
+ android:id="@+id/action_import"
+ android:alphabeticShortcut="i"
+ android:title="@string/cal_import_menu_title"
+ android:icon="@drawable/ic_menu_export"
+ android:showAsAction="never"
+ android:orderInCategory="9" />
</menu>
diff --git a/res/menu/event_info_title_bar.xml b/res/menu/event_info_title_bar.xml
index 75623a44..c78beb31 100644
--- a/res/menu/event_info_title_bar.xml
+++ b/res/menu/event_info_title_bar.xml
@@ -36,10 +36,17 @@
android:enabled="false"
android:visible="false" />
<item android:id="@+id/info_action_delete"
- android:alphabeticShortcut="x"
+ android:alphabeticShortcut="d"
android:title="@string/delete_label"
android:icon="@drawable/ic_menu_trash_holo_light"
android:showAsAction="always"
android:enabled="false"
android:visible="false" />
+ <item android:id="@+id/info_action_export"
+ android:alphabeticShortcut="x"
+ android:title="@string/cal_export_event_sdcard_title"
+ android:icon="@drawable/ic_menu_export"
+ android:showAsAction="always"
+ android:enabled="true"
+ android:visible="true" />
</menu>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index 5652b3ef..65409309 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -50,4 +50,12 @@
<!-- Sharing calendar event -->
<string name="cal_share_intent_title">Send event to:</string>
+
+ <string name="cal_export_event_sdcard_title">Export to sdcard</string>
+ <string name="cal_export_succ_msg">Event exported successfully : %1s</string>
+ <string name="cal_import_menu_title">Import event</string>
+ <string name="cal_nothing_to_import">Nothing to import</string>
+ <string name="cal_import_error_msg">Importing to calendar failed</string>
+ <string name="cal_pick_ics">Pick file to import</string>
+
</resources>
diff --git a/src/com/android/calendar/AllInOneActivity.java b/src/com/android/calendar/AllInOneActivity.java
index d9e1a9ca..b43a1262 100644
--- a/src/com/android/calendar/AllInOneActivity.java
+++ b/src/com/android/calendar/AllInOneActivity.java
@@ -772,6 +772,9 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
getMenuInflater().inflate(extensionMenuRes, menu);
}
+ MenuItem item = menu.findItem(R.id.action_import);
+ item.setVisible(ImportActivity.hasThingsToImport(this));
+
mSearchMenu = menu.findItem(R.id.action_search);
mSearchView = (SearchView) mSearchMenu.getActionView();
if (mSearchView != null) {
@@ -885,6 +888,8 @@ public class AllInOneActivity extends AbstractCalendarActivity implements EventH
GoToDialogFragment goToFrg = GoToDialogFragment.newInstance(timeZone);
goToFrg.show(getFragmentManager(), "goto");
return true;
+ } else if (itemId == R.id.action_import) {
+ ImportActivity.pickImportFile(this);
} else {
return mExtensions.handleItemSelected(item, this);
}
diff --git a/src/com/android/calendar/EventInfoFragment.java b/src/com/android/calendar/EventInfoFragment.java
index 90f73b02..4374226e 100644
--- a/src/com/android/calendar/EventInfoFragment.java
+++ b/src/com/android/calendar/EventInfoFragment.java
@@ -48,6 +48,7 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Environment;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.Calendars;
@@ -185,6 +186,14 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
| TOKEN_QUERY_ATTENDEES | TOKEN_QUERY_CALENDARS | TOKEN_QUERY_EVENT
| TOKEN_QUERY_REMINDERS | TOKEN_QUERY_VISIBLE_CALENDARS | TOKEN_QUERY_COLORS;
+ public static final File EXPORT_SDCARD_DIRECTORY = new File(
+ Environment.getExternalStorageDirectory(), "CalendarEvents");
+
+ private enum ShareType {
+ SDCARD,
+ INTENT
+ }
+
private int mCurrentQuery = 0;
private static final String[] EVENT_PROJECTION = new String[] {
@@ -1258,7 +1267,9 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
} else if (itemId == R.id.info_action_change_color) {
showEventColorPickerDialog();
} else if (itemId == R.id.info_action_share_event) {
- shareEvent();
+ shareEvent(ShareType.INTENT);
+ } else if (itemId == R.id.info_action_export) {
+ shareEvent(ShareType.SDCARD);
}
return super.onOptionsItemSelected(item);
}
@@ -1267,7 +1278,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
* Generates an .ics formatted file with the event info and launches intent chooser to
* share said file
*/
- private void shareEvent() {
+ private void shareEvent(ShareType type) {
// Create the respective ICalendar objects from the event info
VCalendar calendar = new VCalendar();
calendar.addProperty(VCalendar.VERSION, "2.0");
@@ -1330,37 +1341,62 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
// prefix length constraint is imposed by File#createTempFile
filePrefix = "invite";
}
- File inviteFile = File.createTempFile(filePrefix, ".ics",
- mActivity.getExternalCacheDir());
+
+ filePrefix = filePrefix.replaceAll("\\W+", " ");
+
+ if (!filePrefix.endsWith(" ")) {
+ filePrefix += " ";
+ }
+
+ File dir;
+ if (type == ShareType.SDCARD) {
+ dir = EXPORT_SDCARD_DIRECTORY;
+ if (!dir.exists()) {
+ dir.mkdir();
+ }
+ } else {
+ dir = mActivity.getExternalCacheDir();
+ }
+
+ File inviteFile = IcalendarUtils.createTempFile(filePrefix, ".ics",
+ dir);
+
if (IcalendarUtils.writeCalendarToFile(calendar, inviteFile)) {
- inviteFile.setReadable(true,false); // set world-readable
- Intent shareIntent = new Intent();
- shareIntent.setAction(Intent.ACTION_SEND);
- shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(inviteFile));
- // the ics file is sent as an extra, the receiving application decides whether to
- // parse the file to extract calendar events or treat it as a regular file
- shareIntent.setType("application/octet-stream");
-
- Intent chooserIntent = Intent.createChooser(shareIntent,
- getResources().getString(R.string.cal_share_intent_title));
-
- // The MMS app only responds to "text/x-vcalendar" so we create a chooser intent
- // that includes the targeted mms intent + any that respond to the above general
- // purpose "application/octet-stream" intent.
- File vcsInviteFile = File.createTempFile(filePrefix, ".vcs",
- mActivity.getExternalCacheDir());
- // for now , we are duplicating ics file and using that as the vcs file
- // TODO: revisit above
- if (IcalendarUtils.copyFile(inviteFile, vcsInviteFile)) {
- Intent mmsShareIntent = new Intent();
- mmsShareIntent.setAction(Intent.ACTION_SEND);
- mmsShareIntent.setPackage("com.android.mms");
- mmsShareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(vcsInviteFile));
- mmsShareIntent.setType("text/x-vcalendar");
- chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
- new Intent[]{mmsShareIntent});
+ if (type == ShareType.INTENT) {
+ inviteFile.setReadable(true, false); // set world-readable
+ Intent shareIntent = new Intent();
+ shareIntent.setAction(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(inviteFile));
+ // the ics file is sent as an extra, the receiving application decides whether
+ // to parse the file to extract calendar events or treat it as a regular file
+ shareIntent.setType("application/octet-stream");
+
+ Intent chooserIntent = Intent.createChooser(shareIntent,
+ getResources().getString(R.string.cal_share_intent_title));
+
+ // The MMS app only responds to "text/x-vcalendar" so we create a chooser intent
+ // that includes the targeted mms intent + any that respond to the above general
+ // purpose "application/octet-stream" intent.
+ File vcsInviteFile = File.createTempFile(filePrefix, ".vcs",
+ mActivity.getExternalCacheDir());
+
+ // for now , we are duplicating ics file and using that as the vcs file
+ // TODO: revisit above
+ if (IcalendarUtils.copyFile(inviteFile, vcsInviteFile)) {
+ Intent mmsShareIntent = new Intent();
+ mmsShareIntent.setAction(Intent.ACTION_SEND);
+ mmsShareIntent.setPackage("com.android.mms");
+ mmsShareIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(vcsInviteFile));
+ mmsShareIntent.setType("text/x-vcalendar");
+ chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS,
+ new Intent[]{mmsShareIntent});
+ }
+ startActivity(chooserIntent);
+ } else {
+ String msg = getString(R.string.cal_export_succ_msg);
+ Toast.makeText(mActivity, String.format(msg, inviteFile),
+ Toast.LENGTH_SHORT).show();
}
- startActivity(chooserIntent);
isShareSuccessful = true;
} else {
@@ -1368,6 +1404,7 @@ public class EventInfoFragment extends DialogFragment implements OnCheckedChange
isShareSuccessful = false;
}
} catch (IOException e) {
+ e.printStackTrace();
isShareSuccessful = false;
}
diff --git a/src/com/android/calendar/ImportActivity.java b/src/com/android/calendar/ImportActivity.java
new file mode 100644
index 00000000..0a4a89a1
--- /dev/null
+++ b/src/com/android/calendar/ImportActivity.java
@@ -0,0 +1,186 @@
+package com.android.calendar;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.CalendarContract;
+import android.text.TextUtils;
+import android.widget.Toast;
+import com.android.calendar.icalendar.Attendee;
+import com.android.calendar.icalendar.IcalendarUtils;
+import com.android.calendar.icalendar.VCalendar;
+import com.android.calendar.icalendar.VEvent;
+
+import java.io.File;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.LinkedList;
+import java.util.TimeZone;
+
+public class ImportActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (!isValidIntent()) {
+ Toast.makeText(this, R.string.cal_nothing_to_import, Toast.LENGTH_SHORT).show();
+ finish();
+ } else {
+ parseCalFile();
+ }
+ }
+
+ private long getLocalTimeFromString(String iCalDate) {
+ SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
+ format.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+ try {
+ format.parse(iCalDate);
+ format.setTimeZone(TimeZone.getDefault());
+ return format.getCalendar().getTimeInMillis();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+
+ return System.currentTimeMillis();
+ }
+
+ private void showErrorToast() {
+ Toast.makeText(this, R.string.cal_import_error_msg, Toast.LENGTH_SHORT).show();
+ finish();
+ }
+
+ private void parseCalFile() {
+ Uri uri = getIntent().getData();
+ VCalendar calendar = IcalendarUtils.readCalendarFromFile(this, uri);
+
+ if (calendar == null) {
+ showErrorToast();
+ return;
+ }
+
+ Intent calIntent = new Intent(Intent.ACTION_INSERT);
+ calIntent.setType("vnd.android.cursor.item/event");
+
+ LinkedList<VEvent> events = calendar.getAllEvents();
+ if (events == null) {
+ showErrorToast();
+ return;
+ }
+
+ VEvent firstEvent = calendar.getAllEvents().getFirst();
+ calIntent.putExtra(CalendarContract.Events.TITLE,
+ IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.SUMMARY)));
+ calIntent.putExtra(CalendarContract.Events.EVENT_LOCATION,
+ IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.LOCATION)));
+ calIntent.putExtra(CalendarContract.Events.DESCRIPTION,
+ IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.DESCRIPTION)));
+ calIntent.putExtra(CalendarContract.Events.ORGANIZER,
+ IcalendarUtils.uncleanseString(firstEvent.getProperty(VEvent.ORGANIZER)));
+
+ if (firstEvent.mAttendees.size() > 0) {
+ StringBuilder builder = new StringBuilder();
+ for (Attendee attendee : firstEvent.mAttendees) {
+ builder.append(attendee.mEmail);
+ builder.append(",");
+ }
+ calIntent.putExtra(Intent.EXTRA_EMAIL, builder.toString());
+ }
+
+ String dtStart = firstEvent.getProperty(VEvent.DTSTART);
+ if (!TextUtils.isEmpty(dtStart)) {
+ calIntent.putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME,
+ getLocalTimeFromString(dtStart));
+ }
+
+ String dtEnd = firstEvent.getProperty(VEvent.DTEND);
+ if (!TextUtils.isEmpty(dtEnd)) {
+ calIntent.putExtra(CalendarContract.EXTRA_EVENT_END_TIME,
+ getLocalTimeFromString(dtEnd));
+ }
+
+ try {
+ startActivity(calIntent);
+ } catch (ActivityNotFoundException e) {
+ // Oh well...
+ } finally {
+ finish();
+ }
+ }
+
+ private boolean isValidIntent() {
+ Intent intent = getIntent();
+ if (intent == null) {
+ return false;
+ }
+ Uri fileUri = intent.getData();
+ if (fileUri == null) {
+ return false;
+ }
+ String scheme = fileUri.getScheme();
+ return ContentResolver.SCHEME_CONTENT.equals(scheme)
+ || ContentResolver.SCHEME_FILE.equals(scheme);
+ }
+
+ private static class ListFilesTask extends AsyncTask<Void, Void, String[]> {
+
+ private final Activity mActivity;
+
+ public ListFilesTask(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ protected String[] doInBackground(Void... params) {
+ if (!hasThingsToImport(mActivity)) {
+ return null;
+ }
+ File folder = EventInfoFragment.EXPORT_SDCARD_DIRECTORY;
+ String[] result = null;
+ if (folder.exists()) {
+ result = folder.list();
+ }
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(final String[] files) {
+ if (files == null || files.length == 0) {
+ Toast.makeText(mActivity, R.string.cal_nothing_to_import,
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
+ builder.setTitle(R.string.cal_pick_ics)
+ .setItems(files, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ Intent i = new Intent(mActivity, ImportActivity.class);
+ File f = new File(EventInfoFragment.EXPORT_SDCARD_DIRECTORY,
+ files[which]);
+ i.setData(Uri.fromFile(f));
+ mActivity.startActivity(i);
+ }
+ });
+ builder.show();
+ }
+
+ }
+
+ public static void pickImportFile(Activity activity) {
+ new ListFilesTask(activity).execute();
+ }
+
+ public static boolean hasThingsToImport(Context context) {
+ File folder = EventInfoFragment.EXPORT_SDCARD_DIRECTORY;
+ return folder.exists() && folder.list().length > 0;
+ }
+
+}
diff --git a/src/com/android/calendar/icalendar/Attendee.java b/src/com/android/calendar/icalendar/Attendee.java
index fc8c78bb..c3c2ba84 100644
--- a/src/com/android/calendar/icalendar/Attendee.java
+++ b/src/com/android/calendar/icalendar/Attendee.java
@@ -5,6 +5,7 @@
package com.android.calendar.icalendar;
import java.util.HashMap;
+import java.util.ListIterator;
/**
* Models the Attendee component of a calendar event
@@ -74,4 +75,13 @@ public class Attendee {
return output.toString();
}
+ public void populateFromEntries(ListIterator<String> iter) {
+ String line = iter.next();
+ if (line.contains("ATTENDEE")) {
+ String entry = VEvent.parseTillNextAttribute(iter, line);
+ String[] entries = entry.split("X-NUM-GUESTS=0:mailto:");
+ mEmail = entries[1];
+ }
+ }
+
}
diff --git a/src/com/android/calendar/icalendar/IcalendarUtils.java b/src/com/android/calendar/icalendar/IcalendarUtils.java
index e179926b..efbb4583 100644
--- a/src/com/android/calendar/icalendar/IcalendarUtils.java
+++ b/src/com/android/calendar/icalendar/IcalendarUtils.java
@@ -4,20 +4,17 @@
package com.android.calendar.icalendar;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.Uri;
import android.provider.CalendarContract;
-import android.util.Log;
import com.android.calendar.CalendarEventModel;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
import java.text.SimpleDateFormat;
+import java.util.ArrayList;
import java.util.Calendar;
-import java.util.Date;
+import java.util.Random;
import java.util.TimeZone;
/**
@@ -25,7 +22,21 @@ import java.util.TimeZone;
*/
public class IcalendarUtils {
- private static int sPermittedLineLength = 75; // Line length mandated by iCalendar format
+ public static int sPermittedLineLength = 75; // Line length mandated by iCalendar format
+ private static final Random tempFileRandom = new Random();
+
+ public static String uncleanseString(CharSequence sequence) {
+ if (sequence == null) return null;
+ String input = sequence.toString();
+
+ // reintroduce new lines with the literal '\n'
+ input = input.replaceAll("\\\\n", "\n");
+ // reintroduce semicolons and commas
+ input = input.replaceAll("\\\\;", ";");
+ input = input.replaceAll("\\\\\\,", ",");
+
+ return input;
+ }
/**
* ensure the string conforms to the iCalendar encoding requirements
@@ -47,6 +58,99 @@ public class IcalendarUtils {
}
/**
+ * Creates an empty temporary file in the given directory using the given
+ * prefix and suffix as part of the file name. If {@code suffix} is null, {@code .tmp} is used.
+ *
+ * <p>Note that this method does <i>not</i> call {@link #deleteOnExit}, but see the
+ * documentation for that method before you call it manually.
+ *
+ * @param prefix
+ * the prefix to the temp file name.
+ * @param suffix
+ * the suffix to the temp file name.
+ * @param directory
+ * the location to which the temp file is to be written, or
+ * {@code null} for the default location for temporary files,
+ * which is taken from the "java.io.tmpdir" system property. It
+ * may be necessary to set this property to an existing, writable
+ * directory for this method to work properly.
+ * @return the temporary file.
+ * @throws IllegalArgumentException
+ * if the length of {@code prefix} is less than 3.
+ * @throws IOException
+ * if an error occurs when writing the file.
+ */
+ public static File createTempFile(String prefix, String suffix, File directory)
+ throws IOException {
+ // Force a prefix null check first
+ if (prefix.length() < 3) {
+ throw new IllegalArgumentException("prefix must be at least 3 characters");
+ }
+ if (suffix == null) {
+ suffix = ".tmp";
+ }
+ File tmpDirFile = directory;
+ if (tmpDirFile == null) {
+ String tmpDir = System.getProperty("java.io.tmpdir", ".");
+ tmpDirFile = new File(tmpDir);
+ }
+ File result;
+ do {
+ result = new File(tmpDirFile,
+ prefix + tempFileRandom.nextInt(Integer.MAX_VALUE) + suffix);
+ } while (!result.createNewFile());
+ return result;
+ }
+
+ public static VCalendar readCalendarFromFile(Context context, Uri uri) {
+ ArrayList<String> contents = getStringArrayFromFile(context, uri);
+ if (contents == null || contents.isEmpty()) {
+ return null;
+ }
+ VCalendar calendar = new VCalendar();
+ calendar.populateFromString(contents);
+ return calendar;
+ }
+
+ public static ArrayList<String> getStringArrayFromFile(Context context, Uri uri) {
+ String scheme = uri.getScheme();
+ InputStream inputStream = null;
+ if(ContentResolver.SCHEME_CONTENT.equals(scheme)) {
+ try {
+ inputStream = context.getContentResolver().openInputStream(uri);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ } else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
+ File f = new File(uri.getPath());
+ try {
+ inputStream = new FileInputStream(f);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ if (inputStream == null) {
+ return null;
+ }
+
+ ArrayList<String> result = new ArrayList<String>();
+
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ result.add(line);
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ /**
* Stringify VCalendar object and write to file
* @param calendar
* @param file
diff --git a/src/com/android/calendar/icalendar/Organizer.java b/src/com/android/calendar/icalendar/Organizer.java
index 2e039849..9f9780d5 100644
--- a/src/com/android/calendar/icalendar/Organizer.java
+++ b/src/com/android/calendar/icalendar/Organizer.java
@@ -39,4 +39,12 @@ public class Organizer {
return output.toString();
}
+ public static Organizer populateFromICalString(String iCalFormattedString) {
+ // TODO add santiy checks
+ String[] organizer = iCalFormattedString.split(";");
+ String[] entries = organizer[1].split(":");
+ String name = entries[0].replace("CN=", "");
+ String email = entries[1].replace("mailto=", "");
+ return new Organizer(name, email);
+ }
}
diff --git a/src/com/android/calendar/icalendar/VCalendar.java b/src/com/android/calendar/icalendar/VCalendar.java
index 00c7b81b..9f422e6f 100644
--- a/src/com/android/calendar/icalendar/VCalendar.java
+++ b/src/com/android/calendar/icalendar/VCalendar.java
@@ -4,8 +4,12 @@
package com.android.calendar.icalendar;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
/**
* Models the Calendar/VCalendar component of the iCalendar format
@@ -102,6 +106,29 @@ public class VCalendar {
return output.toString();
}
+ public void populateFromString(ArrayList<String> input) {
+ ListIterator<String> iter = input.listIterator();
+
+ while (iter.hasNext()) {
+ String line = iter.next();
+ if (line.contains("BEGIN:VEVENT")) {
+ // Go one previous, so VEvent, parses current line
+ iter.previous();
+
+ // Offload to vevent for parsing
+ VEvent event = new VEvent();
+ event.populateFromEntries(iter);
+ mEvents.add(event);
+ } else if (line.contains("END:VCALENDAR")) {
+ break;
+ }
+ }
+ }
+
+ public String getProperty(String key) {
+ return mProperties.get(key);
+ }
+
/**
* TODO: Aggressive validation of VCalendar and all of its components to ensure they conform
* to the ical specification
diff --git a/src/com/android/calendar/icalendar/VEvent.java b/src/com/android/calendar/icalendar/VEvent.java
index 1aff0bd4..672aa18b 100644
--- a/src/com/android/calendar/icalendar/VEvent.java
+++ b/src/com/android/calendar/icalendar/VEvent.java
@@ -6,6 +6,7 @@ package com.android.calendar.icalendar;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.ListIterator;
import java.util.UUID;
/**
@@ -173,4 +174,47 @@ public class VEvent {
return sb.toString();
}
+ public void populateFromEntries(ListIterator<String> iter) {
+ while (iter.hasNext()) {
+ String line = iter.next();
+ if (line.contains("BEGIN:VEVENT")) {
+ // Continue
+ } else if (line.startsWith("END:EVENT")) {
+ break;
+ } else if (line.startsWith("ORGANIZER")) {
+ String entry = parseTillNextAttribute(iter, line);
+ mOrganizer = Organizer.populateFromICalString(entry);
+ } else if (line.startsWith("ATTENDEE")) {
+ // Go one previous, so VEvent, parses current line
+ iter.previous();
+
+ // Offload to Attendee for parsing
+ Attendee attendee = new Attendee();
+ attendee.populateFromEntries(iter);
+ mAttendees.add(attendee);
+ } else if (line.contains(":")) {
+ String entry = parseTillNextAttribute(iter, line);
+ int indexOfFirstColon = entry.indexOf(":");
+ String key = entry.substring(0, indexOfFirstColon);
+ String value = entry.substring(indexOfFirstColon + 1);
+ mProperties.put(key, value);
+ }
+ }
+ }
+
+ public static String parseTillNextAttribute(ListIterator<String> iter, String currentLine) {
+ StringBuilder parse = new StringBuilder();
+ parse.append(currentLine);
+ while (iter.hasNext()) {
+ String line = iter.next();
+ if (line.startsWith(" ")) {
+ parse.append(line.replaceFirst(" ", ""));
+ } else {
+ iter.previous();
+ break;
+ }
+ }
+ return parse.toString();
+ }
+
}