Code drop from //branches/cupcake/...@124589
[android/platform/packages/apps/Calendar.git] / src / com / android / calendar / SelectCalendarsActivity.java
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.app.AlertDialog;
21 import android.content.AsyncQueryHandler;
22 import android.content.ContentResolver;
23 import android.content.ContentUris;
24 import android.content.ContentValues;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.database.Cursor;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.provider.Calendar.Calendars;
31 import android.util.Log;
32 import android.view.Menu;
33 import android.view.MenuItem;
34 import android.view.View;
35 import android.view.Window;
36 import android.view.MenuItem.OnMenuItemClickListener;
37 import android.widget.AdapterView;
38 import android.widget.CheckBox;
39 import android.widget.ListView;
40
41
42 public class SelectCalendarsActivity extends Activity implements ListView.OnItemClickListener {
43
44     private static final String TAG = "Calendar";
45     private View mView = null;
46     private Cursor mCursor = null;
47     private QueryHandler mQueryHandler;
48     private SelectCalendarsAdapter mAdapter;
49     private static final String[] PROJECTION = new String[] {
50         Calendars._ID,
51         Calendars.DISPLAY_NAME,
52         Calendars.COLOR,
53         Calendars.SELECTED,
54         Calendars.SYNC_EVENTS
55     };
56
57     @Override
58     protected void onCreate(Bundle icicle) {
59         super.onCreate(icicle);
60         requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
61         setContentView(R.layout.calendars_activity);
62         getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
63                 Window.PROGRESS_INDETERMINATE_ON);
64         mQueryHandler = new QueryHandler(getContentResolver());
65         mView = findViewById(R.id.calendars);
66         ListView items = (ListView) mView.findViewById(R.id.items);
67         Context context = mView.getContext();
68         mCursor = managedQuery(Calendars.CONTENT_URI, PROJECTION,
69                 Calendars.SYNC_EVENTS + "=1",
70                 null /* selectionArgs */,
71                 Calendars.DEFAULT_SORT_ORDER);
72                                      
73         mAdapter = new SelectCalendarsAdapter(context, mCursor);
74         items.setAdapter(mAdapter);
75         items.setOnItemClickListener(this);
76         
77         // Start a background sync to get the list of calendars from the server.
78         startCalendarSync();
79     }
80     
81     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
82         CheckBox box = (CheckBox) view.findViewById(R.id.checkbox);
83         box.toggle();
84     }
85     
86     @Override
87     public boolean onCreateOptionsMenu(Menu menu) {
88         super.onCreateOptionsMenu(menu);
89         MenuItem item;
90         item = menu.add(0, 0, 0, R.string.add_calendars)
91                 .setOnMenuItemClickListener(new ChangeCalendarAction(false /* not remove */));
92         item.setIcon(android.R.drawable.ic_menu_add);
93         
94         item = menu.add(0, 0, 0, R.string.remove_calendars)
95                 .setOnMenuItemClickListener(new ChangeCalendarAction(true /* remove */));
96         item.setIcon(android.R.drawable.ic_menu_delete);
97         return true;
98     }
99
100     /**
101      * ChangeCalendarAction is used both for adding and removing calendars.
102      * The constructor takes a boolean argument that is false if adding
103      * calendars and true if removing calendars.  The user selects calendars
104      * to be added or removed from a pop-up list. 
105      */
106     public class ChangeCalendarAction implements OnMenuItemClickListener,
107             DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener {
108         
109         int mNumItems;
110         long[] mCalendarIds;
111         boolean[] mIsChecked;
112         ContentResolver mContentResolver;
113         boolean mRemove;
114         
115         public ChangeCalendarAction(boolean remove) {
116             mContentResolver = SelectCalendarsActivity.this.getContentResolver();
117             mRemove = remove;
118         }
119
120         /*
121          * This is called when the user selects a calendar from either the
122          * "Add calendars" or "Remove calendars" popup dialog. 
123          */
124         public void onClick(DialogInterface dialog, int position, boolean isChecked) {
125             mIsChecked[position] = isChecked;
126         }
127
128         /*
129          * This is called when the user presses the OK or Cancel button on the
130          * "Add calendars" or "Remove calendars" popup dialog. 
131          */
132         public void onClick(DialogInterface dialog, int which) {
133             // If the user cancelled the dialog, then do nothing.
134             if (which == DialogInterface.BUTTON2) {
135                 return;
136             }
137             
138             boolean changesFound = false;
139             for (int position = 0; position < mNumItems; position++) {
140                 // If this calendar wasn't selected, then skip it.
141                 if (!mIsChecked[position]) {
142                     continue;
143                 }
144                 changesFound = true;
145                 
146                 long id = mCalendarIds[position];
147                 Uri uri = ContentUris.withAppendedId(Calendars.CONTENT_URI, id);
148                 ContentValues values = new ContentValues();
149                 int selected = 1;
150                 if (mRemove) {
151                     selected = 0;
152                 }
153                 values.put(Calendars.SELECTED, selected);
154                 values.put(Calendars.SYNC_EVENTS, selected);
155                 mContentResolver.update(uri, values, null, null);
156             }
157             
158             // If there were any changes, then update the list of calendars
159             // that are synced.
160             if (changesFound) {
161                 mCursor.requery();
162             }
163         }
164         
165         public boolean onMenuItemClick(MenuItem item) {
166             AlertDialog.Builder builder = new AlertDialog.Builder(SelectCalendarsActivity.this);
167             String selection;
168             if (mRemove) {
169                 builder.setTitle(R.string.remove_calendars)
170                     .setIcon(android.R.drawable.ic_dialog_alert);
171                 selection = Calendars.SYNC_EVENTS + "=1";
172             } else {
173                 builder.setTitle(R.string.add_calendars);
174                 selection = Calendars.SYNC_EVENTS + "=0";
175             }
176             ContentResolver cr = getContentResolver();
177             Cursor cursor = cr.query(Calendars.CONTENT_URI, PROJECTION,
178                     selection, null /* selectionArgs */,
179                     Calendars.DEFAULT_SORT_ORDER);
180             if (cursor == null) {
181                 Log.w(TAG, "Cannot get cursor for calendars");
182                 return true;
183             }
184
185             int count = cursor.getCount();
186             mNumItems = count;
187             CharSequence[] calendarNames = new CharSequence[count];
188             mCalendarIds = new long[count];
189             mIsChecked = new boolean[count];
190             try {
191                 int pos = 0;
192                 while (cursor.moveToNext()) {
193                     mCalendarIds[pos] = cursor.getLong(0);
194                     calendarNames[pos] = cursor.getString(1);
195                     pos += 1;
196                 }
197             } finally {
198                 cursor.close();
199             }
200             
201             builder.setMultiChoiceItems(calendarNames, null, this)
202                 .setPositiveButton(android.R.string.ok, this)
203                 .setNegativeButton(android.R.string.cancel, this)
204                 .show();
205             return true;
206         }
207     }
208     
209     private class QueryHandler extends AsyncQueryHandler {
210         public QueryHandler(ContentResolver cr) {
211             super(cr);
212         }
213
214         @Override
215         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
216             getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
217                     Window.PROGRESS_VISIBILITY_OFF);
218
219             // If the Activity is finishing, then close the cursor.
220             // Otherwise, use the new cursor in the adapter.
221             if (isFinishing()) {
222                 stopManagingCursor(cursor);
223                 cursor.close();
224             } else {
225                 if (cursor.getCount() == 0) {
226                     // There are no calendars.  This might happen if we lost
227                     // the wireless connection (in airplane mode, for example).
228                     // Leave the current list of calendars alone and pop up
229                     // a dialog explaining that the connection is down.
230                     // But allow the user to add and remove calendars.
231                     return;
232                 }
233                 if (mCursor != null) {
234                     stopManagingCursor(mCursor);
235                 }
236                 mCursor = cursor;
237                 startManagingCursor(cursor);
238                 mAdapter.changeCursor(cursor);
239             }
240         }
241     }
242
243     // This class implements the menu option "Refresh list from server".
244     // (No longer used.)
245     public class RefreshAction implements Runnable {
246         public void run() {
247             startCalendarSync();
248         }
249     }
250     
251     // startCalendarSync() checks the server for an updated list of Calendars
252     // (in the background) using an AsyncQueryHandler.
253     //
254     // Calendars are never removed from the phone due to a server sync.
255     // But if a Calendar is added on the web (and it is selected and not
256     // hidden) then it will be added to the list of calendars on the phone
257     // (when this asynchronous query finishes).  When a new calendar from the
258     // web is added to the phone, then the events for that calendar are also
259     // downloaded from the web.
260     // 
261     // This sync is done automatically in the background when the
262     // SelectCalendars activity is started.
263     private void startCalendarSync() {
264         getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
265                 Window.PROGRESS_VISIBILITY_ON);
266
267         // TODO: make sure the user has login info.
268         
269         Uri uri = Calendars.LIVE_CONTENT_URI;
270         mQueryHandler.startQuery(0, null, uri, PROJECTION,
271                 Calendars.SYNC_EVENTS + "=1",
272                 null, Calendars.DEFAULT_SORT_ORDER);
273     }
274 }