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