Calendar color picker Exchange account fix

Bug: 9196777
Change-Id: I21c97bf06ab51254d816f7c5cc2817f85e91d2eb
diff --git a/src/com/android/calendar/selectcalendars/CalendarColorCache.java b/src/com/android/calendar/selectcalendars/CalendarColorCache.java
new file mode 100644
index 0000000..0f9d14e
--- /dev/null
+++ b/src/com/android/calendar/selectcalendars/CalendarColorCache.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2013 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.selectcalendars;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.provider.CalendarContract.Colors;
+
+import com.android.calendar.AsyncQueryService;
+
+import java.util.HashSet;
+
+/**
+ * CalendarColorCache queries the provider and stores the account identifiers (name and type)
+ * of the accounts which contain optional calendar colors, and thus should allow for the
+ * user to choose calendar colors.
+ */
+public class CalendarColorCache {
+
+    private HashSet<String> mCache = new HashSet<String>();
+
+    private static final String SEPARATOR = "::";
+
+    private AsyncQueryService mService;
+    private OnCalendarColorsLoadedListener mListener;
+
+    private StringBuffer mStringBuffer = new StringBuffer();
+
+    private static String[] PROJECTION = new String[] {Colors.ACCOUNT_NAME, Colors.ACCOUNT_TYPE };
+
+    /**
+     * Interface which provides callback after provider query of calendar colors.
+     */
+    public interface OnCalendarColorsLoadedListener {
+
+        /**
+         * Callback after the set of accounts with additional calendar colors are loaded.
+         */
+        void onCalendarColorsLoaded();
+    }
+
+    public CalendarColorCache(Context context, OnCalendarColorsLoadedListener listener) {
+        mListener = listener;
+        mService = new AsyncQueryService(context) {
+
+            @Override
+            public void onQueryComplete(int token, Object cookie, Cursor c) {
+                if (c == null) {
+                    return;
+                }
+                if (c.moveToFirst()) {
+                    clear();
+                    do {
+                        insert(c.getString(0), c.getString(1));
+                    } while (c.moveToNext());
+                    mListener.onCalendarColorsLoaded();
+                }
+                if (c != null) {
+                    c.close();
+                }
+            }
+        };
+        mService.startQuery(0, null, Colors.CONTENT_URI, PROJECTION,
+                Colors.COLOR_TYPE + "=" + Colors.TYPE_CALENDAR, null, null);
+    }
+
+    /**
+     * Inserts a specified account into the set.
+     */
+    private void insert(String accountName, String accountType) {
+        mCache.add(generateKey(accountName, accountType));
+    }
+
+    /**
+     * Does a set lookup to determine if a specified account has more optional calendar colors.
+     */
+    public boolean hasColors(String accountName, String accountType) {
+        return mCache.contains(generateKey(accountName, accountType));
+    }
+
+    /**
+     * Clears the cached set.
+     */
+    private void clear() {
+        mCache.clear();
+    }
+
+    /**
+     * Generates a single key based on account name and account type for map lookup/insertion.
+     */
+    private String generateKey(String accountName, String accountType) {
+        mStringBuffer.setLength(0);
+        return mStringBuffer.append(accountName).append(SEPARATOR).append(accountType).toString();
+    }
+}
diff --git a/src/com/android/calendar/selectcalendars/SelectCalendarsSimpleAdapter.java b/src/com/android/calendar/selectcalendars/SelectCalendarsSimpleAdapter.java
index 07bafae..6a75d2b 100644
--- a/src/com/android/calendar/selectcalendars/SelectCalendarsSimpleAdapter.java
+++ b/src/com/android/calendar/selectcalendars/SelectCalendarsSimpleAdapter.java
@@ -39,8 +39,10 @@
 import com.android.calendar.CalendarColorPickerDialog;
 import com.android.calendar.R;
 import com.android.calendar.Utils;
+import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
 
-public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAdapter {
+public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAdapter,
+    OnCalendarColorsLoadedListener {
     private static final String TAG = "SelectCalendarsAdapter";
     private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";
 
@@ -71,16 +73,22 @@
     private int mColorColumn;
     private int mVisibleColumn;
     private int mOwnerAccountColumn;
+    private int mAccountNameColumn;
+    private int mAccountTypeColumn;
     private static float mScale = 0;
     private int mColorCalendarVisible;
     private int mColorCalendarHidden;
     private int mColorCalendarSecondaryVisible;
     private int mColorCalendarSecondaryHidden;
 
+    private CalendarColorCache mCache;
+
     private class CalendarRow {
         long id;
         String displayName;
         String ownerAccount;
+        String accountName;
+        String accountType;
         int color;
         boolean selected;
     }
@@ -103,6 +111,8 @@
             NORMAL_ITEM_HEIGHT *= mScale;
         }
 
+        mCache = new CalendarColorCache(context, this);
+
         mFragmentManager = fm;
         mColorPickerDialog = (CalendarColorPickerDialog)
                 fm.findFragmentByTag(COLOR_PICKER_DIALOG_TAG);
@@ -178,6 +188,8 @@
         mColorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
         mVisibleColumn = c.getColumnIndexOrThrow(Calendars.VISIBLE);
         mOwnerAccountColumn = c.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT);
+        mAccountNameColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME);
+        mAccountTypeColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE);
 
         mRowCount = c.getCount();
         mData = new CalendarRow[(c.getCount())];
@@ -190,6 +202,8 @@
             mData[p].color = c.getInt(mColorColumn);
             mData[p].selected = c.getInt(mVisibleColumn) != 0;
             mData[p].ownerAccount = c.getString(mOwnerAccountColumn);
+            mData[p].accountName = c.getString(mAccountNameColumn);
+            mData[p].accountType = c.getString(mAccountTypeColumn);
             p++;
         }
     }
@@ -238,6 +252,11 @@
         colorView.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
+                // Purely for sanity check--view should be disabled if account has no more colors
+                if (!hasMoreColors(position)) {
+                    return;
+                }
+
                 if (mColorPickerDialog == null) {
                     mColorPickerDialog = CalendarColorPickerDialog.newInstance(mData[position].id,
                             mIsTablet);
@@ -261,9 +280,11 @@
 
         CheckBox syncCheckBox = (CheckBox) view.findViewById(R.id.sync);
         if (syncCheckBox != null) {
+
             // Full screen layout
             syncCheckBox.setChecked(selected);
 
+            colorView.setEnabled(hasMoreColors(position));
             LayoutParams layoutParam = calendarName.getLayoutParams();
             TextView secondaryText = (TextView) view.findViewById(R.id.status);
             if (!TextUtils.isEmpty(mData[position].ownerAccount)
@@ -288,7 +309,7 @@
 
         } else {
             // Tablet layout
-            view.findViewById(R.id.color).setEnabled(selected);
+            view.findViewById(R.id.color).setEnabled(selected && hasMoreColors(position));
             view.setBackgroundDrawable(getBackground(position, selected));
             ViewGroup.LayoutParams newParams = view.getLayoutParams();
             if (position == mData.length - 1) {
@@ -306,6 +327,10 @@
         return view;
     }
 
+    private boolean hasMoreColors(int position) {
+        return mCache.hasColors(mData[position].accountName, mData[position].accountType);
+    }
+
     /**
      * @param position position of the calendar item
      * @param selected whether it is selected or not
@@ -355,4 +380,9 @@
     public boolean hasStableIds() {
         return true;
     }
+
+    @Override
+    public void onCalendarColorsLoaded() {
+        notifyDataSetChanged();
+    }
 }
diff --git a/src/com/android/calendar/selectcalendars/SelectCalendarsSyncAdapter.java b/src/com/android/calendar/selectcalendars/SelectCalendarsSyncAdapter.java
index 606c996..6e740bb 100644
--- a/src/com/android/calendar/selectcalendars/SelectCalendarsSyncAdapter.java
+++ b/src/com/android/calendar/selectcalendars/SelectCalendarsSyncAdapter.java
@@ -38,11 +38,12 @@
 import com.android.calendar.CalendarColorPickerDialog;
 import com.android.calendar.R;
 import com.android.calendar.Utils;
+import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
 
 import java.util.HashMap;
 
 public class SelectCalendarsSyncAdapter extends BaseAdapter
-        implements ListAdapter, AdapterView.OnItemClickListener {
+        implements ListAdapter, AdapterView.OnItemClickListener, OnCalendarColorsLoadedListener {
     private static final String TAG = "SelCalsAdapter";
     private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";
 
@@ -50,6 +51,7 @@
     private RectShape r = new RectShape();
 
     private CalendarColorPickerDialog mColorPickerDialog;
+    private CalendarColorCache mCache;
 
     private LayoutInflater mInflater;
     private static final int LAYOUT = R.layout.calendar_sync_item;
@@ -61,6 +63,8 @@
     private int mNameColumn;
     private int mColorColumn;
     private int mSyncedColumn;
+    private int mAccountNameColumn;
+    private int mAccountTypeColumn;
 
     private boolean mIsTablet;
     private FragmentManager mFragmentManager;
@@ -76,11 +80,14 @@
         int color;
         boolean synced;
         boolean originalSynced;
+        String accountName;
+        String accountType;
     }
 
     public SelectCalendarsSyncAdapter(Context context, Cursor c, FragmentManager manager) {
         super();
         initData(c);
+        mCache = new CalendarColorCache(context, this);
         mFragmentManager = manager;
         mColorPickerDialog = (CalendarColorPickerDialog)
                 manager.findFragmentByTag(COLOR_PICKER_DIALOG_TAG);
@@ -106,6 +113,8 @@
         mNameColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME);
         mColorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
         mSyncedColumn = c.getColumnIndexOrThrow(Calendars.SYNC_EVENTS);
+        mAccountNameColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME);
+        mAccountTypeColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE);
 
         mRowCount = c.getCount();
         mData = new CalendarRow[mRowCount];
@@ -118,6 +127,8 @@
             mData[p].displayName = c.getString(mNameColumn);
             mData[p].color = c.getInt(mColorColumn);
             mData[p].originalSynced = c.getInt(mSyncedColumn) != 0;
+            mData[p].accountName = c.getString(mAccountNameColumn);
+            mData[p].accountType = c.getString(mAccountTypeColumn);
             if (mChanges.containsKey(id)) {
                 mData[p].synced = mChanges.get(id).synced;
             } else {
@@ -174,11 +185,17 @@
         }
 
         View colorView = view.findViewById(R.id.color);
+        colorView.setEnabled(hasMoreColors(position));
         colorView.setBackgroundColor(color);
         colorView.setOnClickListener(new OnClickListener() {
 
             @Override
             public void onClick(View v) {
+                // Purely for sanity check--view should be disabled if account has no more colors
+                if (!hasMoreColors(position)) {
+                    return;
+                }
+
                 if (mColorPickerDialog == null) {
                     mColorPickerDialog = CalendarColorPickerDialog.newInstance(mData[position].id,
                             mIsTablet);
@@ -196,6 +213,10 @@
         return view;
     }
 
+    private boolean hasMoreColors(int position) {
+        return mCache.hasColors(mData[position].accountName, mData[position].accountType);
+    }
+
     private static void setText(View view, int id, String text) {
         if (TextUtils.isEmpty(text)) {
             return;
@@ -259,4 +280,9 @@
     public HashMap<Long, CalendarRow> getChanges() {
         return mChanges;
     }
+
+    @Override
+    public void onCalendarColorsLoaded() {
+        notifyDataSetChanged();
+    }
 }
diff --git a/src/com/android/calendar/selectcalendars/SelectCalendarsSyncFragment.java b/src/com/android/calendar/selectcalendars/SelectCalendarsSyncFragment.java
index 4c148aa..abdd50d 100644
--- a/src/com/android/calendar/selectcalendars/SelectCalendarsSyncFragment.java
+++ b/src/com/android/calendar/selectcalendars/SelectCalendarsSyncFragment.java
@@ -36,14 +36,12 @@
 import android.provider.CalendarContract.Calendars;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.Button;
 import android.widget.ListAdapter;
 import android.widget.TextView;
 
 import com.android.calendar.AsyncQueryService;
-import com.android.calendar.CalendarColorPickerDialog;
 import com.android.calendar.R;
 import com.android.calendar.Utils;
 import com.android.calendar.selectcalendars.SelectCalendarsSyncAdapter.CalendarRow;
@@ -68,6 +66,8 @@
         Calendars.CALENDAR_DISPLAY_NAME,
         Calendars.CALENDAR_COLOR,
         Calendars.SYNC_EVENTS,
+        Calendars.ACCOUNT_NAME,
+        Calendars.ACCOUNT_TYPE,
         "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + IS_PRIMARY, };
 
     private TextView mSyncStatus;
diff --git a/src/com/android/calendar/selectcalendars/SelectSyncedCalendarsMultiAccountAdapter.java b/src/com/android/calendar/selectcalendars/SelectSyncedCalendarsMultiAccountAdapter.java
index 91610c6..58cdbdb 100644
--- a/src/com/android/calendar/selectcalendars/SelectSyncedCalendarsMultiAccountAdapter.java
+++ b/src/com/android/calendar/selectcalendars/SelectSyncedCalendarsMultiAccountAdapter.java
@@ -44,13 +44,14 @@
 import com.android.calendar.CalendarColorPickerDialog;
 import com.android.calendar.R;
 import com.android.calendar.Utils;
+import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
 
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
 public class SelectSyncedCalendarsMultiAccountAdapter extends CursorTreeAdapter implements
-        View.OnClickListener {
+        View.OnClickListener, OnCalendarColorsLoadedListener {
 
     private static final String TAG = "Calendar";
     private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";
@@ -119,6 +120,7 @@
       Calendars.VISIBLE,
       Calendars.SYNC_EVENTS,
       "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + IS_PRIMARY,
+      Calendars.ACCOUNT_TYPE
     };
     //Keep these in sync with the projection
     private static final int ID_COLUMN = 0;
@@ -129,10 +131,13 @@
     private static final int SELECTED_COLUMN = 5;
     private static final int SYNCED_COLUMN = 6;
     private static final int PRIMARY_COLUMN = 7;
+    private static final int ACCOUNT_TYPE_COLUMN = 8;
 
     private static final int TAG_ID_CALENDAR_ID = R.id.calendar;
     private static final int TAG_ID_SYNC_CHECKBOX = R.id.sync;
 
+    private CalendarColorCache mCache;
+
     private class AsyncCalendarsUpdater extends AsyncQueryHandler {
 
         public AsyncCalendarsUpdater(ContentResolver cr) {
@@ -216,6 +221,8 @@
         mSyncedText = context.getString(R.string.synced);
         mNotSyncedText = context.getString(R.string.not_synced);
 
+        mCache = new CalendarColorCache(context, this);
+
         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mResolver = context.getContentResolver();
         mActivity = act;
@@ -312,9 +319,12 @@
         final long id = cursor.getLong(ID_COLUMN);
         String name = cursor.getString(NAME_COLUMN);
         String owner = cursor.getString(OWNER_COLUMN);
+        final String accountName = cursor.getString(ACCOUNT_COLUMN);
+        final String accountType = cursor.getString(ACCOUNT_TYPE_COLUMN);
         int color = Utils.getDisplayColorFromColor(cursor.getInt(COLOR_COLUMN));
 
         final View colorSquare = view.findViewById(R.id.color);
+        colorSquare.setEnabled(mCache.hasColors(accountName, accountType));
         colorSquare.setBackgroundColor(color);
         final View delegateParent = (View) colorSquare.getParent();
         delegateParent.post(new Runnable() {
@@ -334,6 +344,9 @@
 
             @Override
             public void onClick(View v) {
+                if (!mCache.hasColors(accountName, accountType)) {
+                    return;
+                }
                 if (mColorPickerDialog == null) {
                     mColorPickerDialog = CalendarColorPickerDialog.newInstance(id, mIsTablet);
                 } else {
@@ -449,4 +462,9 @@
                     CALENDARS_ORDERBY);
         }
     }
+
+    @Override
+    public void onCalendarColorsLoaded() {
+        notifyDataSetChanged();
+    }
 }
diff --git a/src/com/android/calendar/selectcalendars/SelectVisibleCalendarsFragment.java b/src/com/android/calendar/selectcalendars/SelectVisibleCalendarsFragment.java
index d0bbdb6..5302896 100644
--- a/src/com/android/calendar/selectcalendars/SelectVisibleCalendarsFragment.java
+++ b/src/com/android/calendar/selectcalendars/SelectVisibleCalendarsFragment.java
@@ -16,13 +16,6 @@
 
 package com.android.calendar.selectcalendars;
 
-import com.android.calendar.AsyncQueryService;
-import com.android.calendar.CalendarController.EventInfo;
-import com.android.calendar.CalendarController.EventType;
-import com.android.calendar.R;
-import com.android.calendar.CalendarController;
-import com.android.calendar.Utils;
-
 import android.app.Activity;
 import android.app.Fragment;
 import android.content.ContentUris;
@@ -31,15 +24,23 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.provider.CalendarContract.Calendars;
-import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.AdapterView;
 import android.widget.ListView;
 
+import com.android.calendar.AsyncQueryService;
+import com.android.calendar.CalendarController;
+import com.android.calendar.CalendarController.EventInfo;
+import com.android.calendar.CalendarController.EventType;
+import com.android.calendar.R;
+import com.android.calendar.Utils;
+import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
+
 public class SelectVisibleCalendarsFragment extends Fragment
-        implements AdapterView.OnItemClickListener, CalendarController.EventHandler {
+        implements AdapterView.OnItemClickListener, CalendarController.EventHandler,
+        OnCalendarColorsLoadedListener {
 
     private static final String TAG = "Calendar";
     private static final String IS_PRIMARY = "\"primary\"";
@@ -49,6 +50,7 @@
     private static final String[] PROJECTION = new String[] {
         Calendars._ID,
         Calendars.ACCOUNT_NAME,
+        Calendars.ACCOUNT_TYPE,
         Calendars.OWNER_ACCOUNT,
         Calendars.CALENDAR_DISPLAY_NAME,
         Calendars.CALENDAR_COLOR,
@@ -183,4 +185,11 @@
     public void handleEvent(EventInfo event) {
         eventsChanged();
     }
+
+    @Override
+    public void onCalendarColorsLoaded() {
+        if (mAdapter != null) {
+            mAdapter.notifyDataSetChanged();
+        }
+    }
 }