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 17 package com.android.calendar; 18 19 import com.android.calendar.AgendaAdapter.ViewHolder; 20 import com.android.calendar.AgendaWindowAdapter.EventInfo; 21 22 import android.content.ContentUris; 23 import android.content.Intent; 24 import android.graphics.Rect; 25 import android.net.Uri; 26 import android.provider.Calendar; 27 import android.provider.Calendar.Events; 28 import android.text.format.Time; 29 import android.util.Log; 30 import android.view.View; 31 import android.view.accessibility.AccessibilityEvent; 32 import android.widget.AdapterView; 33 import android.widget.AdapterView.OnItemClickListener; 34 import android.widget.ListView; 35 import android.widget.TextView; 36 37 public class AgendaListView extends ListView implements OnItemClickListener { 38 39 private static final String TAG = "AgendaListView"; 40 private static final boolean DEBUG = false; 41 42 private AgendaWindowAdapter mWindowAdapter; 43 44 private AgendaActivity mAgendaActivity; 45 private DeleteEventHelper mDeleteEventHelper; 46 47 /** 48 * Reusable view to fetch the day title for accessibility support with no 49 * code duplication reducing fragility. 50 */ 51 private View mLazyTempView; 52 53 // Placeholder if we need some code for updating the tz later. 54 private Runnable mUpdateTZ = null; 55 56 public AgendaListView(AgendaActivity agendaActivity) { 57 super(agendaActivity, null); 58 mAgendaActivity = agendaActivity; 59 60 setOnItemClickListener(this); 61 setChoiceMode(ListView.CHOICE_MODE_SINGLE); 62 setVerticalScrollBarEnabled(false); 63 mWindowAdapter = new AgendaWindowAdapter(agendaActivity, this); 64 setAdapter(mWindowAdapter); 65 mDeleteEventHelper = 66 new DeleteEventHelper(agendaActivity, false /* don't exit when done */); 67 } 68 69 @Override protected void onDetachedFromWindow() { 70 super.onDetachedFromWindow(); 71 mWindowAdapter.close(); 72 } 73 74 @Override 75 public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 76 int titlePosition = 0 ; 77 AgendaByDayAdapter.RowInfo rowInfo = null; 78 // find the index of the item with day title 79 for (titlePosition = getSelectedItemPosition() - 1; titlePosition >= 0; titlePosition--) { 80 Object previousItem = mWindowAdapter.getItem(titlePosition); 81 if (previousItem instanceof AgendaByDayAdapter.RowInfo) { 82 break; 83 } 84 } 85 // append the day title 86 mLazyTempView = mWindowAdapter.getView(titlePosition, mLazyTempView, null); 87 if (mLazyTempView != null && mLazyTempView instanceof TextView) { 88 TextView weekDayTitleView = (TextView) mLazyTempView; 89 CharSequence weekDayTitleViewText = weekDayTitleView.getText(); 90 event.getText().add(weekDayTitleViewText); 91 } 92 return super.dispatchPopulateAccessibilityEvent(event); 93 } 94 95 // Implementation of the interface OnItemClickListener 96 public void onItemClick(AdapterView<?> a, View v, int position, long id) { 97 if (id != -1) { 98 // Switch to the EventInfo view 99 EventInfo event = mWindowAdapter.getEventByPosition(position); 100 if (event != null) { 101 Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id); 102 Intent intent = new Intent(Intent.ACTION_VIEW, uri); 103 intent.putExtra(Calendar.EVENT_BEGIN_TIME, event.begin); 104 intent.putExtra(Calendar.EVENT_END_TIME, event.end); 105 mAgendaActivity.startActivity(intent); 106 } 107 } 108 } 109 110 public void goTo(Time time, boolean forced) { 111 mWindowAdapter.refresh(time, forced); 112 } 113 114 public void refresh(boolean forced) { 115 Time time = new Time(Utils.getTimeZone(mContext, mUpdateTZ)); 116 long goToTime = getFirstVisibleTime(); 117 if (goToTime <= 0) { 118 goToTime = System.currentTimeMillis(); 119 } 120 time.set(goToTime); 121 mWindowAdapter.refresh(time, forced); 122 } 123 124 public void deleteSelectedEvent() { 125 int position = getSelectedItemPosition(); 126 EventInfo event = mWindowAdapter.getEventByPosition(position); 127 if (event != null) { 128 mDeleteEventHelper.delete(event.begin, event.end, event.id, -1); 129 } 130 } 131 132 @Override 133 public int getFirstVisiblePosition() { 134 // TODO File bug! 135 // getFirstVisiblePosition doesn't always return the first visible 136 // item. Sometimes, it is above the visible one. 137 // instead. I loop through the viewgroup children and find the first 138 // visible one. BTW, getFirstVisiblePosition() == getChildAt(0). I 139 // am not looping through the entire list. 140 View v = getFirstVisibleView(); 141 if (v != null) { 142 if (DEBUG) { 143 Log.v(TAG, "getFirstVisiblePosition: " + AgendaWindowAdapter.getViewTitle(v)); 144 } 145 return getPositionForView(v); 146 } 147 return -1; 148 } 149 150 public View getFirstVisibleView() { 151 Rect r = new Rect(); 152 int childCount = getChildCount(); 153 for (int i = 0; i < childCount; ++i) { 154 View listItem = getChildAt(i); 155 listItem.getLocalVisibleRect(r); 156 if (r.top >= 0) { // if visible 157 return listItem; 158 } 159 } 160 return null; 161 } 162 163 public long getSelectedTime() { 164 int position = getSelectedItemPosition(); 165 if (position >= 0) { 166 EventInfo event = mWindowAdapter.getEventByPosition(position); 167 if (event != null) { 168 return event.begin; 169 } 170 } 171 return getFirstVisibleTime(); 172 } 173 174 public long getFirstVisibleTime() { 175 int position = getFirstVisiblePosition(); 176 if (DEBUG) { 177 Log.v(TAG, "getFirstVisiblePosition = " + position); 178 } 179 180 EventInfo event = mWindowAdapter.getEventByPosition(position); 181 if (event != null) { 182 return event.begin; 183 } 184 return 0; 185 } 186 187 // Move the currently selected or visible focus down by offset amount. 188 // offset could be negative. 189 public void shiftSelection(int offset) { 190 shiftPosition(offset); 191 int position = getSelectedItemPosition(); 192 if (position != INVALID_POSITION) { 193 setSelectionFromTop(position + offset, 0); 194 } 195 } 196 197 private void shiftPosition(int offset) { 198 if (DEBUG) { 199 Log.v(TAG, "Shifting position "+ offset); 200 } 201 202 View firstVisibleItem = getFirstVisibleView(); 203 204 if (firstVisibleItem != null) { 205 Rect r = new Rect(); 206 firstVisibleItem.getLocalVisibleRect(r); 207 // if r.top is < 0, getChildAt(0) and getFirstVisiblePosition() is 208 // returning an item above the first visible item. 209 int position = getPositionForView(firstVisibleItem); 210 setSelectionFromTop(position + offset, r.top > 0 ? -r.top : r.top); 211 if (DEBUG) { 212 if (firstVisibleItem.getTag() instanceof AgendaAdapter.ViewHolder) { 213 ViewHolder viewHolder = (AgendaAdapter.ViewHolder)firstVisibleItem.getTag(); 214 Log.v(TAG, "Shifting from " + position + " by " + offset + ". Title " 215 + viewHolder.title.getText()); 216 } else if (firstVisibleItem.getTag() instanceof AgendaByDayAdapter.ViewHolder) { 217 AgendaByDayAdapter.ViewHolder viewHolder = 218 (AgendaByDayAdapter.ViewHolder)firstVisibleItem.getTag(); 219 Log.v(TAG, "Shifting from " + position + " by " + offset + ". Date " 220 + viewHolder.dateView.getText()); 221 } else if (firstVisibleItem instanceof TextView) { 222 Log.v(TAG, "Shifting: Looking at header here. " + getSelectedItemPosition()); 223 } 224 } 225 } else if (getSelectedItemPosition() >= 0) { 226 if (DEBUG) { 227 Log.v(TAG, "Shifting selection from " + getSelectedItemPosition() + " by " + offset); 228 } 229 setSelection(getSelectedItemPosition() + offset); 230 } 231 } 232 233 public void setHideDeclinedEvents(boolean hideDeclined) { 234 mWindowAdapter.setHideDeclinedEvents(hideDeclined); 235 } 236 237 public void onResume() { 238 mWindowAdapter.notifyDataSetChanged(); 239 } 240 public void onPause() { 241 mWindowAdapter.notifyDataSetInvalidated(); 242 } 243 } 244