package com.vaguehope.onosendai.model; import android.os.Bundle; import android.view.View; import android.widget.ListAdapter; import android.widget.ListView; import com.vaguehope.onosendai.util.LogWrapper; import com.vaguehope.onosendai.widget.ScrollIndicator; public class ScrollState { public enum ScrollDirection { UNKNOWN(0), UP(1), DOWN(2); private final int value; private ScrollDirection (final int value) { this.value = value; } public int getValue () { return this.value; } public static ScrollDirection parseValue (final int value) { switch (value) { case 1: return UP; case 2: return DOWN; default: return UNKNOWN; } } } private static final String KEY_ITEM_ID = "list_view_item_id"; private static final String KEY_TOP = "list_view_top"; private static final String KEY_ITEM_TIME = "list_view_item_time"; private static final String KEY_UNREAD_TIME = "list_view_unread_time"; private static final String KEY_SCROLL_DIRECTION = "list_view_scroll_direction"; private static final LogWrapper LOG = new LogWrapper("ST"); private final long itemId; private final int top; private final long itemTime; private final long unreadTime; private final ScrollDirection scrollDirection; public ScrollState (final long itemId, final int top, final long itemTime, final long unreadTime, final ScrollDirection scrollDirection) { this.itemId = itemId; this.top = top; this.itemTime = itemTime; this.unreadTime = unreadTime; this.scrollDirection = scrollDirection; } @Override public String toString () { return new StringBuilder() .append("ScrollState{").append(this.itemId) .append(',').append(this.top) .append(',').append(this.itemTime) .append(',').append(this.unreadTime) .append(',').append(this.scrollDirection) .append('}') .toString(); } public long getItemId () { return this.itemId; } public int getTop () { return this.top; } public long getItemTime () { return this.itemTime; } public long getUnreadTime () { return this.unreadTime; } public ScrollDirection getScrollDirection () { if (this.scrollDirection == null) return ScrollDirection.UNKNOWN; return this.scrollDirection; } public void applyTo (final ListView lv, final ScrollIndicator scrollIndicator) { scrollIndicator.setUnreadTime(this.unreadTime); applyToListView(lv); } public void applyToIfNewer (final ScrollIndicator scrollIndicator) { scrollIndicator.setUnreadTimeIfNewer(this.unreadTime); } public void applyToIfNewer (final ListView lv, final ScrollDirection lastScrollDirection) { if (lastScrollDirection == ScrollDirection.DOWN) return; final long listViewTime = ((TweetListCursorAdapter) lv.getAdapter()).getItemTime(lv.getFirstVisiblePosition()); if (this.itemTime > listViewTime) applyToListView(lv); } private void applyToListView (final ListView lv) { // NOTE if this seems unreliable try wrapping setSelection*() calls in lv.post(...). final ListAdapter adapter = lv.getAdapter(); if (this.itemId >= 0L) { for (int i = 0; i < adapter.getCount(); i++) { if (adapter.getItemId(i) == this.itemId) { lv.setSelectionFromTop(i, this.top); return; } } } // Also search by time before giving up. if (this.itemTime > 0L && adapter instanceof TweetListCursorAdapter) { final TweetListCursorAdapter tlca = (TweetListCursorAdapter) adapter; for (int i = 0; i < tlca.getCount(); i++) { final long itime = tlca.getItemTime(i); if (itime > 0L && itime <= this.itemTime) { lv.setSelectionFromTop(i, 0); return; } } LOG.w("Failed to restore scroll state %s to list of %s items.", this, tlca.getCount()); } else { LOG.w("Failed to restore scroll state %s.", this); } lv.setSelection(lv.getCount() - 1); } public void addToBundle (final Bundle bundle) { if (bundle == null) return; bundle.putLong(KEY_ITEM_ID, this.itemId); bundle.putInt(KEY_TOP, this.top); bundle.putLong(KEY_ITEM_TIME, this.itemTime); bundle.putLong(KEY_UNREAD_TIME, this.unreadTime); if (this.scrollDirection != null) bundle.putInt(KEY_SCROLL_DIRECTION, this.scrollDirection.getValue()); } public static ScrollState from (final ListView lv, final ScrollIndicator scrollIndicator, final ScrollDirection scrollDirection) { final int index = lv.getFirstVisiblePosition(); final View v = lv.getChildAt(0); final int top = (v == null) ? 0 : v.getTop(); final long itemId = lv.getAdapter().getItemId(index); if (itemId <= 0) return null; long time = ((TweetListCursorAdapter) lv.getAdapter()).getItemTime(index); if (time < 0L) time = 0L; return new ScrollState(itemId, top, time, scrollIndicator.getUnreadTime(), scrollDirection); } public static ScrollState fromBundle (final Bundle bundle) { if (bundle == null) return null; if (bundle.containsKey(KEY_ITEM_ID) && bundle.containsKey(KEY_TOP)) { final long itemId = bundle.getLong(KEY_ITEM_ID); final int top = bundle.getInt(KEY_TOP); final long itemTime = bundle.getLong(KEY_ITEM_TIME); final long unreadTime = bundle.getLong(KEY_UNREAD_TIME); return new ScrollState(itemId, top, itemTime, unreadTime, ScrollDirection.parseValue(bundle.getInt(KEY_SCROLL_DIRECTION, -1))); } return null; } }