1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.calendar; 18 19 import android.app.Activity; 20 import android.content.ContentResolver; 21 import android.content.ContentValues; 22 import android.content.Intent; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.os.Bundle; 26 import android.pim.ICalendar; 27 import android.provider.Calendar; 28 import android.util.Config; 29 import android.util.Log; 30 import android.view.View; 31 import android.widget.ArrayAdapter; 32 import android.widget.Button; 33 import android.widget.ImageView; 34 import android.widget.Spinner; 35 import android.widget.TextView; 36 37 import java.io.ByteArrayOutputStream; 38 import java.io.FileNotFoundException; 39 import java.io.IOException; 40 import java.io.InputStream; 41 import java.util.ArrayList; 42 43 public class IcsImportActivity extends Activity { 44 45 private static final String TAG = "Calendar"; 46 47 // TODO: consolidate this code with the EventActivity 48 private static class CalendarInfo { 49 public final long id; 50 public final String name; 51 CalendarInfo(long id, String name)52 public CalendarInfo(long id, String name) { 53 this.id = id; 54 this.name = name; 55 } 56 57 @Override toString()58 public String toString() { 59 return name; 60 } 61 } 62 63 private Button mImportButton; 64 private Button mCancelButton; 65 private Spinner mCalendars; 66 private TextView mNumEvents; 67 68 private ICalendar.Component mCalendar = null; 69 70 private View.OnClickListener mImportListener = new View.OnClickListener() { 71 public void onClick(View v) { 72 importCalendar(); 73 finish(); 74 } 75 }; 76 77 private View.OnClickListener mCancelListener = new View.OnClickListener() { 78 public void onClick(View v) { 79 finish(); 80 } 81 }; 82 83 @Override onCreate(Bundle icicle)84 protected void onCreate(Bundle icicle) { 85 super.onCreate(icicle); 86 setContentView(R.layout.ics_import_activity); 87 88 mCalendars = (Spinner) findViewById(R.id.calendars); 89 populateCalendars(); 90 91 mImportButton = (Button) findViewById(R.id.import_button); 92 mImportButton.setOnClickListener(mImportListener); 93 mCancelButton = (Button) findViewById(R.id.cancel_button); 94 mCancelButton.setOnClickListener(mCancelListener); 95 96 mNumEvents = (TextView) findViewById(R.id.num_events); 97 98 Intent intent = getIntent(); 99 String data = intent.getStringExtra("ics"); 100 if (data == null) { 101 Uri content = intent.getData(); 102 if (content != null) { 103 InputStream is = null; 104 try { 105 is = getContentResolver().openInputStream(content); 106 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 107 byte[] buf = new byte[8096]; 108 int bytesRead = -1; 109 int pos = 0; 110 while ((bytesRead = is.read(buf)) != -1) { 111 baos.write(buf, pos, bytesRead); 112 pos += bytesRead; 113 } 114 data = new String(baos.toByteArray(), "UTF-8"); 115 } catch (FileNotFoundException fnfe) { 116 Log.w(TAG, "Could not open data.", fnfe); 117 } catch (IOException ioe) { 118 Log.w(TAG, "Could not read data.", ioe); 119 } finally { 120 if (is != null) { 121 try { 122 is.close(); 123 } catch (IOException ioe) { 124 Log.w(TAG, "Could not close InputStream.", ioe); 125 } 126 } 127 } 128 } 129 } 130 if (data == null) { 131 Log.w(TAG, "No iCalendar data to parse."); 132 finish(); 133 return; 134 } 135 parseCalendar(data); 136 } 137 populateCalendars()138 private void populateCalendars() { 139 ContentResolver cr = getContentResolver(); 140 Cursor c = cr.query(Calendar.Calendars.CONTENT_URI, 141 new String[] { Calendar.Calendars._ID, 142 Calendar.Calendars.DISPLAY_NAME, 143 Calendar.Calendars.SELECTED, 144 Calendar.Calendars.ACCESS_LEVEL }, 145 Calendar.Calendars.SELECTED + "=1 AND " 146 + Calendar.Calendars.ACCESS_LEVEL + ">=" 147 + Calendar.Calendars.CONTRIBUTOR_ACCESS, 148 null, null /* sort order */); 149 150 ArrayList<CalendarInfo> items = new ArrayList<CalendarInfo>(); 151 try { 152 // TODO: write a custom adapter that wraps the cursor? 153 int idColumn = c.getColumnIndex(Calendar.Calendars._ID); 154 int nameColumn = c.getColumnIndex(Calendar.Calendars.DISPLAY_NAME); 155 while (c.moveToNext()) { 156 long id = c.getLong(idColumn); 157 String name = c.getString(nameColumn); 158 items.add(new CalendarInfo(id, name)); 159 } 160 } finally { 161 c.deactivate(); 162 } 163 164 mCalendars.setAdapter(new ArrayAdapter<CalendarInfo>(this, 165 android.R.layout.simple_spinner_item, items)); 166 } 167 parseCalendar(String data)168 private void parseCalendar(String data) { 169 mCalendar = null; 170 try { 171 mCalendar = ICalendar.parseCalendar(data); 172 } catch (ICalendar.FormatException fe) { 173 if (Config.LOGD) { 174 Log.d(TAG, "Could not parse iCalendar.", fe); 175 // TODO: show an error message. 176 finish(); 177 return; 178 } 179 } 180 if (mCalendar.getComponents() == null) { 181 Log.d(TAG, "No events in iCalendar."); 182 finish(); 183 return; 184 } 185 int numEvents = 0; 186 for (ICalendar.Component component : mCalendar.getComponents()) { 187 if ("VEVENT".equals(component.getName())) { 188 // TODO: display a list of the events (start time, title) in 189 // the UI? 190 ++numEvents; 191 } 192 } 193 // TODO: special-case a single-event calendar. switch to the 194 // EventActivity, once the EventActivity supports displaying data that 195 // is passed in via the extras. 196 // OR, we could flip things around, where the EventActivity handles ICS 197 // import by default, and delegates to the IcsImportActivity if it finds 198 // that there are more than one event in the iCalendar. that would 199 // avoid an extra activity launch for the expected common case of 200 // importing a single event. 201 mNumEvents.setText(Integer.toString(numEvents)); 202 } 203 importCalendar()204 private void importCalendar() { 205 206 ContentResolver cr = getContentResolver(); 207 208 int numImported = 0; 209 ContentValues values = new ContentValues(); 210 211 for (ICalendar.Component component : mCalendar.getComponents()) { 212 if ("VEVENT".equals(component.getName())) { 213 CalendarInfo calInfo = 214 (CalendarInfo) mCalendars.getSelectedItem(); 215 if (Calendar.Events.insertVEvent(cr, component, calInfo.id, 216 Calendar.Events.STATUS_CONFIRMED, values) != null) { 217 ++numImported; 218 } 219 } 220 } 221 // TODO: display how many were imported. 222 } 223 } 224