AgendaListView.java revision 9a39bddb8e519b710a06d1c77257de6962ae5f72
1/* 2 * Copyright (C) 2009 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.agenda; 18 19import com.android.calendar.CalendarController; 20import com.android.calendar.CalendarController.EventType; 21import com.android.calendar.DeleteEventHelper; 22import com.android.calendar.R; 23import com.android.calendar.Utils; 24import com.android.calendar.agenda.AgendaAdapter.ViewHolder; 25import com.android.calendar.agenda.AgendaWindowAdapter.DayAdapterInfo; 26import com.android.calendar.agenda.AgendaWindowAdapter.EventInfo; 27 28import android.content.Context; 29import android.graphics.Rect; 30import android.text.format.Time; 31import android.util.AttributeSet; 32import android.util.Log; 33import android.view.View; 34import android.widget.AdapterView; 35import android.widget.AdapterView.OnItemClickListener; 36import android.widget.ListView; 37import android.widget.TextView; 38 39public class AgendaListView extends ListView implements OnItemClickListener { 40 41 private static final String TAG = "AgendaListView"; 42 private static final boolean DEBUG = false; 43 44 private AgendaWindowAdapter mWindowAdapter; 45 private DeleteEventHelper mDeleteEventHelper; 46 private Context mContext; 47 private String mTimeZone; 48 private boolean mShowEventDetailsWithAgenda; 49 50 private Runnable mTZUpdater = new Runnable() { 51 @Override 52 public void run() { 53 mTimeZone = Utils.getTimeZone(mContext, this); 54 } 55 }; 56 57 public AgendaListView(Context context, AttributeSet attrs) { 58 super(context, attrs); 59 initView(context); 60 } 61 62 private void initView(Context context) { 63 mContext = context; 64 mTimeZone = Utils.getTimeZone(context, mTZUpdater); 65 setOnItemClickListener(this); 66 setChoiceMode(ListView.CHOICE_MODE_SINGLE); 67 setVerticalScrollBarEnabled(false); 68 mWindowAdapter = new AgendaWindowAdapter(context, this, 69 Utils.getConfigBool(context, R.bool.show_event_details_with_agenda)); 70 mWindowAdapter.setSelectedInstanceId(-1/* TODO:instanceId */); 71 setAdapter(mWindowAdapter); 72 setCacheColorHint(context.getResources().getColor(R.color.agenda_item_not_selected)); 73 mDeleteEventHelper = 74 new DeleteEventHelper(context, null, false /* don't exit when done */); 75 mShowEventDetailsWithAgenda = Utils.getConfigBool(mContext, 76 R.bool.show_event_details_with_agenda); 77 } 78 79 @Override 80 protected void onDetachedFromWindow() { 81 super.onDetachedFromWindow(); 82 mWindowAdapter.close(); 83 } 84 85 // Implementation of the interface OnItemClickListener 86 @Override 87 public void onItemClick(AdapterView<?> a, View v, int position, long id) { 88 if (id != -1) { 89 // Switch to the EventInfo view 90 EventInfo event = mWindowAdapter.getEventByPosition(position); 91 long oldInstanceId = mWindowAdapter.getSelectedInstanceId(); 92 mWindowAdapter.setSelectedView(v); 93 94 // If events are shown to the side of the agenda list , do nothing 95 // when the same 96 // event is selected , otherwise show the selected event. 97 98 if (event != null && (oldInstanceId != mWindowAdapter.getSelectedInstanceId() || 99 !mShowEventDetailsWithAgenda)) { 100 CalendarController.getInstance(mContext).sendEventRelatedEvent(this, 101 EventType.VIEW_EVENT, event.id, event.begin, event.end, 0, 0, -1); 102 } 103 } 104 } 105 106 public void goTo(Time time, long id, String searchQuery, boolean forced) { 107 if (time == null) { 108 time = new Time(mTimeZone); 109 long goToTime = getFirstVisibleTime(); 110 if (goToTime <= 0) { 111 goToTime = System.currentTimeMillis(); 112 } 113 time.set(goToTime); 114 } 115 mWindowAdapter.refresh(time, id, searchQuery, forced); 116 } 117 118 public void refresh(boolean forced) { 119 Time time = new Time(Utils.getTimeZone(mContext, null)); 120 long goToTime = getFirstVisibleTime(); 121 if (goToTime <= 0) { 122 goToTime = System.currentTimeMillis(); 123 } 124 time.set(goToTime); 125 mWindowAdapter.refresh(time, -1, null, forced); 126 } 127 128 public void deleteSelectedEvent() { 129 int position = getSelectedItemPosition(); 130 EventInfo event = mWindowAdapter.getEventByPosition(position); 131 if (event != null) { 132 mDeleteEventHelper.delete(event.begin, event.end, event.id, -1); 133 } 134 } 135 136 @Override 137 public int getFirstVisiblePosition() { 138 // TODO File bug! 139 // getFirstVisiblePosition doesn't always return the first visible 140 // item. Sometimes, it is above the visible one. 141 // instead. I loop through the viewgroup children and find the first 142 // visible one. BTW, getFirstVisiblePosition() == getChildAt(0). I 143 // am not looping through the entire list. 144 View v = getFirstVisibleView(); 145 if (v != null) { 146 if (DEBUG) { 147 Log.v(TAG, "getFirstVisiblePosition: " + AgendaWindowAdapter.getViewTitle(v)); 148 } 149 return getPositionForView(v); 150 } 151 return -1; 152 } 153 154 public View getFirstVisibleView() { 155 Rect r = new Rect(); 156 int childCount = getChildCount(); 157 for (int i = 0; i < childCount; ++i) { 158 View listItem = getChildAt(i); 159 listItem.getLocalVisibleRect(r); 160 if (r.top >= 0) { // if visible 161 return listItem; 162 } 163 } 164 return null; 165 } 166 167 public long getSelectedTime() { 168 int position = getSelectedItemPosition(); 169 if (position >= 0) { 170 EventInfo event = mWindowAdapter.getEventByPosition(position); 171 if (event != null) { 172 return event.begin; 173 } 174 } 175 return getFirstVisibleTime(); 176 } 177 178 public long getFirstVisibleTime() { 179 int position = getFirstVisiblePosition(); 180 if (DEBUG) { 181 Log.v(TAG, "getFirstVisiblePosition = " + position); 182 } 183 184 EventInfo event = mWindowAdapter.getEventByPosition(position); 185 if (event != null) { 186 Time t = new Time(mTimeZone); 187 t.set(event.begin); 188 t.setJulianDay(event.startDay); 189 return t.normalize(false); 190 } 191 return 0; 192 } 193 194 public int getJulianDayFromPosition(int position) { 195 DayAdapterInfo info = mWindowAdapter.getAdapterInfoByPosition(position); 196 if (info != null) { 197 return info.dayAdapter.findJulianDayFromPosition(position - info.offset); 198 } 199 return 0; 200 } 201 202 // Finds is a specific event (defined by start time and id) is visible 203 public boolean isEventVisible(Time startTime, long id) { 204 205 if (id == -1 || startTime == null) { 206 return false; 207 } 208 209 View child = getChildAt(0); 210 // View not set yet, so not child - return 211 if (child == null) { 212 return false; 213 } 214 int start = getPositionForView(child); 215 long milliTime = startTime.toMillis(true); 216 int childCount = getChildCount(); 217 int eventsInAdapter = mWindowAdapter.getCount(); 218 219 for (int i = 0; i < childCount; i++) { 220 if (i + start >= eventsInAdapter) { 221 break; 222 } 223 EventInfo event = mWindowAdapter.getEventByPosition(i + start); 224 if (event == null) { 225 continue; 226 } 227 if (event.id == id && event.begin == milliTime) { 228 View listItem = getChildAt(i); 229 if (listItem.getBottom() <= getHeight() && 230 listItem.getTop() >= 0) { 231 return true; 232 } 233 } 234 } 235 return false; 236 } 237 238 public long getSelectedInstanceId() { 239 return mWindowAdapter.getSelectedInstanceId(); 240 } 241 242 // Move the currently selected or visible focus down by offset amount. 243 // offset could be negative. 244 public void shiftSelection(int offset) { 245 shiftPosition(offset); 246 int position = getSelectedItemPosition(); 247 if (position != INVALID_POSITION) { 248 setSelectionFromTop(position + offset, 0); 249 } 250 } 251 252 private void shiftPosition(int offset) { 253 if (DEBUG) { 254 Log.v(TAG, "Shifting position " + offset); 255 } 256 257 View firstVisibleItem = getFirstVisibleView(); 258 259 if (firstVisibleItem != null) { 260 Rect r = new Rect(); 261 firstVisibleItem.getLocalVisibleRect(r); 262 // if r.top is < 0, getChildAt(0) and getFirstVisiblePosition() is 263 // returning an item above the first visible item. 264 int position = getPositionForView(firstVisibleItem); 265 setSelectionFromTop(position + offset, r.top > 0 ? -r.top : r.top); 266 if (DEBUG) { 267 if (firstVisibleItem.getTag() instanceof AgendaAdapter.ViewHolder) { 268 ViewHolder viewHolder = (AgendaAdapter.ViewHolder) firstVisibleItem.getTag(); 269 Log.v(TAG, "Shifting from " + position + " by " + offset + ". Title " 270 + viewHolder.title.getText()); 271 } else if (firstVisibleItem.getTag() instanceof AgendaByDayAdapter.ViewHolder) { 272 AgendaByDayAdapter.ViewHolder viewHolder = 273 (AgendaByDayAdapter.ViewHolder) firstVisibleItem.getTag(); 274 Log.v(TAG, "Shifting from " + position + " by " + offset + ". Date " 275 + viewHolder.dateView.getText()); 276 } else if (firstVisibleItem instanceof TextView) { 277 Log.v(TAG, "Shifting: Looking at header here. " + getSelectedItemPosition()); 278 } 279 } 280 } else if (getSelectedItemPosition() >= 0) { 281 if (DEBUG) { 282 Log.v(TAG, "Shifting selection from " + getSelectedItemPosition() + 283 " by " + offset); 284 } 285 setSelection(getSelectedItemPosition() + offset); 286 } 287 } 288 289 public void setHideDeclinedEvents(boolean hideDeclined) { 290 mWindowAdapter.setHideDeclinedEvents(hideDeclined); 291 } 292 293 public void onResume() { 294 mTZUpdater.run(); 295 mWindowAdapter.onResume(); 296 } 297 298 public void onPause() { 299 mWindowAdapter.notifyDataSetInvalidated(); 300 } 301} 302