Bezel Gestures with Android
Asked Answered
P

3

7

I'm working on an application (on a Galaxy Nexus), and I noticed that Google implemented the "Google Now" application when you swipe from the bottom of the bezel onto the screen (the chrome browser also did this for a while, not sure if it still does). I've been looking around but have been unable to determine how they accomplished this. Is there an easy way to handle gestures that begin in the bezel rather than on the screen?

I checked the developer pages for reference, but the only article I could find was here:

http://developer.android.com/design/patterns/gestures.html

Is there anywhere else where this information would be available?

[Edit]

I've been trying very unsuccessfully to get edge gestures working based on Lain_B's method, but cannot make it work. Here's the code that I'm using to try and detect the gestures, but logcat is always outputting a zero...

public class MainActivity extends Activity implements OnGestureListener {

private GestureDetector myGesture;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    myGesture = new GestureDetector(getBaseContext(),
            (OnGestureListener) this);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.e("Flags Touch", "Flags: " + event.getEdgeFlags());
    return myGesture.onTouchEvent(event);
}

@Override
public boolean onDown(MotionEvent e) {
    Log.e("Flags", "Flags: " + e.getEdgeFlags());
    Log.e("Event", "onDown");
    // TODO Auto-generated method stub
    return false;
}

@Override
public void onLongPress(MotionEvent e) {
    Log.e("Event", "onLongPress");
    // TODO Auto-generated method stub

}

@Override
public void onShowPress(MotionEvent e) {
    Log.e("Flags", "Flags: " + e.getEdgeFlags());
    Log.e("Event", "onShowPress");
    // TODO Auto-generated method stub

}

@Override
public boolean onSingleTapUp(MotionEvent e) {
    Log.e("Event", "onSingleTapUp");
    // TODO Auto-generated method stub
    return false;
}

@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
        float distanceY) {
    // Log.e("Event", "onScroll");
    return false;
}

// these constants are used for onFling
private static final int SWIPE_MIN_DISTANCE = 120;
private static final int SWIPE_MAX_OFF_PATH = 250;
private static final int SWIPE_THRESHOLD_VELOCITY = 200;

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
        float velocityY) {

    Log.e("Event", "onFling");
    Log.e("Flags", "Flags: " + e1.getEdgeFlags());

    if (e1.getEdgeFlags() == MotionEvent.EDGE_LEFT) {
        // code to handle swipe from left edge
        Log.e("!!!!!", "Edge fling!");
    }

    try {
        // do not do anything if the swipe does not reach a certain length
        // of distance
        if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH)
            return false;

        // right to left swipe
        if (e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE
                && Math.abs(velocityX) < SWIPE_THRESHOLD_VELOCITY) {

        }
        // left to right swipe
        else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE
                && Math.abs(velocityX) < SWIPE_THRESHOLD_VELOCITY) {

        }
    } catch (Exception e) {
        // nothing
    }
    return false;

}
}

[Edit 2]

With Lain_B's requested output...

(Using Nexus 7)... Starting from farthest possible left point on the bezel, and swiping right to the middle(ish).

08-16 16:44:13.674: I/Fling(16702): Flags: 0
08-16 16:44:13.674: I/Fling(16702): e1: 2.5 711.5152
08-16 16:44:13.674: I/Fling(16702): e2: 215.4591 717.08105

Swiping from the center point of the screen, off the screen (to the right side)

08-16 16:46:37.364: I/Fling(16702): Flags: 0
08-16 16:46:37.364: I/Fling(16702): e1: 392.5 758.1818
08-16 16:46:37.364: I/Fling(16702): e2: 783.4375 743.3334
Pericynthion answered 15/8, 2012 at 13:49 Comment(2)
I'm not sure, but are you looking for this ? developer.android.com/reference/android/view/…Bittersweet
I'm already using a gesture listener, I was just trying to determine how to implement it when you start from outside the bounds of the screen.Pericynthion
P
9

I think MotionEvent.getEdgeFlags() is what you're looking for. You can then compare the returned value with EDGE_LEFT , EDGE_RIGHT , etc to see which edge was touched.

if( event1.getEdgeFlags()&MotionEvent.EDGE_LEFT != 0 ){
    //code to handle swipe from left edge
}

or

if( e1.getX() < 5.0f ){
    //handle swipe from left edge
}
Popelka answered 15/8, 2012 at 14:1 Comment(9)
Nailed it. Thanks Lain! You've been improving my code a lot the last few daysPericynthion
Happy to help, the questions have been in areas I've done a fair bit of work in.Popelka
Okay, so this still isn't working for me, I added this into my onFling event, and it's always returning a 0x0 for the edge flags (nothing touched). Am I missing something? Does Jellybean's system UI override any attempt to use these because it's listening for the "Google Now" swipe up from the bottom of the bezel? I'm trying to work in similar functionality, but from the left/right edgesPericynthion
Well it definitely can be done as I'm also on JellyBean and I can get swipePad (launcher tool) to work from left/right (Google Now does make using the bottom almost impossible). Perhaps just do it manually, i.e. if event1.x < 5 then left edge?Popelka
I had to untag this as an answer... I simply cannot get edge gestures working at all. I'll post the code I'm working with to show what I'm doing, hopefully you can provide some more feedback.Pericynthion
Sure, that's fair. Could you print out e1 & e2 [0] and post a sample logcat output with the physical context (e.g. half way up left edge to center of screen)? Another thing the getEdgeFlags() docs do mention that "This property is only set for ACTION_DOWN events." so check if they are set properly in onDown. [0] pastebin.com/4WXpWdJ3Popelka
In that case my above comment "event1.x < 5" (event1 becomes e1) should have worked no? I'll edit answerPopelka
That part will work, but my edge flags always register a 0. Apps like SwipePad work (or at least appear to) by using the bezel on the device, I was wondering why I couldn't ever get those flags to fire.Pericynthion
The docs do say that the flags are only set for ACTION_DOWN but they also say that event1 e1 is a "down" event so it should have the flags, I don't get it. The view has the full width of the screen so scope doesn't seem to be a problem. I'll try and find some time soon to knock up a quick test.Popelka
W
7

Relying on MotionEvent.getEdgeFlags is wrong. See this discussion for more info: https://groups.google.com/forum/?fromgroups=#!topic/android-developers/ZNjpzbkxeNM

Conceptually to detect a edge swipe you need to detect a drag/scroll event and also make sure the original "x" coordinate of the down event is within some distance from the edge.

The easiest way to do this is to use a GestureDetector to detect drag/scroll. Luckily onScroll method of SimpleOnGestureListener has everything you need.

So something like this (if you are using a GestureDetector configured with SimpleOnGestureListener):

 public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        if (e1.getX() < SOME_DISTANCE_FROM_SCREEN) {
           //your code  
           return true;
        }
        return false;
 }
Waft answered 8/5, 2013 at 22:50 Comment(2)
What does some distance from screen mean? What is its purpose? I am not understanding the code above.Meimeibers
@Rarster it's some constant that you define to mark the maximum acceptance distance from the edge of a screen in which a touch event would be considered an edge swipe.Incompliant
P
1

Well, the solution for me was rather different as I was trying to achieve edge swipe / border swipe feature (similar to iOS) but from right to left.

I created a Gesture Manager class that handles fling (for now) and compares the distance of the gesture(start and end) to the size of the screen.

Anyhow, here it is:

public class GestureManager {

private static final String TAG = GestureManager.class.getSimpleName();

private Context context;
private Presenter presenter;
private GestureDetector gesture;

public GestureManager(Context context, Presenter presenter) {
    this.context = context;
    this.presenter = presenter;
    this.gesture = new GestureDetector(context,
            new GestureDetector.SimpleOnGestureListener() {
                @Override
                public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                    int deviceWidth = Helper.getDeviceWith();
                    //Log.d(TAG, "onFling e1: "+e1.getX()+" e2: "+e2.getX()+" deviceWidth: "+deviceWidth * 0.80);
                    if (context instanceof DeckMainActivity){
                        if (!presenter.isSideBarVisible() && e1.getX() > deviceWidth * 0.98 && e2.getX() > deviceWidth * 0.80) {
                            presenter.showSideBar();
                            return true;
                        }
                    }
                    return false;
                }


            });
}

public GestureDetector getGesture(){
    return gesture;
}

Hope it helps :)

Pluviometer answered 17/4, 2019 at 0:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.