diff --git a/clients/android/NewsBlur/res/values/strings.xml b/clients/android/NewsBlur/res/values/strings.xml
index 2ee8acb2e..efd93ffd9 100644
--- a/clients/android/NewsBlur/res/values/strings.xml
+++ b/clients/android/NewsBlur/res/values/strings.xml
@@ -258,6 +258,8 @@
light
+ Gestures
+
Tidying up...
Catching up %d reading actions...
Catching up reading actions...
@@ -300,4 +302,33 @@
- NONE
FOLDER_ONLY
+
+ Rightward Swipe on Story Title
+ No Action
+ Mark Story Read
+ Mark Story Unread
+
+ - @string/gest_action_none
+ - @string/gest_action_markread
+ - @string/gest_action_markunread
+
+
+ - GEST_ACTION_NONE
+ - GEST_ACTION_MARKREAD
+ - GEST_ACTION_MARKUNREAD
+
+ GEST_ACTION_MARKREAD
+
+ Leftward Swipe on Story Title
+
+ - @string/gest_action_none
+ - @string/gest_action_markread
+ - @string/gest_action_markunread
+
+
+ - GEST_ACTION_NONE
+ - GEST_ACTION_MARKREAD
+ - GEST_ACTION_MARKUNREAD
+
+ GEST_ACTION_MARKUNREAD
diff --git a/clients/android/NewsBlur/res/xml/activity_settings.xml b/clients/android/NewsBlur/res/xml/activity_settings.xml
index 0e758e967..830c8fb24 100644
--- a/clients/android/NewsBlur/res/xml/activity_settings.xml
+++ b/clients/android/NewsBlur/res/xml/activity_settings.xml
@@ -103,4 +103,22 @@
android:defaultValue="@string/default_theme_value" />
+
+
+
+
+
diff --git a/clients/android/NewsBlur/src/com/newsblur/fragment/ItemListFragment.java b/clients/android/NewsBlur/src/com/newsblur/fragment/ItemListFragment.java
index 813480575..3b8dc09c2 100644
--- a/clients/android/NewsBlur/src/com/newsblur/fragment/ItemListFragment.java
+++ b/clients/android/NewsBlur/src/com/newsblur/fragment/ItemListFragment.java
@@ -37,6 +37,7 @@ import com.newsblur.service.NBSyncService;
import com.newsblur.util.DefaultFeedView;
import com.newsblur.util.FeedSet;
import com.newsblur.util.FeedUtils;
+import com.newsblur.util.GestureAction;
import com.newsblur.util.PrefsUtils;
import com.newsblur.util.ReadFilter;
import com.newsblur.util.StoryOrder;
@@ -64,6 +65,13 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
private View fleuronFooter;
+ // row index of the last story to get a LTR gesture or -1 if none
+ private int gestureLeftToRightFlag = -1;
+ // row index of the last story to get a RTL gesture or -1 if none
+ private int gestureRightToLeftFlag = -1;
+ // flag indicating a gesture just occurred so we can ignore spurious story taps right after
+ private boolean gestureDebounce = false;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -109,7 +117,7 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
itemList.addFooterView(fleuronFooter, null, false);
itemList.setEmptyView(v.findViewById(R.id.empty_view));
- setupBezelSwipeDetector(itemList);
+ setupGestureDetector(itemList);
itemList.setOnScrollListener(this);
itemList.setOnItemClickListener(this);
itemList.setOnCreateContextMenuListener(this);
@@ -280,6 +288,10 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
+ // context menu like to get accidentally triggered by the ListView event handler right after
+ // we detect a gesure. if so, let the gesture happen rather than popping up the menu
+ if ((gestureLeftToRightFlag > -1) || (gestureRightToLeftFlag > -1)) return;
+
MenuInflater inflater = getActivity().getMenuInflater();
if (PrefsUtils.getStoryOrder(activity, getFeedSet()) == StoryOrder.NEWEST) {
inflater.inflate(R.menu.context_story_newest, menu);
@@ -353,6 +365,14 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
@Override
public synchronized void onItemClick(AdapterView> parent, View view, int position, long id) {
+ // clicks like to get accidentally triggered by the ListView event handler right after we detect
+ // a gesture. if so, let the gesture happen rather than popping up the menu
+ if (gestureDebounce){
+ gestureDebounce = false;
+ return;
+ }
+ if ((gestureLeftToRightFlag > -1) || (gestureRightToLeftFlag > -1)) return;
+
int truePosition = position - 1;
Story story = adapter.getStory(truePosition);
if (getActivity().isFinishing()) return;
@@ -364,36 +384,86 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
adapter.setTextSize(size);
adapter.notifyDataSetChanged();
}
-
}
- protected void setupBezelSwipeDetector(View v) {
- final GestureDetector gestureDetector = new GestureDetector(getActivity(), new BezelSwipeDetector());
+ protected void setupGestureDetector(View v) {
+ final GestureDetector gestureDetector = new GestureDetector(getActivity(), new ItemListGestureDetector());
v.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
- return gestureDetector.onTouchEvent(event);
+ boolean result = gestureDetector.onTouchEvent(event);
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ ItemListFragment.this.flushGesture();
+ }
+ return result;
}
});
}
- /**
- * A gesture detector that captures bezel swipes and finishes the activity,
- * to simulate a 'back' gesture.
- *
- * NB: pretty much all Views still try to process on-tap events despite
- * returning true, so be sure to check isFinishing() on all other
- * tap handlers.
- */
- class BezelSwipeDetector extends GestureDetector.SimpleOnGestureListener {
+ protected void gestureLeftToRight(float x, float y) {
+ int index = itemList.pointToPosition((int) x, (int) y);
+ gestureLeftToRightFlag = index;
+ }
+
+ protected void gestureRightToLeft(float x, float y) {
+ int index = itemList.pointToPosition((int) x, (int) y);
+ gestureRightToLeftFlag = index;
+ }
+
+ // the above gesture* methods will trigger more than once while being performed. it is not until
+ // the up-event that we look to see if any happened, and if so, take action and flush.
+ protected void flushGesture() {
+ int index = -1;
+ GestureAction action = GestureAction.GEST_ACTION_NONE;
+ if (gestureLeftToRightFlag > -1) {
+ index = gestureLeftToRightFlag;
+ action = PrefsUtils.getLeftToRightGestureAction(getActivity());
+ gestureLeftToRightFlag = -1;
+ gestureDebounce = true;
+ }
+ if (gestureRightToLeftFlag > -1) {
+ index = gestureRightToLeftFlag;
+ action = PrefsUtils.getRightToLeftGestureAction(getActivity());
+ gestureRightToLeftFlag = -1;
+ gestureDebounce = true;
+ }
+ if (index <= -1) return;
+ Story story = adapter.getStory(index-1);
+ switch (action) {
+ case GEST_ACTION_MARKREAD:
+ FeedUtils.markStoryAsRead(story, getActivity());;
+ break;
+ case GEST_ACTION_MARKUNREAD:
+ FeedUtils.markStoryUnread(story, getActivity());;
+ break;
+ case GEST_ACTION_NONE:
+ default:
+ }
+ }
+
+ class ItemListGestureDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- if((e1.getX() < 75f) && // the gesture should start from the left bezel and
- ((e2.getX()-e1.getX()) > 90f) && // move horizontally to the right and
- (Math.abs(e1.getY()-e2.getY()) < 40f) // have minimal vertical travel, so we don't capture scrolling gestures
- ) {
+ if ((e1.getX() < 75f) && // the gesture should start from the left bezel and
+ ((e2.getX()-e1.getX()) > 90f) && // move horizontally to the right and
+ (Math.abs(e1.getY()-e2.getY()) < 40f) // have minimal vertical travel, so we don't capture scrolling gestures
+ ) {
ItemListFragment.this.getActivity().finish();
return true;
}
+ if ((e1.getX() > 75f) && // the gesture should not start from the left bezel and
+ ((e2.getX()-e1.getX()) > 120f) && // move horizontally to the right and
+ (Math.abs(e1.getY()-e2.getY()) < 40f) // have minimal vertical travel, so we don't capture scrolling gestures
+ ) {
+ ItemListFragment.this.gestureLeftToRight(e1.getX(), e1.getY());
+ return true;
+ }
+ if ((e1.getX() > 75f) && // the gesture should not start from the left bezel and
+ ((e1.getX()-e2.getX()) > 120f) && // move horizontally to the left and
+ (Math.abs(e1.getY()-e2.getY()) < 40f) // have minimal vertical travel, so we don't capture scrolling gestures
+ ) {
+ ItemListFragment.this.gestureRightToLeft(e1.getX(), e1.getY());
+ return true;
+ }
return false;
}
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/util/GestureAction.java b/clients/android/NewsBlur/src/com/newsblur/util/GestureAction.java
new file mode 100644
index 000000000..acb3b7927
--- /dev/null
+++ b/clients/android/NewsBlur/src/com/newsblur/util/GestureAction.java
@@ -0,0 +1,9 @@
+package com.newsblur.util;
+
+public enum GestureAction {
+
+ GEST_ACTION_NONE,
+ GEST_ACTION_MARKREAD,
+ GEST_ACTION_MARKUNREAD;
+
+}
diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefConstants.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefConstants.java
index d66a0039e..fb8accc9a 100644
--- a/clients/android/NewsBlur/src/com/newsblur/util/PrefConstants.java
+++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefConstants.java
@@ -72,4 +72,7 @@ public class PrefConstants {
public static final String VOLUME_KEY_NAVIGATION = "volume_key_navigation";
public static final String MARK_ALL_READ_CONFIRMATION = "pref_confirm_mark_all_read";
+
+ public static final String LTR_GESTURE_ACTION = "ltr_gesture_action";
+ public static final String RTL_GESTURE_ACTION = "rtl_gesture_action";
}
diff --git a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java
index 4050790b7..f8a84a71d 100644
--- a/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java
+++ b/clients/android/NewsBlur/src/com/newsblur/util/PrefsUtils.java
@@ -599,4 +599,14 @@ public class PrefsUtils {
SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
return MarkAllReadConfirmation.valueOf(prefs.getString(PrefConstants.MARK_ALL_READ_CONFIRMATION, MarkAllReadConfirmation.FOLDER_ONLY.toString()));
}
+
+ public static GestureAction getLeftToRightGestureAction(Context context) {
+ SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
+ return GestureAction.valueOf(prefs.getString(PrefConstants.LTR_GESTURE_ACTION, GestureAction.GEST_ACTION_MARKREAD.toString()));
+ }
+
+ public static GestureAction getRightToLeftGestureAction(Context context) {
+ SharedPreferences prefs = context.getSharedPreferences(PrefConstants.PREFERENCES, 0);
+ return GestureAction.valueOf(prefs.getString(PrefConstants.RTL_GESTURE_ACTION, GestureAction.GEST_ACTION_MARKUNREAD.toString()));
+ }
}