12aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan/* 22aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * Copyright (C) 2011 The Android Open Source Project 32aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * 42aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * Licensed under the Apache License, Version 2.0 (the "License"); 52aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * you may not use this file except in compliance with the License. 62aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * You may obtain a copy of the License at 72aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * 82aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * http://www.apache.org/licenses/LICENSE-2.0 92aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * 102aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * Unless required by applicable law or agreed to in writing, software 112aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * distributed under the License is distributed on an "AS IS" BASIS, 122aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * See the License for the specific language governing permissions and 142aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * limitations under the License. 152aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan */ 162aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 172aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanpackage com.android.calendar.selectcalendars; 182aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 192aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport com.android.calendar.R; 202aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport com.android.calendar.Utils; 212aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 222aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.accounts.AccountManager; 232aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.accounts.AuthenticatorDescription; 242aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.content.AsyncQueryHandler; 252aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.content.ContentResolver; 262aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.content.ContentUris; 272aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.content.ContentValues; 282aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.content.Context; 292aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.content.pm.PackageManager; 302aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.database.Cursor; 312aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.database.MatrixCursor; 322aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.net.Uri; 332aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.provider.CalendarContract.Calendars; 342aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.text.TextUtils; 352aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.util.Log; 362aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.view.LayoutInflater; 372aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.view.View; 382aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.view.ViewGroup; 392aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.widget.CheckBox; 402aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.widget.CursorTreeAdapter; 412aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport android.widget.TextView; 422aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 432aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport java.util.HashMap; 442aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport java.util.Iterator; 452aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanimport java.util.Map; 462aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 472aeb8d988aa4b65d3402374832613ab977e009dcMichael Chanpublic class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter implements 482aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan View.OnClickListener { 492aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 502aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final String TAG = "Calendar"; 512aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 522aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final String IS_PRIMARY = "\"primary\""; 532aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final String CALENDARS_ORDERBY = IS_PRIMARY + " DESC," 542aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan + Calendars.CALENDAR_DISPLAY_NAME + " COLLATE NOCASE"; 552aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final String ACCOUNT_SELECTION = Calendars.ACCOUNT_NAME + "=?" 562aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan + " AND " + Calendars.ACCOUNT_TYPE + "=?"; 572aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 582aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private final LayoutInflater mInflater; 592aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private final ContentResolver mResolver; 602aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private final SelectSyncedCalendarsMultiAccountActivity mActivity; 612aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private final View mView; 622aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private final static Runnable mStopRefreshing = new Runnable() { 632aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public void run() { 642aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mRefresh = false; 652aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 662aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan }; 672aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private Map<String, AuthenticatorDescription> mTypeToAuthDescription 682aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan = new HashMap<String, AuthenticatorDescription>(); 692aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected AuthenticatorDescription[] mAuthDescs; 702aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 712aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // These track changes to the synced state of calendars 722aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private Map<Long, Boolean> mCalendarChanges 732aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan = new HashMap<Long, Boolean>(); 742aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private Map<Long, Boolean> mCalendarInitialStates 752aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan = new HashMap<Long, Boolean>(); 762aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 772aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // This is for keeping MatrixCursor copies so that we can requery in the background. 782aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static Map<String, Cursor> mChildrenCursors 792aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan = new HashMap<String, Cursor>(); 802aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 812aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static AsyncCalendarsUpdater mCalendarsUpdater; 822aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // This is to keep our update tokens separate from other tokens. Since we cancel old updates 832aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // when a new update comes in, we'd like to leave a token space that won't be canceled. 842aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int MIN_UPDATE_TOKEN = 1000; 852aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static int mUpdateToken = MIN_UPDATE_TOKEN; 862aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // How long to wait between requeries of the calendars to see if anything has changed. 872aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int REFRESH_DELAY = 5000; 882aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // How long to keep refreshing for 892aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int REFRESH_DURATION = 60000; 902aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static boolean mRefresh = true; 912aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 922aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static String mSyncedText; 932aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static String mNotSyncedText; 942aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 952aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // This is to keep track of whether or not multiple calendars have the same display name 962aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static HashMap<String, Boolean> mIsDuplicateName = new HashMap<String, Boolean>(); 972aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 982aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final String[] PROJECTION = new String[] { 992aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars._ID, 1002aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.ACCOUNT_NAME, 1012aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.OWNER_ACCOUNT, 1022aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.CALENDAR_DISPLAY_NAME, 1032aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.CALENDAR_COLOR, 1042aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.VISIBLE, 1052aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.SYNC_EVENTS, 1062aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + IS_PRIMARY, 1072aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan }; 1082aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan //Keep these in sync with the projection 1092aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int ID_COLUMN = 0; 1102aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int ACCOUNT_COLUMN = 1; 1112aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int OWNER_COLUMN = 2; 1122aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int NAME_COLUMN = 3; 1132aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int COLOR_COLUMN = 4; 1142aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int SELECTED_COLUMN = 5; 1152aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int SYNCED_COLUMN = 6; 1162aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int PRIMARY_COLUMN = 7; 1172aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1182aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int TAG_ID_CALENDAR_ID = R.id.calendar; 1192aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static final int TAG_ID_SYNC_CHECKBOX = R.id.sync; 1202aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1212aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private class AsyncCalendarsUpdater extends AsyncQueryHandler { 1222aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1232aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public AsyncCalendarsUpdater(ContentResolver cr) { 1242aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan super(cr); 1252aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1262aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1272aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan @Override 1282aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected void onQueryComplete(int token, Object cookie, Cursor cursor) { 1292aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if(cursor == null) { 1302aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return; 1312aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1322aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1332aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Cursor currentCursor = mChildrenCursors.get(cookie); 1342aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Check if the new cursor has the same content as our old cursor 1352aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (currentCursor != null) { 1362aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (Utils.compareCursors(currentCursor, cursor)) { 1372aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan cursor.close(); 1382aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return; 1392aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1402aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1412aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // If not then make a new matrix cursor for our Map 1422aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan MatrixCursor newCursor = Utils.matrixCursorFromCursor(cursor); 1432aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan cursor.close(); 1442aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // And update our list of duplicated names 1452aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Utils.checkForDuplicateNames(mIsDuplicateName, newCursor, NAME_COLUMN); 1462aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1472aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mChildrenCursors.put((String)cookie, newCursor); 1482aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan try { 1492aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan setChildrenCursor(token, newCursor); 1502aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mActivity.startManagingCursor(newCursor); 1512aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } catch (NullPointerException e) { 1522aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Log.w(TAG, "Adapter expired, try again on the next query: " + e); 1532aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1542aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Clean up our old cursor if we had one. We have to do this after setting the new 1552aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // cursor so that our view doesn't throw on an invalid cursor. 1562aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (currentCursor != null) { 1572aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mActivity.stopManagingCursor(currentCursor); 1582aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan currentCursor.close(); 1592aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1602aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1612aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1622aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1632aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan /** 1642aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * Method for changing the sync state when a calendar's button is pressed. 1652aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * 1662aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * This gets called when the CheckBox for a calendar is clicked. It toggles 1672aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * the sync state for the associated calendar and saves a change of state to 1682aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * a hashmap. It also compares against the original value and removes any 1692aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * changes from the hashmap if this is back at its initial state. 1702aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan */ 1712aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public void onClick(View v) { 1722aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan long id = (Long) v.getTag(TAG_ID_CALENDAR_ID); 1732aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan boolean newState; 1742aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan boolean initialState = mCalendarInitialStates.get(id); 1752aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (mCalendarChanges.containsKey(id)) { 1762aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Negate to reflect the click 1772aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan newState = !mCalendarChanges.get(id); 1782aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } else { 1792aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Negate to reflect the click 1802aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan newState = !initialState; 1812aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1822aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1832aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (newState == initialState) { 1842aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarChanges.remove(id); 1852aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } else { 1862aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarChanges.put(id, newState); 1872aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1882aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1892aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan ((CheckBox) v.getTag(TAG_ID_SYNC_CHECKBOX)).setChecked(newState); 1902aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan setText(v, R.id.status, newState ? mSyncedText : mNotSyncedText); 1912aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 1922aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1932aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public SelectSyncedCalendarsMultiAccountAdapter(Context context, Cursor acctsCursor, 1942aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan SelectSyncedCalendarsMultiAccountActivity act) { 1952aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan super(acctsCursor, context); 1962aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mSyncedText = context.getString(R.string.synced); 1972aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mNotSyncedText = context.getString(R.string.not_synced); 1982aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 1992aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 2002aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mResolver = context.getContentResolver(); 2012aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mActivity = act; 2022aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (mCalendarsUpdater == null) { 2032aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarsUpdater = new AsyncCalendarsUpdater(mResolver); 2042aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2052aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2066fb869698103a0f897537fa7f4ebabe714db9d37Michael Chan if (acctsCursor == null || acctsCursor.getCount() == 0) { 2076fb869698103a0f897537fa7f4ebabe714db9d37Michael Chan Log.i(TAG, "SelectCalendarsAdapter: No accounts were returned!"); 2082aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2092aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Collect proper description for account types 2102aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mAuthDescs = AccountManager.get(context).getAuthenticatorTypes(); 2112aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan for (int i = 0; i < mAuthDescs.length; i++) { 2122aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]); 2132aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2142aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mView = mActivity.getExpandableListView(); 2152aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mRefresh = true; 2162aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2172aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2182aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public void startRefreshStopDelay() { 2192aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mRefresh = true; 2202aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mView.postDelayed(mStopRefreshing, REFRESH_DURATION); 2212aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2222aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2232aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public void cancelRefreshStopDelay() { 2242aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mView.removeCallbacks(mStopRefreshing); 2252aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2262aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2272aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan /* 2282aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * Write back the changes that have been made. The sync code will pick up any changes and 2292aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * do updates on its own. 2302aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan */ 2312aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public void doSaveAction() { 2322aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Cancel the previous operation 2332aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarsUpdater.cancelOperation(mUpdateToken); 2342aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mUpdateToken++; 2352aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // This is to allow us to do queries and updates with the same AsyncQueryHandler without 2362aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // accidently canceling queries. 2372aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if(mUpdateToken < MIN_UPDATE_TOKEN) { 2382aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mUpdateToken = MIN_UPDATE_TOKEN; 2392aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2402aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2412aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Iterator<Long> changeKeys = mCalendarChanges.keySet().iterator(); 2422aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan while (changeKeys.hasNext()) { 2432aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan long id = changeKeys.next(); 2442aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan boolean newSynced = mCalendarChanges.get(id); 2452aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2462aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Uri uri = ContentUris.withAppendedId(Calendars.CONTENT_URI, id); 2472aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan ContentValues values = new ContentValues(); 2482aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan values.put(Calendars.VISIBLE, newSynced ? 1 : 0); 2492aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan values.put(Calendars.SYNC_EVENTS, newSynced ? 1 : 0); 2502aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarsUpdater.startUpdate(mUpdateToken, id, uri, values, null, null); 2512aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2522aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2532aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2542aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private static void setText(View view, int id, String text) { 2552aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (TextUtils.isEmpty(text)) { 2562aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return; 2572aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2582aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan TextView textView = (TextView) view.findViewById(id); 2592aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan textView.setText(text); 2602aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2612aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2622aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan /** 2632aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * Gets the label associated with a particular account type. If none found, return null. 2642aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * @param accountType the type of account 2652aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan * @return a CharSequence for the label or null if one cannot be found. 2662aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan */ 2672aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected CharSequence getLabelForType(final String accountType) { 2682aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan CharSequence label = null; 2692aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (mTypeToAuthDescription.containsKey(accountType)) { 2702aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan try { 2712aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType); 2722aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Context authContext = mActivity.createPackageContext(desc.packageName, 0); 2732aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan label = authContext.getResources().getText(desc.labelId); 2742aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } catch (PackageManager.NameNotFoundException e) { 2752aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Log.w(TAG, "No label for account type " + ", type " + accountType); 2762aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2772aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2782aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return label; 2792aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2802aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2812aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan @Override 2822aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected void bindChildView(View view, Context context, Cursor cursor, boolean isLastChild) { 2832aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan view.findViewById(R.id.color).setBackgroundColor(cursor.getInt(COLOR_COLUMN)); 2842aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String name = cursor.getString(NAME_COLUMN); 2852aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String owner = cursor.getString(OWNER_COLUMN); 2862aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (mIsDuplicateName.containsKey(name) && mIsDuplicateName.get(name) && 2872aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan !name.equalsIgnoreCase(owner)) { 2882aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan name = new StringBuilder(name) 2892aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan .append(Utils.OPEN_EMAIL_MARKER) 2902aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan .append(owner) 2912aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan .append(Utils.CLOSE_EMAIL_MARKER) 2922aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan .toString(); 2932aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 2942aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan setText(view, R.id.calendar, name); 2952aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 2962aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // First see if the user has already changed the state of this calendar 2972aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan long id = cursor.getLong(ID_COLUMN); 2982aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Boolean sync = mCalendarChanges.get(id); 2992aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if (sync == null) { 3002aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan sync = cursor.getInt(SYNCED_COLUMN) == 1; 3012aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarInitialStates.put(id, sync); 3022aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3032aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3042aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan CheckBox button = (CheckBox) view.findViewById(R.id.sync); 3052aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan button.setChecked(sync); 3062aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan setText(view, R.id.status, sync ? mSyncedText : mNotSyncedText); 3072aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3082aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan view.setTag(TAG_ID_CALENDAR_ID, id); 3092aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan view.setTag(TAG_ID_SYNC_CHECKBOX, button); 3102aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan view.setOnClickListener(this); 3112aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3122aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3132aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan @Override 3142aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected void bindGroupView(View view, Context context, Cursor cursor, boolean isExpanded) { 3152aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan int accountColumn = cursor.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME); 3162aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan int accountTypeColumn = cursor.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE); 3172aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String account = cursor.getString(accountColumn); 3182aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String accountType = cursor.getString(accountTypeColumn); 319953fb5d952c793a2514fd48abef8d8bcff7661dcSara Ting CharSequence accountLabel = getLabelForType(accountType); 3202aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan setText(view, R.id.account, account); 321953fb5d952c793a2514fd48abef8d8bcff7661dcSara Ting if (accountLabel != null) { 322953fb5d952c793a2514fd48abef8d8bcff7661dcSara Ting setText(view, R.id.account_type, accountLabel.toString()); 323953fb5d952c793a2514fd48abef8d8bcff7661dcSara Ting } 3242aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3252aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3262aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan @Override 3272aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected Cursor getChildrenCursor(Cursor groupCursor) { 3282aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan int accountColumn = groupCursor.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME); 3292aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan int accountTypeColumn = groupCursor.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE); 3302aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String account = groupCursor.getString(accountColumn); 3312aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String accountType = groupCursor.getString(accountTypeColumn); 3322aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan //Get all the calendars for just this account. 33365cf22bc9170c8aa6a26ef0bf91888a0ae1afaebMichael Chan Cursor childCursor = mChildrenCursors.get(accountType + "#" + account); 3342aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan new RefreshCalendars(groupCursor.getPosition(), account, accountType).run(); 3352aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return childCursor; 3362aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3372aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3382aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan @Override 3392aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected View newChildView(Context context, Cursor cursor, boolean isLastChild, 3402aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan ViewGroup parent) { 3412aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return mInflater.inflate(R.layout.calendar_sync_item, parent, false); 3422aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3432aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3442aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan @Override 3452aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan protected View newGroupView(Context context, Cursor cursor, boolean isExpanded, 3462aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan ViewGroup parent) { 3472aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan return mInflater.inflate(R.layout.account_item, parent, false); 3482aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3492aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3502aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan private class RefreshCalendars implements Runnable { 3512aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3522aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan int mToken; 3532aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String mAccount; 3542aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan String mAccountType; 3552aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 35665cf22bc9170c8aa6a26ef0bf91888a0ae1afaebMichael Chan public RefreshCalendars(int token, String account, String accountType) { 3572aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mToken = token; 35865cf22bc9170c8aa6a26ef0bf91888a0ae1afaebMichael Chan mAccount = account; 3592aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mAccountType = accountType; 3602aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3612aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan 3622aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan public void run() { 3632aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarsUpdater.cancelOperation(mToken); 3642aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan // Set up a refresh for some point in the future if we haven't stopped updates yet 3652aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan if(mRefresh) { 3662aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mView.postDelayed(new RefreshCalendars(mToken, mAccount, mAccountType), 3672aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan REFRESH_DELAY); 3682aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3692aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan mCalendarsUpdater.startQuery(mToken, 37065cf22bc9170c8aa6a26ef0bf91888a0ae1afaebMichael Chan mAccountType + "#" + mAccount, 3712aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan Calendars.CONTENT_URI, PROJECTION, 3722aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan ACCOUNT_SELECTION, 3732aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan new String[] { mAccount, mAccountType } /*selectionArgs*/, 3742aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan CALENDARS_ORDERBY); 3752aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3762aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan } 3772aeb8d988aa4b65d3402374832613ab977e009dcMichael Chan} 378