1 /*
2  * Copyright (C) 2013 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.Dialog;
21 import android.content.ContentUris;
22 import android.content.ContentValues;
23 import android.content.Context;
24 import android.database.Cursor;
25 import android.net.Uri;
26 import android.os.Bundle;
27 import android.provider.CalendarContract.Calendars;
28 import android.provider.CalendarContract.Colors;
29 import android.util.SparseIntArray;
30 
31 import com.android.colorpicker.ColorPickerDialog;
32 import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
33 import com.android.colorpicker.HsvColorComparator;
34 
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 
38 public class CalendarColorPickerDialog extends ColorPickerDialog {
39 
40     private static final int NUM_COLUMNS = 4;
41 
42     private static final String KEY_CALENDAR_ID = "calendar_id";
43     private static final String KEY_COLOR_KEYS = "color_keys";
44 
45     private static final int TOKEN_QUERY_CALENDARS = 1 << 1;
46     private static final int TOKEN_QUERY_COLORS = 1 << 2;
47 
48     static final String[] CALENDARS_PROJECTION = new String[] {
49             Calendars.ACCOUNT_NAME,
50             Calendars.ACCOUNT_TYPE,
51             Calendars.CALENDAR_COLOR
52     };
53 
54     static final int CALENDARS_INDEX_ACCOUNT_NAME = 0;
55     static final int CALENDARS_INDEX_ACCOUNT_TYPE = 1;
56     static final int CALENDARS_INDEX_CALENDAR_COLOR = 2;
57 
58     static final String[] COLORS_PROJECTION = new String[] {
59             Colors.COLOR,
60             Colors.COLOR_KEY
61     };
62 
63     static final String COLORS_WHERE = Colors.ACCOUNT_NAME + "=? AND " + Colors.ACCOUNT_TYPE +
64             "=? AND " + Colors.COLOR_TYPE + "=" + Colors.TYPE_CALENDAR;
65 
66     public static final int COLORS_INDEX_COLOR = 0;
67     public static final int COLORS_INDEX_COLOR_KEY = 1;
68 
69 
70     private QueryService mService;
71     private SparseIntArray mColorKeyMap = new SparseIntArray();
72     private long mCalendarId;
73 
74     private class QueryService extends AsyncQueryService {
75 
QueryService(Context context)76         private QueryService(Context context) {
77             super(context);
78         }
79 
80         @Override
onQueryComplete(int token, Object cookie, Cursor cursor)81         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
82             // If the query didn't return a cursor for some reason return
83             if (cursor == null) {
84                 return;
85             }
86 
87             // If the Activity is finishing, then close the cursor.
88             // Otherwise, use the new cursor in the adapter.
89             final Activity activity = getActivity();
90             if (activity == null || activity.isFinishing()) {
91                 cursor.close();
92                 return;
93             }
94 
95             switch (token) {
96                 case TOKEN_QUERY_CALENDARS:
97                     if (!cursor.moveToFirst()) {
98                         cursor.close();
99                         dismiss();
100                         break;
101                     }
102                     mSelectedColor = Utils.getDisplayColorFromColor(
103                             cursor.getInt(CALENDARS_INDEX_CALENDAR_COLOR));
104                     Uri uri = Colors.CONTENT_URI;
105                     String[] args = new String[] {
106                             cursor.getString(CALENDARS_INDEX_ACCOUNT_NAME),
107                             cursor.getString(CALENDARS_INDEX_ACCOUNT_TYPE) };
108                     cursor.close();
109                     startQuery(TOKEN_QUERY_COLORS, null, uri, COLORS_PROJECTION, COLORS_WHERE,
110                             args, null);
111                     break;
112                 case TOKEN_QUERY_COLORS:
113                     if (!cursor.moveToFirst()) {
114                         cursor.close();
115                         dismiss();
116                         break;
117                     }
118                     mColorKeyMap.clear();
119                     ArrayList<Integer> colors = new ArrayList<Integer>();
120                     do
121                     {
122                         int colorKey = cursor.getInt(COLORS_INDEX_COLOR_KEY);
123                         int rawColor = cursor.getInt(COLORS_INDEX_COLOR);
124                         int displayColor = Utils.getDisplayColorFromColor(rawColor);
125                         mColorKeyMap.put(displayColor, colorKey);
126                         colors.add(displayColor);
127                     } while (cursor.moveToNext());
128                     Integer[] colorsToSort = colors.toArray(new Integer[colors.size()]);
129                     Arrays.sort(colorsToSort, new HsvColorComparator());
130                     mColors = new int[colorsToSort.length];
131                     for (int i = 0; i < mColors.length; i++) {
132                         mColors[i] = colorsToSort[i];
133                     }
134                     showPaletteView();
135                     cursor.close();
136                     break;
137             }
138         }
139     }
140 
141     private class OnCalendarColorSelectedListener implements OnColorSelectedListener {
142 
143         @Override
onColorSelected(int color)144         public void onColorSelected(int color) {
145             if (color == mSelectedColor || mService == null) {
146                 return;
147             }
148 
149             ContentValues values = new ContentValues();
150             values.put(Calendars.CALENDAR_COLOR_KEY, mColorKeyMap.get(color));
151             mService.startUpdate(mService.getNextToken(), null, ContentUris.withAppendedId(
152                     Calendars.CONTENT_URI, mCalendarId), values, null, null, Utils.UNDO_DELAY);
153         }
154     }
155 
CalendarColorPickerDialog()156     public CalendarColorPickerDialog() {
157         // Empty constructor required for dialog fragments.
158     }
159 
newInstance(long calendarId, boolean isTablet)160     public static CalendarColorPickerDialog newInstance(long calendarId, boolean isTablet) {
161         CalendarColorPickerDialog ret = new CalendarColorPickerDialog();
162         ret.setArguments(R.string.calendar_color_picker_dialog_title, NUM_COLUMNS,
163                 isTablet ? SIZE_LARGE : SIZE_SMALL);
164         ret.setCalendarId(calendarId);
165         return ret;
166     }
167 
168     @Override
onSaveInstanceState(Bundle outState)169     public void onSaveInstanceState(Bundle outState) {
170         super.onSaveInstanceState(outState);
171         outState.putLong(KEY_CALENDAR_ID, mCalendarId);
172         saveColorKeys(outState);
173     }
174 
saveColorKeys(Bundle outState)175     private void saveColorKeys(Bundle outState) {
176         // No color keys to save, so just return
177         if (mColors == null) {
178             return;
179         }
180         int[] colorKeys = new int[mColors.length];
181         for (int i = 0; i < mColors.length; i++) {
182             colorKeys[i] = mColorKeyMap.get(mColors[i]);
183         }
184         outState.putIntArray(KEY_COLOR_KEYS, colorKeys);
185     }
186 
187     @Override
onCreate(Bundle savedInstanceState)188     public void onCreate(Bundle savedInstanceState) {
189         super.onCreate(savedInstanceState);
190         if (savedInstanceState != null) {
191             mCalendarId = savedInstanceState.getLong(KEY_CALENDAR_ID);
192             retrieveColorKeys(savedInstanceState);
193         }
194         setOnColorSelectedListener(new OnCalendarColorSelectedListener());
195     }
196 
retrieveColorKeys(Bundle savedInstanceState)197     private void retrieveColorKeys(Bundle savedInstanceState) {
198         int[] colorKeys = savedInstanceState.getIntArray(KEY_COLOR_KEYS);
199         if (mColors != null && colorKeys != null) {
200             for (int i = 0; i < mColors.length; i++) {
201                 mColorKeyMap.put(mColors[i], colorKeys[i]);
202             }
203         }
204     }
205 
206     @Override
setColors(int[] colors)207     public void setColors(int[] colors) {
208         throw new IllegalStateException("Must call setCalendarId() to update calendar colors");
209     }
210 
211     @Override
setColors(int[] colors, int selectedColor)212     public void setColors(int[] colors, int selectedColor) {
213         throw new IllegalStateException("Must call setCalendarId() to update calendar colors");
214     }
215 
setCalendarId(long calendarId)216     public void setCalendarId(long calendarId) {
217         if (calendarId != mCalendarId) {
218             mCalendarId = calendarId;
219             startQuery();
220         }
221     }
222 
223     @Override
onCreateDialog(Bundle savedInstanceState)224     public Dialog onCreateDialog(Bundle savedInstanceState) {
225         Dialog dialog = super.onCreateDialog(savedInstanceState);
226         mService = new QueryService(getActivity());
227         if (mColors == null) {
228             startQuery();
229         }
230         return dialog;
231     }
232 
startQuery()233     private void startQuery() {
234         if (mService != null) {
235             showProgressBarView();
236             mService.startQuery(TOKEN_QUERY_CALENDARS, null,
237                     ContentUris.withAppendedId(Calendars.CONTENT_URI, mCalendarId),
238                     CALENDARS_PROJECTION, null, null, null);
239         }
240     }
241 }
242