1 /*
2  * Copyright (C) 2010 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.selectcalendars;
18 
19 import android.app.FragmentManager;
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.database.Cursor;
23 import android.graphics.Rect;
24 import android.graphics.drawable.shapes.RectShape;
25 import android.provider.CalendarContract.Calendars;
26 import android.text.TextUtils;
27 import android.view.LayoutInflater;
28 import android.view.TouchDelegate;
29 import android.view.View;
30 import android.view.View.OnClickListener;
31 import android.view.ViewGroup;
32 import android.widget.AdapterView;
33 import android.widget.BaseAdapter;
34 import android.widget.CheckBox;
35 import android.widget.ListAdapter;
36 import android.widget.TextView;
37 
38 import com.android.calendar.CalendarColorPickerDialog;
39 import com.android.calendar.R;
40 import com.android.calendar.Utils;
41 import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener;
42 
43 import java.util.HashMap;
44 
45 public class SelectCalendarsSyncAdapter extends BaseAdapter
46         implements ListAdapter, AdapterView.OnItemClickListener, OnCalendarColorsLoadedListener {
47     private static final String TAG = "SelCalsAdapter";
48     private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog";
49 
50     private static int COLOR_CHIP_SIZE = 30;
51     private RectShape r = new RectShape();
52 
53     private CalendarColorPickerDialog mColorPickerDialog;
54     private CalendarColorCache mCache;
55 
56     private LayoutInflater mInflater;
57     private static final int LAYOUT = R.layout.calendar_sync_item;
58     private CalendarRow[] mData;
59     private HashMap<Long, CalendarRow> mChanges = new HashMap<Long, CalendarRow>();
60     private int mRowCount = 0;
61 
62     private int mIdColumn;
63     private int mNameColumn;
64     private int mColorColumn;
65     private int mSyncedColumn;
66     private int mAccountNameColumn;
67     private int mAccountTypeColumn;
68 
69     private boolean mIsTablet;
70     private FragmentManager mFragmentManager;
71     private int mColorViewTouchAreaIncrease;
72 
73 
74     private final String mSyncedString;
75     private final String mNotSyncedString;
76 
77     public class CalendarRow {
78         long id;
79         String displayName;
80         int color;
81         boolean synced;
82         boolean originalSynced;
83         String accountName;
84         String accountType;
85     }
86 
SelectCalendarsSyncAdapter(Context context, Cursor c, FragmentManager manager)87     public SelectCalendarsSyncAdapter(Context context, Cursor c, FragmentManager manager) {
88         super();
89         initData(c);
90         mCache = new CalendarColorCache(context, this);
91         mFragmentManager = manager;
92         mColorPickerDialog = (CalendarColorPickerDialog)
93                 manager.findFragmentByTag(COLOR_PICKER_DIALOG_TAG);
94         mColorViewTouchAreaIncrease = context.getResources()
95                 .getDimensionPixelSize(R.dimen.color_view_touch_area_increase);
96         mIsTablet = Utils.getConfigBool(context, R.bool.tablet_config);
97         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
98         COLOR_CHIP_SIZE *= context.getResources().getDisplayMetrics().density;
99         r.resize(COLOR_CHIP_SIZE, COLOR_CHIP_SIZE);
100         Resources res = context.getResources();
101         mSyncedString = res.getString(R.string.synced);
102         mNotSyncedString = res.getString(R.string.not_synced);
103     }
104 
initData(Cursor c)105     private void initData(Cursor c) {
106         if (c == null) {
107             mRowCount = 0;
108             mData = null;
109             return;
110         }
111 
112         mIdColumn = c.getColumnIndexOrThrow(Calendars._ID);
113         mNameColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME);
114         mColorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR);
115         mSyncedColumn = c.getColumnIndexOrThrow(Calendars.SYNC_EVENTS);
116         mAccountNameColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME);
117         mAccountTypeColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE);
118 
119         mRowCount = c.getCount();
120         mData = new CalendarRow[mRowCount];
121         c.moveToPosition(-1);
122         int p = 0;
123         while (c.moveToNext()) {
124             long id = c.getLong(mIdColumn);
125             mData[p] = new CalendarRow();
126             mData[p].id = id;
127             mData[p].displayName = c.getString(mNameColumn);
128             mData[p].color = c.getInt(mColorColumn);
129             mData[p].originalSynced = c.getInt(mSyncedColumn) != 0;
130             mData[p].accountName = c.getString(mAccountNameColumn);
131             mData[p].accountType = c.getString(mAccountTypeColumn);
132             if (mChanges.containsKey(id)) {
133                 mData[p].synced = mChanges.get(id).synced;
134             } else {
135                 mData[p].synced = mData[p].originalSynced;
136             }
137             p++;
138         }
139     }
140 
changeCursor(Cursor c)141     public void changeCursor(Cursor c) {
142         initData(c);
143         notifyDataSetChanged();
144     }
145 
146     @Override
getView(final int position, View convertView, ViewGroup parent)147     public View getView(final int position, View convertView, ViewGroup parent) {
148         if (position >= mRowCount) {
149             return null;
150         }
151         String name = mData[position].displayName;
152         boolean selected = mData[position].synced;
153         int color = Utils.getDisplayColorFromColor(mData[position].color);
154         View view;
155         if (convertView == null) {
156             view = mInflater.inflate(LAYOUT, parent, false);
157             final View delegate = view.findViewById(R.id.color);
158             final View delegateParent = (View) delegate.getParent();
159             delegateParent.post(new Runnable() {
160 
161                 @Override
162                 public void run() {
163                     final Rect r = new Rect();
164                     delegate.getHitRect(r);
165                     r.top -= mColorViewTouchAreaIncrease;
166                     r.bottom += mColorViewTouchAreaIncrease;
167                     r.left -= mColorViewTouchAreaIncrease;
168                     r.right += mColorViewTouchAreaIncrease;
169                     delegateParent.setTouchDelegate(new TouchDelegate(r, delegate));
170                 }
171             });
172         } else {
173             view = convertView;
174         }
175 
176         view.setTag(mData[position]);
177 
178         CheckBox cb = (CheckBox) view.findViewById(R.id.sync);
179         cb.setChecked(selected);
180 
181         if (selected) {
182             setText(view, R.id.status, mSyncedString);
183         } else {
184             setText(view, R.id.status, mNotSyncedString);
185         }
186 
187         View colorView = view.findViewById(R.id.color);
188         colorView.setEnabled(hasMoreColors(position));
189         colorView.setBackgroundColor(color);
190         colorView.setOnClickListener(new OnClickListener() {
191 
192             @Override
193             public void onClick(View v) {
194                 // Purely for sanity check--view should be disabled if account has no more colors
195                 if (!hasMoreColors(position)) {
196                     return;
197                 }
198 
199                 if (mColorPickerDialog == null) {
200                     mColorPickerDialog = CalendarColorPickerDialog.newInstance(mData[position].id,
201                             mIsTablet);
202                 } else {
203                     mColorPickerDialog.setCalendarId(mData[position].id);
204                 }
205                 mFragmentManager.executePendingTransactions();
206                 if (!mColorPickerDialog.isAdded()) {
207                     mColorPickerDialog.show(mFragmentManager, COLOR_PICKER_DIALOG_TAG);
208                 }
209             }
210         });
211 
212         setText(view, R.id.calendar, name);
213         return view;
214     }
215 
hasMoreColors(int position)216     private boolean hasMoreColors(int position) {
217         return mCache.hasColors(mData[position].accountName, mData[position].accountType);
218     }
219 
setText(View view, int id, String text)220     private static void setText(View view, int id, String text) {
221         if (TextUtils.isEmpty(text)) {
222             return;
223         }
224         TextView textView = (TextView) view.findViewById(id);
225         textView.setText(text);
226     }
227 
228     @Override
getCount()229     public int getCount() {
230         return mRowCount;
231     }
232 
233     @Override
getItem(int position)234     public Object getItem(int position) {
235         if (position >= mRowCount) {
236             return null;
237         }
238         CalendarRow item = mData[position];
239         return item;
240     }
241 
242     @Override
getItemId(int position)243     public long getItemId(int position) {
244         if (position >= mRowCount) {
245             return 0;
246         }
247         return mData[position].id;
248     }
249 
250     @Override
hasStableIds()251     public boolean hasStableIds() {
252         return true;
253     }
254 
getSynced(int position)255     public int getSynced(int position) {
256         return mData[position].synced ? 1 : 0;
257     }
258 
259     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)260     public void onItemClick(AdapterView<?> parent, View view, int position, long id)  {
261         CalendarRow row = (CalendarRow) view.getTag();
262         row.synced = !row.synced;
263 
264         String status;
265         if (row.synced) {
266             status = mSyncedString;
267         } else {
268             status = mNotSyncedString;
269         }
270         setText(view, R.id.status, status);
271 
272         CheckBox cb = (CheckBox) view.findViewById(R.id.sync);
273         cb.setChecked(row.synced);
274 
275         // There is some data loss in long -> int, but we should never see it in
276         // practice regarding calendar ids.
277         mChanges.put(row.id, row);
278     }
279 
getChanges()280     public HashMap<Long, CalendarRow> getChanges() {
281         return mChanges;
282     }
283 
284     @Override
onCalendarColorsLoaded()285     public void onCalendarColorsLoaded() {
286         notifyDataSetChanged();
287     }
288 }
289