| /* |
| * Copyright (C) 2007 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, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.calendar; |
| |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.content.AsyncQueryHandler; |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.Calendar.Calendars; |
| import android.util.Log; |
| import android.view.Menu; |
| import android.view.MenuItem; |
| import android.view.View; |
| import android.view.Window; |
| import android.view.MenuItem.OnMenuItemClickListener; |
| import android.widget.AdapterView; |
| import android.widget.Button; |
| import android.widget.CheckBox; |
| import android.widget.ListView; |
| |
| |
| public class SelectCalendarsActivity extends Activity implements ListView.OnItemClickListener { |
| |
| private static final String TAG = "Calendar"; |
| private View mView = null; |
| private Cursor mCursor = null; |
| private QueryHandler mQueryHandler; |
| private SelectCalendarsAdapter mAdapter; |
| private static final String[] PROJECTION = new String[] { |
| Calendars._ID, |
| Calendars.DISPLAY_NAME, |
| Calendars.COLOR, |
| Calendars.SELECTED, |
| Calendars.SYNC_EVENTS |
| }; |
| |
| @Override |
| protected void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); |
| setContentView(R.layout.calendars_activity); |
| getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, |
| Window.PROGRESS_INDETERMINATE_ON); |
| mQueryHandler = new QueryHandler(getContentResolver()); |
| mView = findViewById(R.id.calendars); |
| ListView items = (ListView) mView.findViewById(R.id.items); |
| Context context = mView.getContext(); |
| mCursor = managedQuery(Calendars.CONTENT_URI, PROJECTION, |
| Calendars.SYNC_EVENTS + "=1", |
| null /* selectionArgs */, |
| Calendars.DEFAULT_SORT_ORDER); |
| |
| mAdapter = new SelectCalendarsAdapter(context, mCursor); |
| items.setAdapter(mAdapter); |
| items.setOnItemClickListener(this); |
| |
| // Start a background sync to get the list of calendars from the server. |
| startCalendarSync(); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| } |
| |
| public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
| CheckBox box = (CheckBox) view.findViewById(R.id.checkbox); |
| box.toggle(); |
| } |
| |
| @Override |
| public boolean onCreateOptionsMenu(Menu menu) { |
| super.onCreateOptionsMenu(menu); |
| MenuItem item; |
| item = menu.add(0, 0, 0, R.string.add_calendars) |
| .setOnMenuItemClickListener(new ChangeCalendarAction(false /* not remove */)); |
| item.setIcon(android.R.drawable.ic_menu_add); |
| |
| item = menu.add(0, 0, 0, R.string.remove_calendars) |
| .setOnMenuItemClickListener(new ChangeCalendarAction(true /* remove */)); |
| item.setIcon(android.R.drawable.ic_menu_delete); |
| return true; |
| } |
| |
| /** |
| * ChangeCalendarAction is used both for adding and removing calendars. |
| * The constructor takes a boolean argument that is false if adding |
| * calendars and true if removing calendars. The user selects calendars |
| * to be added or removed from a pop-up list. |
| */ |
| public class ChangeCalendarAction implements OnMenuItemClickListener, |
| DialogInterface.OnClickListener, DialogInterface.OnMultiChoiceClickListener { |
| |
| int mNumItems; |
| long[] mCalendarIds; |
| boolean[] mIsChecked; |
| ContentResolver mContentResolver; |
| boolean mRemove; |
| private int mCheckedCount; |
| private Button mOkButtonInAddDeleteCalendar; |
| |
| public ChangeCalendarAction(boolean remove) { |
| mContentResolver = SelectCalendarsActivity.this.getContentResolver(); |
| mRemove = remove; |
| } |
| |
| /* |
| * This is called when the user selects a calendar from either the |
| * "Add calendars" or "Remove calendars" popup dialog. |
| */ |
| public void onClick(DialogInterface dialog, int position, boolean isChecked) { |
| mIsChecked[position] = isChecked; |
| if (isChecked) { |
| mCheckedCount++; |
| } else { |
| mCheckedCount--; |
| } |
| |
| mOkButtonInAddDeleteCalendar.setEnabled(mCheckedCount > 0); |
| } |
| |
| /* |
| * This is called when the user presses the OK or Cancel button on the |
| * "Add calendars" or "Remove calendars" popup dialog. |
| */ |
| public void onClick(DialogInterface dialog, int which) { |
| // If the user cancelled the dialog, then do nothing. |
| if (which == DialogInterface.BUTTON_NEGATIVE) { |
| return; |
| } |
| |
| boolean changesFound = false; |
| for (int position = 0; position < mNumItems; position++) { |
| // If this calendar wasn't selected, then skip it. |
| if (!mIsChecked[position]) { |
| continue; |
| } |
| changesFound = true; |
| |
| long id = mCalendarIds[position]; |
| Uri uri = ContentUris.withAppendedId(Calendars.CONTENT_URI, id); |
| ContentValues values = new ContentValues(); |
| int selected = 1; |
| if (mRemove) { |
| selected = 0; |
| } |
| values.put(Calendars.SELECTED, selected); |
| values.put(Calendars.SYNC_EVENTS, selected); |
| mContentResolver.update(uri, values, null, null); |
| } |
| |
| // If there were any changes, then update the list of calendars |
| // that are synced. |
| if (changesFound) { |
| mCursor.requery(); |
| } |
| } |
| |
| public boolean onMenuItemClick(MenuItem item) { |
| AlertDialog.Builder builder = new AlertDialog.Builder(SelectCalendarsActivity.this); |
| String selection; |
| if (mRemove) { |
| builder.setTitle(R.string.remove_calendars) |
| .setIcon(android.R.drawable.ic_dialog_alert); |
| selection = Calendars.SYNC_EVENTS + "=1"; |
| } else { |
| builder.setTitle(R.string.add_calendars); |
| selection = Calendars.SYNC_EVENTS + "=0"; |
| } |
| ContentResolver cr = getContentResolver(); |
| // TODO this can cause ANRs http://b/1736511 |
| Cursor cursor = cr.query(Calendars.CONTENT_URI, PROJECTION, |
| selection, null /* selectionArgs */, |
| Calendars.DEFAULT_SORT_ORDER); |
| if (cursor == null) { |
| Log.w(TAG, "Cannot get cursor for calendars"); |
| return true; |
| } |
| |
| int count = cursor.getCount(); |
| mNumItems = count; |
| CharSequence[] calendarNames = new CharSequence[count]; |
| mCalendarIds = new long[count]; |
| mIsChecked = new boolean[count]; |
| mCheckedCount = 0; |
| try { |
| int pos = 0; |
| while (cursor.moveToNext()) { |
| mCalendarIds[pos] = cursor.getLong(0); |
| calendarNames[pos] = cursor.getString(1); |
| pos += 1; |
| } |
| } finally { |
| cursor.close(); |
| } |
| |
| AlertDialog dialog = builder.setMultiChoiceItems(calendarNames, null, this) |
| .setPositiveButton(android.R.string.ok, this) |
| .setNegativeButton(android.R.string.cancel, this).create(); |
| dialog.show(); |
| mOkButtonInAddDeleteCalendar = dialog.getButton(DialogInterface.BUTTON_POSITIVE); |
| mOkButtonInAddDeleteCalendar.setEnabled(false); |
| |
| return true; |
| } |
| } |
| |
| private class QueryHandler extends AsyncQueryHandler { |
| public QueryHandler(ContentResolver cr) { |
| super(cr); |
| } |
| |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, |
| Window.PROGRESS_VISIBILITY_OFF); |
| |
| // If the Activity is finishing, then close the cursor. |
| // Otherwise, use the new cursor in the adapter. |
| if (isFinishing()) { |
| stopManagingCursor(cursor); |
| cursor.close(); |
| } else { |
| if (cursor.getCount() == 0) { |
| // There are no calendars. This might happen if we lost |
| // the wireless connection (in airplane mode, for example). |
| // Leave the current list of calendars alone and pop up |
| // a dialog explaining that the connection is down. |
| // But allow the user to add and remove calendars. |
| return; |
| } |
| if (mCursor != null) { |
| stopManagingCursor(mCursor); |
| } |
| mCursor = cursor; |
| startManagingCursor(cursor); |
| mAdapter.changeCursor(cursor); |
| } |
| } |
| } |
| |
| // This class implements the menu option "Refresh list from server". |
| // (No longer used.) |
| public class RefreshAction implements Runnable { |
| public void run() { |
| startCalendarSync(); |
| } |
| } |
| |
| // startCalendarSync() checks the server for an updated list of Calendars |
| // (in the background) using an AsyncQueryHandler. |
| // |
| // Calendars are never removed from the phone due to a server sync. |
| // But if a Calendar is added on the web (and it is selected and not |
| // hidden) then it will be added to the list of calendars on the phone |
| // (when this asynchronous query finishes). When a new calendar from the |
| // web is added to the phone, then the events for that calendar are also |
| // downloaded from the web. |
| // |
| // This sync is done automatically in the background when the |
| // SelectCalendars activity is started. |
| private void startCalendarSync() { |
| getWindow().setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS, |
| Window.PROGRESS_VISIBILITY_ON); |
| |
| // TODO: make sure the user has login info. |
| |
| Uri uri = Calendars.LIVE_CONTENT_URI; |
| mQueryHandler.startQuery(0, null, uri, PROJECTION, |
| Calendars.SYNC_EVENTS + "=1", |
| null, Calendars.DEFAULT_SORT_ORDER); |
| } |
| } |