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.Configuration; 22 import android.content.res.Resources; 23 import android.database.Cursor; 24 import android.graphics.Rect; 25 import android.graphics.drawable.Drawable; 26 import android.provider.CalendarContract.Calendars; 27 import android.text.TextUtils; 28 import android.view.LayoutInflater; 29 import android.view.TouchDelegate; 30 import android.view.View; 31 import android.view.View.OnClickListener; 32 import android.view.ViewGroup; 33 import android.view.ViewGroup.LayoutParams; 34 import android.widget.BaseAdapter; 35 import android.widget.CheckBox; 36 import android.widget.ListAdapter; 37 import android.widget.TextView; 38 39 import com.android.calendar.CalendarColorPickerDialog; 40 import com.android.calendar.R; 41 import com.android.calendar.Utils; 42 import com.android.calendar.selectcalendars.CalendarColorCache.OnCalendarColorsLoadedListener; 43 44 public class SelectCalendarsSimpleAdapter extends BaseAdapter implements ListAdapter, 45 OnCalendarColorsLoadedListener { 46 private static final String TAG = "SelectCalendarsAdapter"; 47 private static final String COLOR_PICKER_DIALOG_TAG = "ColorPickerDialog"; 48 49 private static int BOTTOM_ITEM_HEIGHT = 64; 50 private static int NORMAL_ITEM_HEIGHT = 48; 51 52 private static final int IS_SELECTED = 1 << 0; 53 private static final int IS_TOP = 1 << 1; 54 private static final int IS_BOTTOM = 1 << 2; 55 private static final int IS_BELOW_SELECTED = 1 << 3; 56 57 private CalendarColorPickerDialog mColorPickerDialog; 58 59 private LayoutInflater mInflater; 60 Resources mRes; 61 private int mLayout; 62 private int mOrientation; 63 private CalendarRow[] mData; 64 private Cursor mCursor; 65 private int mRowCount = 0; 66 67 private FragmentManager mFragmentManager; 68 private boolean mIsTablet; 69 private int mColorViewTouchAreaIncrease; 70 71 private int mIdColumn; 72 private int mNameColumn; 73 private int mColorColumn; 74 private int mVisibleColumn; 75 private int mOwnerAccountColumn; 76 private int mAccountNameColumn; 77 private int mAccountTypeColumn; 78 private static float mScale = 0; 79 private int mColorCalendarVisible; 80 private int mColorCalendarHidden; 81 private int mColorCalendarSecondaryVisible; 82 private int mColorCalendarSecondaryHidden; 83 84 private CalendarColorCache mCache; 85 86 private class CalendarRow { 87 long id; 88 String displayName; 89 String ownerAccount; 90 String accountName; 91 String accountType; 92 int color; 93 boolean selected; 94 } 95 96 public SelectCalendarsSimpleAdapter(Context context, int layout, Cursor c, FragmentManager fm) { 97 super(); 98 mLayout = layout; 99 mOrientation = context.getResources().getConfiguration().orientation; 100 initData(c); 101 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 102 mRes = context.getResources(); 103 mColorCalendarVisible = mRes.getColor(R.color.calendar_visible); 104 mColorCalendarHidden = mRes.getColor(R.color.calendar_hidden); 105 mColorCalendarSecondaryVisible = mRes.getColor(R.color.calendar_secondary_visible); 106 mColorCalendarSecondaryHidden = mRes.getColor(R.color.calendar_secondary_hidden); 107 108 if (mScale == 0) { 109 mScale = mRes.getDisplayMetrics().density; 110 BOTTOM_ITEM_HEIGHT *= mScale; 111 NORMAL_ITEM_HEIGHT *= mScale; 112 } 113 114 mCache = new CalendarColorCache(context, this); 115 116 mFragmentManager = fm; 117 mColorPickerDialog = (CalendarColorPickerDialog) 118 fm.findFragmentByTag(COLOR_PICKER_DIALOG_TAG); 119 mIsTablet = Utils.getConfigBool(context, R.bool.tablet_config); 120 mColorViewTouchAreaIncrease = context.getResources() 121 .getDimensionPixelSize(R.dimen.color_view_touch_area_increase); 122 } 123 124 private static class TabletCalendarItemBackgrounds { 125 static private int[] mBackgrounds = null; 126 127 /** 128 * Sets up the background drawables for the calendars list 129 * 130 * @param res The context's resources 131 */ 132 static int[] getBackgrounds() { 133 // Not thread safe. Ok if called only from main thread 134 if (mBackgrounds != null) { 135 return mBackgrounds; 136 } 137 138 mBackgrounds = new int[16]; 139 140 mBackgrounds[0] = R.drawable.calname_unselected; 141 142 mBackgrounds[IS_SELECTED] = R.drawable.calname_select_underunselected; 143 144 mBackgrounds[IS_SELECTED | IS_BOTTOM] = 145 R.drawable.calname_bottom_select_underunselected; 146 147 mBackgrounds[IS_SELECTED | IS_BOTTOM | IS_BELOW_SELECTED] = 148 R.drawable.calname_bottom_select_underselect; 149 mBackgrounds[IS_SELECTED | IS_TOP | IS_BOTTOM | IS_BELOW_SELECTED] = mBackgrounds[ 150 IS_SELECTED | IS_BOTTOM | IS_BELOW_SELECTED]; 151 mBackgrounds[IS_SELECTED | IS_TOP | IS_BOTTOM] = mBackgrounds[IS_SELECTED | IS_BOTTOM 152 | IS_BELOW_SELECTED]; 153 154 mBackgrounds[IS_SELECTED | IS_BELOW_SELECTED] = R.drawable.calname_select_underselect; 155 mBackgrounds[IS_SELECTED | IS_TOP | IS_BELOW_SELECTED] = mBackgrounds[IS_SELECTED 156 | IS_BELOW_SELECTED]; 157 mBackgrounds[IS_SELECTED | IS_TOP] = mBackgrounds[IS_SELECTED | IS_BELOW_SELECTED]; 158 159 mBackgrounds[IS_BOTTOM] = R.drawable.calname_bottom_unselected; 160 161 mBackgrounds[IS_BOTTOM | IS_BELOW_SELECTED] = 162 R.drawable.calname_bottom_unselected_underselect; 163 mBackgrounds[IS_TOP | IS_BOTTOM | IS_BELOW_SELECTED] = mBackgrounds[IS_BOTTOM 164 | IS_BELOW_SELECTED]; 165 mBackgrounds[IS_TOP | IS_BOTTOM] = mBackgrounds[IS_BOTTOM | IS_BELOW_SELECTED]; 166 167 mBackgrounds[IS_BELOW_SELECTED] = R.drawable.calname_unselected_underselect; 168 mBackgrounds[IS_TOP | IS_BELOW_SELECTED] = mBackgrounds[IS_BELOW_SELECTED]; 169 mBackgrounds[IS_TOP] = mBackgrounds[IS_BELOW_SELECTED]; 170 return mBackgrounds; 171 } 172 } 173 174 private void initData(Cursor c) { 175 if (mCursor != null && c != mCursor) { 176 mCursor.close(); 177 } 178 if (c == null) { 179 mCursor = c; 180 mRowCount = 0; 181 mData = null; 182 return; 183 } 184 // TODO create a broadcast listener for ACTION_PROVIDER_CHANGED to update the cursor 185 mCursor = c; 186 mIdColumn = c.getColumnIndexOrThrow(Calendars._ID); 187 mNameColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_DISPLAY_NAME); 188 mColorColumn = c.getColumnIndexOrThrow(Calendars.CALENDAR_COLOR); 189 mVisibleColumn = c.getColumnIndexOrThrow(Calendars.VISIBLE); 190 mOwnerAccountColumn = c.getColumnIndexOrThrow(Calendars.OWNER_ACCOUNT); 191 mAccountNameColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_NAME); 192 mAccountTypeColumn = c.getColumnIndexOrThrow(Calendars.ACCOUNT_TYPE); 193 194 mRowCount = c.getCount(); 195 mData = new CalendarRow[(c.getCount())]; 196 c.moveToPosition(-1); 197 int p = 0; 198 while (c.moveToNext()) { 199 mData[p] = new CalendarRow(); 200 mData[p].id = c.getLong(mIdColumn); 201 mData[p].displayName = c.getString(mNameColumn); 202 mData[p].color = c.getInt(mColorColumn); 203 mData[p].selected = c.getInt(mVisibleColumn) != 0; 204 mData[p].ownerAccount = c.getString(mOwnerAccountColumn); 205 mData[p].accountName = c.getString(mAccountNameColumn); 206 mData[p].accountType = c.getString(mAccountTypeColumn); 207 p++; 208 } 209 } 210 211 public void changeCursor(Cursor c) { 212 initData(c); 213 notifyDataSetChanged(); 214 } 215 216 @Override 217 public View getView(final int position, View convertView, ViewGroup parent) { 218 if (position >= mRowCount) { 219 return null; 220 } 221 String name = mData[position].displayName; 222 boolean selected = mData[position].selected; 223 224 int color = Utils.getDisplayColorFromColor(mData[position].color); 225 View view; 226 if (convertView == null) { 227 view = mInflater.inflate(mLayout, parent, false); 228 final View delegate = view.findViewById(R.id.color); 229 final View delegateParent = (View) delegate.getParent(); 230 delegateParent.post(new Runnable() { 231 232 @Override 233 public void run() { 234 final Rect r = new Rect(); 235 delegate.getHitRect(r); 236 r.top -= mColorViewTouchAreaIncrease; 237 r.bottom += mColorViewTouchAreaIncrease; 238 r.left -= mColorViewTouchAreaIncrease; 239 r.right += mColorViewTouchAreaIncrease; 240 delegateParent.setTouchDelegate(new TouchDelegate(r, delegate)); 241 } 242 }); 243 } else { 244 view = convertView; 245 } 246 247 TextView calendarName = (TextView) view.findViewById(R.id.calendar); 248 calendarName.setText(name); 249 250 View colorView = view.findViewById(R.id.color); 251 colorView.setBackgroundColor(color); 252 colorView.setOnClickListener(new OnClickListener() { 253 @Override 254 public void onClick(View v) { 255 // Purely for sanity check--view should be disabled if account has no more colors 256 if (!hasMoreColors(position)) { 257 return; 258 } 259 260 if (mColorPickerDialog == null) { 261 mColorPickerDialog = CalendarColorPickerDialog.newInstance(mData[position].id, 262 mIsTablet); 263 } else { 264 mColorPickerDialog.setCalendarId(mData[position].id); 265 } 266 mFragmentManager.executePendingTransactions(); 267 if (!mColorPickerDialog.isAdded()) { 268 mColorPickerDialog.show(mFragmentManager, COLOR_PICKER_DIALOG_TAG); 269 } 270 } 271 }); 272 273 int textColor; 274 if (selected) { 275 textColor = mColorCalendarVisible; 276 } else { 277 textColor = mColorCalendarHidden; 278 } 279 calendarName.setTextColor(textColor); 280 281 CheckBox syncCheckBox = (CheckBox) view.findViewById(R.id.sync); 282 if (syncCheckBox != null) { 283 284 // Full screen layout 285 syncCheckBox.setChecked(selected); 286 287 colorView.setEnabled(hasMoreColors(position)); 288 LayoutParams layoutParam = calendarName.getLayoutParams(); 289 TextView secondaryText = (TextView) view.findViewById(R.id.status); 290 if (!TextUtils.isEmpty(mData[position].ownerAccount) 291 && !mData[position].ownerAccount.equals(name) 292 && !mData[position].ownerAccount.endsWith("calendar.google.com")) { 293 int secondaryColor; 294 if (selected) { 295 secondaryColor = mColorCalendarSecondaryVisible; 296 } else { 297 secondaryColor = mColorCalendarSecondaryHidden; 298 } 299 secondaryText.setText(mData[position].ownerAccount); 300 secondaryText.setTextColor(secondaryColor); 301 secondaryText.setVisibility(View.VISIBLE); 302 layoutParam.height = LayoutParams.WRAP_CONTENT; 303 } else { 304 secondaryText.setVisibility(View.GONE); 305 layoutParam.height = LayoutParams.MATCH_PARENT; 306 } 307 308 calendarName.setLayoutParams(layoutParam); 309 310 } else { 311 // Tablet layout 312 view.findViewById(R.id.color).setEnabled(selected && hasMoreColors(position)); 313 view.setBackgroundDrawable(getBackground(position, selected)); 314 ViewGroup.LayoutParams newParams = view.getLayoutParams(); 315 if (position == mData.length - 1) { 316 newParams.height = BOTTOM_ITEM_HEIGHT; 317 } else { 318 newParams.height = NORMAL_ITEM_HEIGHT; 319 } 320 view.setLayoutParams(newParams); 321 CheckBox visibleCheckBox = (CheckBox) view.findViewById(R.id.visible_check_box); 322 if (visibleCheckBox != null) { 323 visibleCheckBox.setChecked(selected); 324 } 325 } 326 view.invalidate(); 327 return view; 328 } 329 330 private boolean hasMoreColors(int position) { 331 return mCache.hasColors(mData[position].accountName, mData[position].accountType); 332 } 333 334 /** 335 * @param position position of the calendar item 336 * @param selected whether it is selected or not 337 * @return the drawable to use for this view 338 */ 339 protected Drawable getBackground(int position, boolean selected) { 340 int bg; 341 bg = selected ? IS_SELECTED : 0; 342 bg |= (position == 0 && mOrientation == Configuration.ORIENTATION_LANDSCAPE) ? IS_TOP : 0; 343 bg |= position == mData.length - 1 ? IS_BOTTOM : 0; 344 bg |= (position > 0 && mData[position - 1].selected) ? IS_BELOW_SELECTED : 0; 345 return mRes.getDrawable(TabletCalendarItemBackgrounds.getBackgrounds()[bg]); 346 } 347 348 @Override 349 public int getCount() { 350 return mRowCount; 351 } 352 353 @Override 354 public Object getItem(int position) { 355 if (position >= mRowCount) { 356 return null; 357 } 358 CalendarRow item = mData[position]; 359 return item; 360 } 361 362 @Override 363 public long getItemId(int position) { 364 if (position >= mRowCount) { 365 return 0; 366 } 367 return mData[position].id; 368 } 369 370 public void setVisible(int position, int visible) { 371 mData[position].selected = visible != 0; 372 notifyDataSetChanged(); 373 } 374 375 public int getVisible(int position) { 376 return mData[position].selected ? 1 : 0; 377 } 378 379 @Override 380 public boolean hasStableIds() { 381 return true; 382 } 383 384 @Override 385 public void onCalendarColorsLoaded() { 386 notifyDataSetChanged(); 387 } 388 } 389