Android 10 gesture navigation disable

Viewed 4976

TLDR: I need a way to disable Android 10 gesture navigation programmatically so that they don't accidentally go back when they swipe from the sides

The backstory: Android 10 introduced gesture navigation as opposed to the buttons at the bottom. So now on Android 10 devices that have it enabled, they can swipe from either side of the screen to go back and swipe from the bottom to navigate home or between apps. However, I am working on an implementation in AR and want to lock the screen to portrait but allow users to go landscape.

If a user turns their phone to landscape but the activity is locked to portrait, the back gesture navigation is now a swipe from the top which is a common way to access the status bar in a full screen app (which this one is) so users will inadvertently go back and leave the experience if they are used to android navigations.

Does anybody know how to either a) disable the gesture navigation (but then how does the user go back/to home?) for Android 10 programmatically or b) know how to just change the orientation for the gestures without needing your activity to support landscape?

3 Answers

It's very easy to block the gestures programmatically, but you can't do that for entire edges on both side. SO you have to decide on how much portion of the screen you want to disable the gestures?

Here is the code :

  • Define this code in your Utils class.

     static List<Rect> exclusionRects = new ArrayList<>();
    
     public static void updateGestureExclusion(AppCompatActivity activity) {
     if (Build.VERSION.SDK_INT < 29) return;
     exclusionRects.clear();
     Rect rect = new Rect(0, 0, SystemUtil.dpToPx(activity, 16), getScreenHeight(activity));
     exclusionRects.add(rect);
    
     activity.findViewById(android.R.id.content).setSystemGestureExclusionRects(exclusionRects);
     }
    
    public static int getScreenHeight(AppCompatActivity activity) {
     DisplayMetrics displayMetrics = new DisplayMetrics();
     activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
     int height = displayMetrics.heightPixels;
     return height;
    }
    
     public static int dpToPx(Context context, int i) {
     return (int) (((float) i) * context.getResources().getDisplayMetrics().density);
    }
    
  • Check if your layout is set in that activity where you want to exclude the edge getures and then apply this code.

     // 'content' is the root view of your layout xml.
     ViewTreeObserver treeObserver = content.getViewTreeObserver();
     treeObserver.addOnGlobalLayoutListener(new 
     ViewTreeObserver.OnGlobalLayoutListener() {
       @Override
       public void onGlobalLayout() {
           content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
           SystemUtil.updateGestureExclusion(MainHomeActivity.this);
       }
     });
    
  • We are adding the exclusion rectangle width to 16dp to fetch the back gesture which you can change according to your preferrences.

Here are some things to note :-

  • You must not block both side gestures. If you do so, it'll be the worst user experience.
  • "getScreenHeight(activity)" is the height of the rectangle, So if you want to block the gesture in left side & top half of the screen then simply replace it with getScreenHeight(activity)/2
  • 1st argument in new Rect() is 0 because we want the gestures on left sdie, If you want it right side then simply put - "getScreenWidth(activity) - SystemUtil.dpToPx(activity, 16)"

Hope this will solve your problem permanently. :)

Remember:

  • setSystemGestureExclusionRects() must be called in doOnLayout() for your view

My implementation:

    binding.root.apply { // changing gesture rects for root view
        
        doOnLayout {
            // updating exclusion rect
            val rects = mutableListOf<Rect>()
            rects.add(Rect(0,0,width,(150 * resources.displayMetrics.density).toInt()))
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                systemGestureExclusionRects = rects
            }
        }
        
    }
  • I excluded gestures for 150dp from the top, and for the entire width (just to test)

While @Dev4Life's answer was helpful, I had no success until visiting the documentation:

Android Docs: setSystemGestureExclusionRects

Not really an answer to option A or B. But at least user don't accidentally go back when they swipe from the sides.

So, if the device is Android 10 and the user execute go back, I make AlertDialog if the user want really to go back or not. Although the prompt will be shown too, even they don't activate gesture navigation (still using 2 or 3 Button Navigation).

@Override
public void onBackPressed() {
    if(android.os.Build.VERSION.SDK_INT >= 29) { //as long as the Device is Android 10 (API level 29) 
        // idk, need more condition like gestureNavigation.isActive()
        AlertDialog.Builder builder = new AlertDialog.Builder(YourActivity.this);


        builder
            .setMessage("Are you sure want to exit?")
            .setPositiveButton("Yes, let me out",  new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog,int id) {
                    YourActivity.super.onBackPressed();
                }
            })
            .setNegativeButton("No, wrong swipe/click", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
                }
            })
            .show();
    } else {
        super.onBackPressed();
    }
}

Consider this as alternative last resort. Hope it helps

Related