Skip to content

Instantly share code, notes, and snippets.

@jgilfelt
Created October 3, 2013 16:28
Show Gist options
  • Save jgilfelt/6812732 to your computer and use it in GitHub Desktop.
Save jgilfelt/6812732 to your computer and use it in GitHub Desktop.
A base Activity class that allows the compatibility menu button (AKA "the menu button of shame") to be displayed for applications whose targetSdkVersion >= 11. On devices with a hard menu key there is no effect. Useful if you want to launch some sort of in-app debug UI from an on-screen affordance without altering your application's user interfa…
package com.example.shame;
import android.annotation.TargetApi;
import android.app.Activity;
import android.os.Build;
import android.view.KeyEvent;
import android.view.ViewConfiguration;
public abstract class ShameActivity extends Activity {
/**
* Flag for a window belonging to an activity that responds to {@link KeyEvent#KEYCODE_MENU}
* and therefore needs a Menu key. For devices where Menu is a physical button this flag is
* ignored, but on devices where the Menu key is drawn in software it may be hidden unless
* this flag is set.
*
* (Note that Action Bars, when available, are the preferred way to offer additional
* functions otherwise accessed via an options menu.)
*
* {@hide}
*/
private static final int FLAG_NEEDS_MENU_KEY = 0x08000000;
private boolean mCompatMenuKeyEnabled = false;
/**
* Enable the compatibility menu button on devices that don't have
* a permanent menu key.
*
* @param enabled true to enable the compatibility menu button, false to disable it.
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
protected void setCompatMenuKeyEnabled(boolean enabled) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
ViewConfiguration vc = ViewConfiguration.get(this);
if (!vc.hasPermanentMenuKey()) {
if (enabled) {
getWindow().addFlags(FLAG_NEEDS_MENU_KEY);
} else {
getWindow().clearFlags(FLAG_NEEDS_MENU_KEY);
}
mCompatMenuKeyEnabled = enabled;
}
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mCompatMenuKeyEnabled) {
if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && event.getAction() == KeyEvent.ACTION_UP) {
onCompatMenuKeyPressed();
// returning true creates an unfortunate side effect with the
// overflow menu, so it will still open here (if present)
return false;
}
}
return super.dispatchKeyEvent(event);
}
/**
* Override to implement a compatibility menu key press action.
*/
protected abstract void onCompatMenuKeyPressed();
}
@marcosdiez
Copy link

marcosdiez commented Sep 7, 2016

Unfortunately that does not work on Android 5+.

So I added this:

    private void showMenuKeyIfNecessaryOnAndroid5OrHigher() {
        String TAG = "MENU_BUTTON";
        Window w = getWindow();


        for (Class clazz = w.getClass(); clazz != null; clazz = clazz.getSuperclass()) {
            try {
                Method method = clazz.getDeclaredMethod("setNeedsMenuKey", int.class);
                method.setAccessible(true);
                try {
                    method.invoke(w, 1);  // 1 == WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE
                    return;
                } catch (IllegalAccessException e) {
                    Log.d(TAG, "IllegalAccessException on window.setNeedsMenuKey");
                } catch (java.lang.reflect.InvocationTargetException e) {
                    Log.d(TAG, "InvocationTargetException on window.setNeedsMenuKey");
                }
            } catch (NoSuchMethodException e) {
                // Log.d(TAG, "NoSuchMethodException");
            }
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment