Fix agenda list view location
Changed AgendaWindowAdapter to move to a specific time/event id instead
of moving to the day of the event.
Stop fling motion when the today button is pressed.
Bug 5424040 ICS: On opening, Agenda view should scroll to
nearest upcoming or in progress event
5547705 AgendaListView keeps scrolling after pressing
the "today" button.
Change-Id: I8982b4d9254a077e83df48c857c418063b9ea691
diff --git a/src/com/android/calendar/StickyHeaderListView.java b/src/com/android/calendar/StickyHeaderListView.java
index 7a691ec..25955a0 100644
--- a/src/com/android/calendar/StickyHeaderListView.java
+++ b/src/com/android/calendar/StickyHeaderListView.java
@@ -56,6 +56,7 @@
protected Context mContext = null;
protected Adapter mAdapter = null;
protected HeaderIndexer mIndexer = null;
+ protected HeaderHeightListener mHeaderHeightListener = null;
protected View mStickyHeader = null;
protected View mDummyHeader = null; // A invisible header used when a section has no header
protected ListView mListView = null;
@@ -63,6 +64,7 @@
private int mSeparatorWidth;
private View mSeparatorView;
+ private int mLastStickyHeaderHeight = 0;
// This code is needed only if dataset changes do not force a call to OnScroll
// protected DataSetObserver mListDataObserver = null;
@@ -103,6 +105,21 @@
int getHeaderItemsNumber(int headerPosition);
}
+ /***
+ *
+ * Interface that is used to update the sticky header's height
+ *
+ */
+ public interface HeaderHeightListener {
+
+ /***
+ * Updated a change in the sticky header's size
+ *
+ * @param height - new height of sticky header
+ */
+ void OnHeaderHeightChanged(int height);
+ }
+
/**
* Sets the adapter to be used by the class to get views of headers
*
@@ -158,6 +175,10 @@
mListener = listener;
}
+ public void setHeaderHeightListener(HeaderHeightListener listener) {
+ mHeaderHeightListener = listener;
+ }
+
// This code is needed only if dataset changes do not force a call to OnScroll
// protected void createDataListener() {
// mListDataObserver = new DataSetObserver() {
@@ -282,6 +303,14 @@
if (stickyHeaderHeight == 0) {
stickyHeaderHeight = mStickyHeader.getMeasuredHeight();
}
+
+ // Update new header height
+ if (mHeaderHeightListener != null &&
+ mLastStickyHeaderHeight != stickyHeaderHeight) {
+ mLastStickyHeaderHeight = stickyHeaderHeight;
+ mHeaderHeightListener.OnHeaderHeightChanged(stickyHeaderHeight);
+ }
+
View SectionLastView = mListView.getChildAt(sectionLastItemPosition);
if (SectionLastView != null && SectionLastView.getBottom() <= stickyHeaderHeight) {
int lastViewBottom = SectionLastView.getBottom();
diff --git a/src/com/android/calendar/Utils.java b/src/com/android/calendar/Utils.java
index 5f8c42e..e3880ca 100644
--- a/src/com/android/calendar/Utils.java
+++ b/src/com/android/calendar/Utils.java
@@ -526,6 +526,26 @@
}
/**
+ * Finds and returns the next midnight after "theTime" in milliseconds UTC
+ *
+ * @param recycle - Time object to recycle, otherwise null.
+ * @param theTime - Time used for calculations (in UTC)
+ * @param tz The time zone to convert this time to.
+ */
+ public static long getNextMidnight(Time recycle, long theTime, String tz) {
+ if (recycle == null) {
+ recycle = new Time();
+ }
+ recycle.timezone = tz;
+ recycle.set(theTime);
+ recycle.monthDay ++;
+ recycle.hour = 0;
+ recycle.minute = 0;
+ recycle.second = 0;
+ return recycle.normalize(true);
+ }
+
+ /**
* Scan through a cursor of calendars and check if names are duplicated.
* This travels a cursor containing calendar display names and fills in the
* provided map with whether or not each name is repeated.
diff --git a/src/com/android/calendar/agenda/AgendaByDayAdapter.java b/src/com/android/calendar/agenda/AgendaByDayAdapter.java
index a7aec99..ef71ae9 100644
--- a/src/com/android/calendar/agenda/AgendaByDayAdapter.java
+++ b/src/com/android/calendar/agenda/AgendaByDayAdapter.java
@@ -80,6 +80,12 @@
mTmpTime = new Time(mTimeZone);
}
+ public long getInstanceId(int position) {
+ if (mRowInfo == null || position >= mRowInfo.size()) {
+ return -1;
+ }
+ return mRowInfo.get(position).mInstanceId;
+ }
// Returns the position of a header of a specific item
public int getHeaderPosition(int position) {
@@ -278,21 +284,31 @@
Cursor cursor = dayAdapterInfo.cursor;
ArrayList<RowInfo> rowInfo = new ArrayList<RowInfo>();
int prevStartDay = -1;
- Time time = new Time(mTimeZone);
+
+ Time tempTime = new Time(mTimeZone);
long now = System.currentTimeMillis();
- time.set(now);
- mTodayJulianDay = Time.getJulianDay(now, time.gmtoff);
+ tempTime.set(now);
+ mTodayJulianDay = Time.getJulianDay(now, tempTime.gmtoff);
+
LinkedList<MultipleDayInfo> multipleDayList = new LinkedList<MultipleDayInfo>();
for (int position = 0; cursor.moveToNext(); position++) {
int startDay = cursor.getInt(AgendaWindowAdapter.INDEX_START_DAY);
-
+ long id = cursor.getLong(AgendaWindowAdapter.INDEX_EVENT_ID);
+ long startTime = cursor.getLong(AgendaWindowAdapter.INDEX_BEGIN);
+ long endTime = cursor.getLong(AgendaWindowAdapter.INDEX_END);
+ long instanceId = cursor.getLong(AgendaWindowAdapter.INDEX_INSTANCE_ID);
+ boolean allDay = cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
+ if (allDay) {
+ startTime = Utils.convertAlldayUtcToLocal(tempTime, startTime, mTimeZone);
+ endTime = Utils.convertAlldayUtcToLocal(tempTime, endTime, mTimeZone);
+ }
// Skip over the days outside of the adapter's range
startDay = Math.max(startDay, dayAdapterInfo.start);
if (startDay != prevStartDay) {
// Check if we skipped over any empty days
if (prevStartDay == -1) {
- rowInfo.add(new RowInfo(TYPE_DAY, startDay, 0));
+ rowInfo.add(new RowInfo(TYPE_DAY, startDay));
} else {
// If there are any multiple-day events that span the empty
// range of days, then create day headers and events for
@@ -313,24 +329,34 @@
// If this is the first event for the day, then
// insert a day header.
if (!dayHeaderAdded) {
- rowInfo.add(new RowInfo(TYPE_DAY, currentDay, 0));
+ rowInfo.add(new RowInfo(TYPE_DAY, currentDay));
dayHeaderAdded = true;
}
- rowInfo.add(new RowInfo(TYPE_MEETING, currentDay, info.mPosition));
+ long infoEndTime =
+ ((info.mEndDay == currentDay) ? info.mEventEndTimeMilli : Utils
+ .getNextMidnight(tempTime, info.mEventStartTimeMilli,
+ mTimeZone));
+ rowInfo.add(new RowInfo(TYPE_MEETING, currentDay, info.mPosition,
+ info.mEventId, info.mEventStartTimeMilli,
+ infoEndTime, info.mInstanceId, info.mAllDay));
+
+ info.mEventStartTimeMilli = Utils.getNextMidnight(tempTime,
+ info.mEventStartTimeMilli, mTimeZone);
}
}
// If the day header was not added for the start day, then
// add it now.
if (!dayHeaderAdded) {
- rowInfo.add(new RowInfo(TYPE_DAY, startDay, 0));
+ rowInfo.add(new RowInfo(TYPE_DAY, startDay));
}
}
prevStartDay = startDay;
}
// Add in the event for this cursor position
- rowInfo.add(new RowInfo(TYPE_MEETING, startDay, position));
+ rowInfo.add(new RowInfo(TYPE_MEETING, startDay, position, id, startTime, endTime,
+ instanceId, allDay));
// If this event spans multiple days, then add it to the multipleDay
// list.
@@ -339,7 +365,9 @@
// Skip over the days outside of the adapter's range
endDay = Math.min(endDay, dayAdapterInfo.end);
if (endDay > startDay) {
- multipleDayList.add(new MultipleDayInfo(position, endDay));
+ multipleDayList.add(new MultipleDayInfo(position, endDay, id,
+ Utils.getNextMidnight(tempTime, startTime, mTimeZone),
+ endTime, instanceId, allDay));
}
}
@@ -362,10 +390,18 @@
// If this is the first event for the day, then
// insert a day header.
if (!dayHeaderAdded) {
- rowInfo.add(new RowInfo(TYPE_DAY, currentDay, 0));
+ rowInfo.add(new RowInfo(TYPE_DAY, currentDay));
dayHeaderAdded = true;
}
- rowInfo.add(new RowInfo(TYPE_MEETING, currentDay, info.mPosition));
+ long nextMidnight = Utils.getNextMidnight(tempTime, info.mEventStartTimeMilli,
+ mTimeZone);
+ long infoEndTime =
+ (info.mEndDay == currentDay) ? info.mEventEndTimeMilli : nextMidnight;
+ rowInfo.add(new RowInfo(TYPE_MEETING, currentDay, info.mPosition,
+ info.mEventId, info.mEventStartTimeMilli, infoEndTime,
+ info.mInstanceId, info.mAllDay));
+
+ info.mEventStartTimeMilli = nextMidnight;
}
}
}
@@ -382,63 +418,146 @@
// or later. This flag is used by the adapter to create a view with a visual separator
// between the past and the present/future
boolean mFirstDayAfterYesterday;
+ final long mEventId;
+ final long mEventStartTimeMilli;
+ final long mEventEndTimeMilli;
+ final long mInstanceId;
+ final boolean mAllDay;
- RowInfo(int type, int julianDay, int position) {
+ RowInfo(int type, int julianDay, int position, long id, long startTime, long endTime,
+ long instanceId, boolean allDay) {
mType = type;
mDay = julianDay;
mPosition = position;
+ mEventId = id;
+ mEventStartTimeMilli = startTime;
+ mEventEndTimeMilli = endTime;
mFirstDayAfterYesterday = false;
+ mInstanceId = instanceId;
+ mAllDay = allDay;
+ }
+
+ RowInfo(int type, int julianDay) {
+ mType = type;
+ mDay = julianDay;
+ mPosition = 0;
+ mEventId = 0;
+ mEventStartTimeMilli = 0;
+ mEventEndTimeMilli = 0;
+ mFirstDayAfterYesterday = false;
+ mInstanceId = -1;
+ mAllDay = false;
}
}
private static class MultipleDayInfo {
final int mPosition;
final int mEndDay;
+ final long mEventId;
+ long mEventStartTimeMilli;
+ long mEventEndTimeMilli;
+ final long mInstanceId;
+ final boolean mAllDay;
- MultipleDayInfo(int position, int endDay) {
+ MultipleDayInfo(int position, int endDay, long id, long startTime, long endTime,
+ long instanceId, boolean allDay) {
mPosition = position;
mEndDay = endDay;
+ mEventId = id;
+ mEventStartTimeMilli = startTime;
+ mEventEndTimeMilli = endTime;
+ mInstanceId = instanceId;
+ mAllDay = allDay;
}
}
/**
- * Searches for the day that matches the given Time object and returns the
- * list position of that day. If there are no events for that day, then it
- * finds the nearest day (before or after) that has events and returns the
- * list position for that day.
- *
- * @param time the date to search for
- * @return the cursor position of the first event for that date, or zero
- * if no match was found
+ * Finds the position in the cursor of the event that best matches the time and Id.
+ * It will try to find the event that has the specified id and start time, if such event
+ * doesn't exist, it will return the event with a matching id that is closest to the start time.
+ * If the id doesn't exist, it will return the event with start time closest to the specified
+ * time.
+ * @param time - start of event in milliseconds (or any arbitrary time if event id is unknown)
+ * @param id - Event id (-1 if unknown).
+ * @return Position of event (if found) or position of nearest event according to the time.
+ * Zero if no event found
*/
- public int findDayPositionNearestTime(Time time) {
+ public int findEventPositionNearestTime(Time time, long id) {
if (mRowInfo == null) {
return 0;
}
long millis = time.toMillis(false /* use isDst */);
- int julianDay = Time.getJulianDay(millis, time.gmtoff);
- int minDistance = 1000; // some big number
+ long minDistance = Integer.MAX_VALUE; // some big number
+ long IdFoundMinDistance = Integer.MAX_VALUE; // some big number
int minIndex = 0;
+ int idFoundMinIndex = 0;
+ int eventInTimeIndex = -1;
+ int allDayEventInTimeIndex = -1;
+ int allDayEventDay = 0;
+ int minDay = 0;
+ boolean idFound = false;
int len = mRowInfo.size();
+
+ // Loop through the events and find the best match
+ // 1. Event id and start time matches requested id and time
+ // 2. Event id matches and closest time
+ // 3. No event id match , time is between event start and end
+ // 4. No event id match , all day event
+ // 5. The closest event to the requested time
+
for (int index = 0; index < len; index++) {
RowInfo row = mRowInfo.get(index);
if (row.mType == TYPE_DAY) {
- int distance = Math.abs(julianDay - row.mDay);
- if (distance == 0) {
+ continue;
+ }
+
+ // Found exact match - done
+ if (row.mEventId == id) {
+ if (row.mEventStartTimeMilli == millis) {
return index;
}
+
+ // Not an exact match, Save event index if it is the closest to time so far
+ long distance = Math.abs(millis - row.mEventStartTimeMilli);
if (distance < minDistance) {
- minDistance = distance;
- minIndex = index;
+ IdFoundMinDistance = distance;
+ idFoundMinIndex = index;
+ }
+ idFound = true;
+ }
+ if (!idFound) {
+ // Found an event that contains the requested time
+ if (millis >= row.mEventStartTimeMilli && millis <= row.mEventEndTimeMilli) {
+ if (row.mAllDay) {
+ allDayEventInTimeIndex = index;
+ allDayEventDay = row.mDay;
+ } else {
+ eventInTimeIndex = index;
+ }
+ } else {
+ // Save event index if it is the closest to time so far
+ long distance = Math.abs(millis - row.mEventStartTimeMilli);
+ if (distance < minDistance) {
+ minDistance = distance;
+ minIndex = index;
+ minDay = row.mDay;
+ }
}
}
}
-
- // We didn't find an exact match so take the nearest day that had
- // events.
+ // We didn't find an exact match so take the best matching event
+ if (idFound) {
+ return idFoundMinIndex;
+ }
+ if (eventInTimeIndex != -1) {
+ return eventInTimeIndex;
+ } else if (allDayEventInTimeIndex != -1 && minDay != allDayEventDay) {
+ return allDayEventInTimeIndex;
+ }
return minIndex;
}
+
/**
* Returns a flag indicating if this position is the first day after "yesterday" that has
* events in it.
@@ -497,6 +616,8 @@
*
* @param listPos the list position of an event
* @return the corresponding cursor position of that event
+ * if the position point to day header , it will give the position of the next event
+ * negated.
*/
public int getCursorPosition(int listPos) {
if (mRowInfo != null && listPos >= 0) {
diff --git a/src/com/android/calendar/agenda/AgendaFragment.java b/src/com/android/calendar/agenda/AgendaFragment.java
index f8db0ff..1ffbfb2 100644
--- a/src/com/android/calendar/agenda/AgendaFragment.java
+++ b/src/com/android/calendar/agenda/AgendaFragment.java
@@ -41,6 +41,7 @@
import com.android.calendar.GeneralPreferences;
import com.android.calendar.R;
import com.android.calendar.StickyHeaderListView;
+import com.android.calendar.StickyHeaderListView.HeaderHeightListener;
import com.android.calendar.StickyHeaderListView.HeaderIndexer;
import com.android.calendar.Utils;
@@ -67,6 +68,7 @@
private boolean mIsTabletConfig;
private EventInfo mOnAttachedInfo = null;
private boolean mOnAttachAllDay = false;
+ private AgendaWindowAdapter mAdapter = null;
// Tracks the time of the top visible view in order to send UPDATE_TITLE messages to the action
// bar.
@@ -157,9 +159,13 @@
Adapter a = mAgendaListView.getAdapter();
lv.setAdapter(a);
if (a instanceof HeaderViewListAdapter) {
- lv.setIndexer((HeaderIndexer) ((HeaderViewListAdapter)a).getWrappedAdapter());
+ mAdapter = (AgendaWindowAdapter) ((HeaderViewListAdapter)a).getWrappedAdapter();
+ lv.setIndexer(mAdapter);
+ lv.setHeaderHeightListener(mAdapter);
} else if (a instanceof AgendaWindowAdapter) {
- lv.setIndexer((HeaderIndexer) a);
+ mAdapter = (AgendaWindowAdapter)a;
+ lv.setIndexer(mAdapter);
+ lv.setHeaderHeightListener(mAdapter);
} else {
Log.wtf(TAG, "Cannot find HeaderIndexer for StickyHeaderListView");
}
@@ -185,7 +191,13 @@
GeneralPreferences.KEY_HIDE_DECLINED, false);
mAgendaListView.setHideDeclinedEvents(hideDeclined);
- mAgendaListView.goTo(mTime, -1, mQuery, true);
+ if (mLastHandledEventId != -1) {
+ mAgendaListView.goTo(mLastHandledEventTime, mLastHandledEventId, mQuery, true, false);
+ mLastHandledEventTime = null;
+ mLastHandledEventId = -1;
+ } else {
+ mAgendaListView.goTo(mTime, -1, mQuery, true, false);
+ }
mAgendaListView.onResume();
// // Register for Intent broadcasts
@@ -265,7 +277,9 @@
.after(event.endTime))) {
mTime.set(event.startTime);
}
- mAgendaListView.goTo(mTime, event.id, mQuery, false);
+ mAgendaListView.goTo(mTime, event.id, mQuery, false,
+ ((event.extraLong & CalendarController.EXTRA_GOTO_TODAY) != 0 &&
+ mShowEventDetailsWithAgenda) ? true : false);
AgendaAdapter.ViewHolder vh = mAgendaListView.getSelectedViewHolder();
showEventInfo(event, vh != null ? vh.allDay : false);
}
@@ -279,7 +293,7 @@
// The view hasn't been set yet. Just return.
return;
}
- mAgendaListView.goTo(time, -1, mQuery, true);
+ mAgendaListView.goTo(time, -1, mQuery, true, false);
}
@Override
@@ -294,12 +308,16 @@
return EventType.GO_TO | EventType.EVENTS_CHANGED | ((mUsedForSearch)?EventType.SEARCH:0);
}
+ private long mLastHandledEventId = -1;
+ private Time mLastHandledEventTime = null;
@Override
public void handleEvent(EventInfo event) {
if (event.eventType == EventType.GO_TO) {
// TODO support a range of time
// TODO support event_id
// TODO figure out the animate bit
+ mLastHandledEventId = event.id;
+ mLastHandledEventTime = event.startTime;
goTo(event, true);
} else if (event.eventType == EventType.SEARCH) {
search(event.query, event.startTime);
@@ -363,8 +381,13 @@
// OnScrollListener implementation to update the date on the pull-down menu of the app
+ @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
- // Do nothing
+ // Save scroll state so that the adapter can stop the scroll when the
+ // agenda list is fling state and it needs to set the agenda list to a new position
+ if (mAdapter != null) {
+ mAdapter.setScrollState(scrollState);
+ }
}
// Gets the time of the first visible view. If it is a new time, send a message to update
diff --git a/src/com/android/calendar/agenda/AgendaListView.java b/src/com/android/calendar/agenda/AgendaListView.java
index 1f6ff4d..98761a2 100644
--- a/src/com/android/calendar/agenda/AgendaListView.java
+++ b/src/com/android/calendar/agenda/AgendaListView.java
@@ -228,7 +228,8 @@
}
}
- public void goTo(Time time, long id, String searchQuery, boolean forced) {
+ public void goTo(Time time, long id, String searchQuery, boolean forced,
+ boolean refreshEventInfo) {
if (time == null) {
time = mTime;
long goToTime = getFirstVisibleTime();
@@ -243,11 +244,11 @@
if (DEBUG) {
Log.d(TAG, "Goto with time " + mTime.toString());
}
- mWindowAdapter.refresh(mTime, id, searchQuery, forced);
+ mWindowAdapter.refresh(mTime, id, searchQuery, forced, refreshEventInfo);
}
public void refresh(boolean forced) {
- mWindowAdapter.refresh(mTime, -1, null, forced);
+ mWindowAdapter.refresh(mTime, -1, null, forced, false);
}
public void deleteSelectedEvent() {
@@ -258,24 +259,6 @@
}
}
-/* @Override
- public int getFirstVisiblePosition() {
- // TODO File bug!
- // getFirstVisiblePosition doesn't always return the first visible
- // item. Sometimes, it is above the visible one.
- // instead. I loop through the viewgroup children and find the first
- // visible one. BTW, getFirstVisiblePosition() == getChildAt(0). I
- // am not looping through the entire list.
- View v = getFirstVisibleView();
- if (v != null) {
- if (DEBUG) {
- Log.v(TAG, "getFirstVisiblePosition: " + AgendaWindowAdapter.getViewTitle(v));
- }
- return getPositionForView(v);
- }
- return -1;
- }
-*/
public View getFirstVisibleView() {
Rect r = new Rect();
int childCount = getChildCount();
diff --git a/src/com/android/calendar/agenda/AgendaWindowAdapter.java b/src/com/android/calendar/agenda/AgendaWindowAdapter.java
index 3481067..14cb185 100644
--- a/src/com/android/calendar/agenda/AgendaWindowAdapter.java
+++ b/src/com/android/calendar/agenda/AgendaWindowAdapter.java
@@ -30,6 +30,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Handler;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.Calendars;
@@ -41,6 +42,8 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.TextView;
@@ -69,7 +72,7 @@
*/
public class AgendaWindowAdapter extends BaseAdapter
- implements StickyHeaderListView.HeaderIndexer{
+ implements StickyHeaderListView.HeaderIndexer, StickyHeaderListView.HeaderHeightListener{
static final boolean BASICLOG = false;
static final boolean DEBUGLOG = false;
@@ -156,6 +159,9 @@
private final boolean mIsTabletConfig;
private final int mSkipDateHeader;
+ boolean mCleanQueryInitiated = false;
+ private int mStickyHeaderSize = 44; // Initial size big enough for it to work
+
/**
* When the user scrolled to the top, a query will be made for older events
* and this will be incremented. Don't make more requests if
@@ -195,6 +201,9 @@
private boolean mShuttingDown;
private boolean mHideDeclined;
+ // Used to stop a fling motion if the ListView is set to a specific position
+ int mListViewScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+
/** The current search query, or null if none */
private String mSearchQuery;
@@ -215,9 +224,11 @@
int end;
String searchQuery;
int queryType;
+ long id;
public QuerySpec(int queryType) {
this.queryType = queryType;
+ id = -1;
}
@Override
@@ -235,6 +246,7 @@
long goToTimeMillis = goToTime.toMillis(false);
result = prime * result + (int) (goToTimeMillis ^ (goToTimeMillis >>> 32));
}
+ result = prime * result + (int)id;
return result;
}
@@ -246,7 +258,7 @@
QuerySpec other = (QuerySpec) obj;
if (end != other.end || queryStartMillis != other.queryStartMillis
|| queryType != other.queryType || start != other.start
- || Utils.equals(searchQuery, other.searchQuery)) {
+ || Utils.equals(searchQuery, other.searchQuery) || id != other.id) {
return false;
}
@@ -319,6 +331,10 @@
mShowEventOnStart = showEventOnStart;
+ // Implies there is no sticky header
+ if (!mShowEventOnStart) {
+ mStickyHeaderSize = 0;
+ }
mSearchQuery = null;
LayoutInflater inflater = (LayoutInflater) context
@@ -385,10 +401,23 @@
}
// Abstract Method in BaseAdapter
+ @Override
public long getItemId(int position) {
DayAdapterInfo info = getAdapterInfoByPosition(position);
if (info != null) {
- return ((position - info.offset) << 20) + info.start ;
+ int curPos = info.dayAdapter.getCursorPosition(position - info.offset);
+ if (curPos == Integer.MIN_VALUE) {
+ return -1;
+ }
+ // Regular event
+ if (curPos >= 0) {
+ info.cursor.moveToPosition(curPos);
+ return info.cursor.getLong(AgendaWindowAdapter.INDEX_EVENT_ID) << 20 +
+ info.cursor.getLong(AgendaWindowAdapter.INDEX_BEGIN);
+ }
+ // Day Header
+ return info.dayAdapter.findJulianDayFromPosition(position);
+
} else {
return -1;
}
@@ -469,12 +498,11 @@
private AgendaAdapter.ViewHolder mSelectedVH = null;
- private int findDayPositionNearestTime(Time time) {
- if (DEBUGLOG) Log.e(TAG, "findDayPositionNearestTime " + time);
-
+ private int findEventPositionNearestTime(Time time, long id) {
+ if (DEBUGLOG) Log.e(TAG, "findEventPositionNearestTime " + time + " id " + id);
DayAdapterInfo info = getAdapterInfoByTime(time);
if (info != null) {
- return info.offset + info.dayAdapter.findDayPositionNearestTime(time);
+ return info.offset + info.dayAdapter.findEventPositionNearestTime(time, id);
} else {
return -1;
}
@@ -550,8 +578,7 @@
}
if (cursorPosition < info.cursor.getCount()) {
- info.cursor.moveToPosition(cursorPosition);
- EventInfo ei = buildEventInfoFromCursor(info.cursor, isDayHeader);
+ EventInfo ei = buildEventInfoFromCursor(info.cursor, cursorPosition, isDayHeader);
if (!returnEventStartDay && !isDayHeader) {
ei.startDay = info.dayAdapter.findJulianDayFromPosition(cursorPosition);
}
@@ -560,7 +587,13 @@
return null;
}
- private EventInfo buildEventInfoFromCursor(final Cursor cursor, boolean isDayHeader) {
+ private EventInfo buildEventInfoFromCursor(final Cursor cursor, int cursorPosition,
+ boolean isDayHeader) {
+ if (cursorPosition == -1) {
+ cursor.moveToFirst();
+ } else {
+ cursor.moveToPosition(cursorPosition);
+ }
EventInfo event = new EventInfo();
event.begin = cursor.getLong(AgendaWindowAdapter.INDEX_BEGIN);
event.end = cursor.getLong(AgendaWindowAdapter.INDEX_END);
@@ -594,25 +627,54 @@
return event;
}
- public void refresh(Time goToTime, long id, String searchQuery, boolean forced) {
+ public void refresh(Time goToTime, long id, String searchQuery, boolean forced,
+ boolean refreshEventInfo) {
if (searchQuery != null) {
mSearchQuery = searchQuery;
}
if (DEBUGLOG) {
- Log.e(TAG, this + ": refresh " + goToTime.toString()
- + (forced ? " forced" : " not forced"));
+ Log.e(TAG, this + ": refresh " + goToTime.toString() + " id " + id
+ + ((searchQuery != null) ? searchQuery : "")
+ + (forced ? " forced" : " not forced")
+ + (refreshEventInfo ? " refresh event info" : ""));
}
-
int startDay = Time.getJulianDay(goToTime.toMillis(false), goToTime.gmtoff);
if (!forced && isInRange(startDay, startDay)) {
// No need to re-query
if (!mAgendaListView.isEventVisible(goToTime, id)) {
- int gotoPosition = findDayPositionNearestTime(goToTime);
+ int gotoPosition = findEventPositionNearestTime(goToTime, id);
if (gotoPosition > 0) {
- mAgendaListView.setSelection(gotoPosition + OFF_BY_ONE_BUG + mSkipDateHeader);
+ mAgendaListView.setSelectionFromTop(gotoPosition +
+ OFF_BY_ONE_BUG + mSkipDateHeader, mStickyHeaderSize);
+ if (mListViewScrollState == OnScrollListener.SCROLL_STATE_FLING) {
+ mAgendaListView.smoothScrollBy(0, 0);
+ }
+ if (refreshEventInfo) {
+ long newInstanceId = findInstanceIdFromPosition(gotoPosition);
+ if (newInstanceId != getSelectedInstanceId()) {
+ setSelectedInstanceId(newInstanceId);
+ new Handler().post(new Runnable() {
+ @Override
+ public void run() {
+ notifyDataSetChanged();
+ }
+ });
+ Cursor tempCursor = getCursorByPosition(gotoPosition);
+ if (tempCursor != null) {
+ int tempCursorPosition = getCursorPositionByPosition(gotoPosition);
+ EventInfo event =
+ buildEventInfoFromCursor(tempCursor, tempCursorPosition,
+ false);
+ CalendarController.getInstance(mContext).sendEventRelatedEvent(
+ this, EventType.VIEW_EVENT, event.id, event.begin,
+ event.end, 0, 0, -1);
+ }
+ }
+ }
}
+
Time actualTime = new Time(mTimeZone);
if (goToTime != null) {
actualTime.set(goToTime);
@@ -625,19 +687,23 @@
return;
}
- // Query for a total of MIN_QUERY_DURATION days
- int endDay = startDay + MIN_QUERY_DURATION;
+ // If AllInOneActivity is sending a second GOTO event(in OnResume), ignore it.
+ if (!mCleanQueryInitiated || searchQuery != null) {
+ // Query for a total of MIN_QUERY_DURATION days
+ int endDay = startDay + MIN_QUERY_DURATION;
- queueQuery(startDay, endDay, goToTime, searchQuery, QUERY_TYPE_CLEAN);
+ mSelectedInstanceId = -1;
+ queueQuery(startDay, endDay, goToTime, searchQuery, QUERY_TYPE_CLEAN, id);
+ mCleanQueryInitiated = true;
- // Pre-fetch more data to overcome a race condition in AgendaListView.shiftSelection
- // Queuing more data with the goToTime set to the selected time skips the call to
- // shiftSelection on refresh.
- mOlderRequests++;
- queueQuery(0, 0, goToTime, searchQuery, QUERY_TYPE_OLDER);
- mNewerRequests++;
- queueQuery(0, 0, goToTime, searchQuery, QUERY_TYPE_NEWER);
-
+ // Pre-fetch more data to overcome a race condition in AgendaListView.shiftSelection
+ // Queuing more data with the goToTime set to the selected time skips the call to
+ // shiftSelection on refresh.
+ mOlderRequests++;
+ queueQuery(0, 0, goToTime, searchQuery, QUERY_TYPE_OLDER, id);
+ mNewerRequests++;
+ queueQuery(0, 0, goToTime, searchQuery, QUERY_TYPE_NEWER, id);
+ }
}
public void close() {
@@ -742,12 +808,13 @@
}
private boolean queueQuery(int start, int end, Time goToTime,
- String searchQuery, int queryType) {
+ String searchQuery, int queryType, long id) {
QuerySpec queryData = new QuerySpec(queryType);
queryData.goToTime = goToTime;
queryData.start = start;
queryData.end = end;
queryData.searchQuery = searchQuery;
+ queryData.id = id;
return queueQuery(queryData);
}
@@ -856,6 +923,10 @@
+ " Count: " + cursor.getCount());
}
+ if (data.queryType == QUERY_TYPE_CLEAN) {
+ mCleanQueryInitiated = false;
+ }
+
if (mShuttingDown) {
cursor.close();
return;
@@ -865,6 +936,7 @@
int cursorSize = cursor.getCount();
if (cursorSize > 0 || mAdapterInfos.isEmpty() || data.queryType == QUERY_TYPE_CLEAN) {
final int listPositionOffset = processNewCursor(data, cursor);
+ int newPosition = -1;
if (data.goToTime == null) { // Typical Scrolling type query
notifyDataSetChanged();
if (listPositionOffset != 0) {
@@ -873,10 +945,13 @@
} else { // refresh() called. Go to the designated position
final Time goToTime = data.goToTime;
notifyDataSetChanged();
- int newPosition = findDayPositionNearestTime(goToTime);
+ newPosition = findEventPositionNearestTime(goToTime, data.id);
if (newPosition >= 0) {
- mAgendaListView.setSelection(newPosition + OFF_BY_ONE_BUG
- + mSkipDateHeader);
+ if (mListViewScrollState == OnScrollListener.SCROLL_STATE_FLING) {
+ mAgendaListView.smoothScrollBy(0, 0);
+ }
+ mAgendaListView.setSelectionFromTop(newPosition + OFF_BY_ONE_BUG
+ + mSkipDateHeader, mStickyHeaderSize);
Time actualTime = new Time(mTimeZone);
actualTime.set(goToTime);
CalendarController.getInstance(mContext).sendEvent(this,
@@ -885,7 +960,16 @@
}
if (DEBUGLOG) {
Log.e(TAG, "Setting listview to " +
- "findDayPositionNearestTime: " + (newPosition + OFF_BY_ONE_BUG));
+ "findEventPositionNearestTime: " + (newPosition + OFF_BY_ONE_BUG));
+ }
+ }
+
+ // Make sure we change the selected instance Id only on a clean query and we
+ // do not have one set already
+ if (mSelectedInstanceId == -1 && newPosition != -1 &&
+ data.queryType == QUERY_TYPE_CLEAN) {
+ if (data.id != -1 || data.goToTime != null) {
+ mSelectedInstanceId = findInstanceIdFromPosition(newPosition);
}
}
@@ -907,17 +991,32 @@
}
}
- if (mSelectedInstanceId == -1 && cursor.moveToFirst()) {
- mSelectedInstanceId = cursor.getLong(AgendaWindowAdapter.INDEX_INSTANCE_ID);
- // Set up a dummy view holder so we have the right all day
- // info when the view is created.
- // TODO determine the full set of what might be useful to
- // know about the selected view and fill it in.
- mSelectedVH = new AgendaAdapter.ViewHolder();
- mSelectedVH.allDay = cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
+ // Show the requested event
+ if (mShowEventOnStart && data.queryType == QUERY_TYPE_CLEAN) {
+ Cursor tempCursor = null;
+ int tempCursorPosition = -1;
- EventInfo event = buildEventInfoFromCursor(cursor, false);
- if (mShowEventOnStart) {
+ // If no valid event is selected , just pick the first one
+ if (mSelectedInstanceId == -1) {
+ if (cursor.moveToFirst()) {
+ mSelectedInstanceId = cursor
+ .getLong(AgendaWindowAdapter.INDEX_INSTANCE_ID);
+ // Set up a dummy view holder so we have the right all day
+ // info when the view is created.
+ // TODO determine the full set of what might be useful to
+ // know about the selected view and fill it in.
+ mSelectedVH = new AgendaAdapter.ViewHolder();
+ mSelectedVH.allDay =
+ cursor.getInt(AgendaWindowAdapter.INDEX_ALL_DAY) != 0;
+ tempCursor = cursor;
+ }
+ } else if (newPosition != -1) {
+ tempCursor = getCursorByPosition(newPosition);
+ tempCursorPosition = getCursorPositionByPosition(newPosition);
+ }
+ if (tempCursor != null) {
+ EventInfo event = buildEventInfoFromCursor(tempCursor, tempCursorPosition,
+ false);
CalendarController.getInstance(mContext).sendEventRelatedEvent(this,
EventType.VIEW_EVENT, event.id, event.begin, event.end, 0, 0, -1);
}
@@ -1161,11 +1260,35 @@
mSelectedVH = null;
}
+ private long findInstanceIdFromPosition(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return info.dayAdapter.getInstanceId(position - info.offset);
+ }
+ return -1;
+ }
+
+ private Cursor getCursorByPosition(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return info.cursor;
+ }
+ return null;
+ }
+
+ private int getCursorPositionByPosition(int position) {
+ DayAdapterInfo info = getAdapterInfoByPosition(position);
+ if (info != null) {
+ return info.dayAdapter.getCursorPosition(position - info.offset);
+ }
+ return -1;
+ }
// Implementation of HeaderIndexer interface for StickyHeeaderListView
// Returns the location of the day header of a specific event specified in the position
// in the adapter
+ @Override
public int getHeaderPositionFromItemPosition(int position) {
// For phone configuration, return -1 so there will be no sticky header
@@ -1182,6 +1305,7 @@
}
// Returns the number of events for a specific day header
+ @Override
public int getHeaderItemsNumber(int headerPosition) {
if (headerPosition < 0 || !mIsTabletConfig) {
return -1;
@@ -1192,4 +1316,13 @@
}
return -1;
}
+
+ @Override
+ public void OnHeaderHeightChanged(int height) {
+ mStickyHeaderSize = height;
+ }
+
+ public void setScrollState(int state) {
+ mListViewScrollState = state;
+ }
}