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
17package com.android.calendar;
18
19import android.app.Activity;
20import android.app.Dialog;
21import android.content.ContentUris;
22import android.content.ContentValues;
23import android.content.Context;
24import android.database.Cursor;
25import android.net.Uri;
26import android.os.Bundle;
27import android.provider.CalendarContract.Calendars;
28import android.provider.CalendarContract.Colors;
29import android.util.SparseIntArray;
30
31import com.android.colorpicker.ColorPickerDialog;
32import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
33import com.android.colorpicker.HsvColorComparator;
34
35import java.util.ArrayList;
36import java.util.Arrays;
37
38public 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
76        private QueryService(Context context) {
77            super(context);
78        }
79
80        @Override
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
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
156    public CalendarColorPickerDialog() {
157        // Empty constructor required for dialog fragments.
158    }
159
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
169    public void onSaveInstanceState(Bundle outState) {
170        super.onSaveInstanceState(outState);
171        outState.putLong(KEY_CALENDAR_ID, mCalendarId);
172        saveColorKeys(outState);
173    }
174
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
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
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
207    public void setColors(int[] colors) {
208        throw new IllegalStateException("Must call setCalendarId() to update calendar colors");
209    }
210
211    @Override
212    public void setColors(int[] colors, int selectedColor) {
213        throw new IllegalStateException("Must call setCalendarId() to update calendar colors");
214    }
215
216    public void setCalendarId(long calendarId) {
217        if (calendarId != mCalendarId) {
218            mCalendarId = calendarId;
219            startQuery();
220        }
221    }
222
223    @Override
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
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