Menubutton больше не работает после обновления до кордовы 5 + cordova android 4.0.0

Недавно я обновился до кордовы 5 и удалил/воссоздал платформу Android в версии 4.0.0 и удалил/переустановил все плагины.

Мне также пришлось обновить android sdk до sdk 22 вместо 21.

С момента обновления я больше не могу поймать событие menubutton, как описано в документации по cordova.

Как он все еще упоминается в документах edge, я предполагаю, что он все равно должен работать, и я ничего не видел об этом в примечаниях к выпуску.

Кнопка

назад все еще работает.

Я попытался установить target-sdk в 19, он ничего не решил о проблеме.

Edit: Я выкопал исходный код кордовы и нашел в CordovaWebViewImpl.java. Я нашел подозрительный комментарий TODO:

   public void setButtonPlumbedToJs(int keyCode, boolean override) {
        switch (keyCode) {
            case KeyEvent.KEYCODE_VOLUME_DOWN:
            case KeyEvent.KEYCODE_VOLUME_UP:
            case KeyEvent.KEYCODE_BACK:
                // TODO: Why are search and menu buttons handled separately?
                if (override) {
                    boundKeyCodes.add(keyCode);
                } else {
                    boundKeyCodes.remove(keyCode);
                }
                return;
            default:
                throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
        }
    }

Ну, мой ответ будет "НЕ ДОЛЖЕН!!!!"

Кордова делает список кодов клавиш для обработки, но не добавляет кнопку меню, а затем код ключа сравнивается с KeyEvent.KEYCODE_MENU только после того, как код ключа был пропущен, потому что он отсутствует в списке.

Я попытался добавить кейс для кнопки меню, но оказывается, что функция вызывается только с кодом обратной кнопки.

Итак, теперь я знаю, почему это не работает, но все же не так, как это исправить.

Изменить 02/2016: В соответствии с последней версией Jira поддержка меню теперь фиксируется в java-части в Cordova Android 5.1.0, но все же не инициализируется из javascript. На данный момент, как указал пользователь Jira Кит Вонг, вам нужно добавить вызов javascript, прежде чем добавить слушателя событий:

document.addEventListener("deviceready", function() {
    ...
    navigator.app.overrideButton("menubutton", true);  // <-- Add this line
    document.addEventListener("menubutton", yourCallbackFunction, false);
    ...
}, false);

Ответ 1

Ясный ответ не сделал этого для меня, кнопка меню все еще не отвечала.

Я попробовал несколько патчей, еще одно предложение отключить проверку boundKeyCodes полностью не делало этого, потому что тогда поведение подкладки было бы скомпрометировано.

Чистый способ вернуть прежнее поведение должен быть следующим. Проверка boundKeyCodes гарантирует, что пользовательское поведение выполняется только тогда, когда на самом деле есть специальный обработчик событий, связанный с событием. Но привязка обработчика событий к "menubutton" в вашем JS-коде приложения больше не запускает menubutton key code для добавления в список boundKeyCodes. Это связано с тем, что метод setButtonPlumbedToJs никогда не выполняется для обработчика "menubutton" в первую очередь И даже если бы это было так, оператор switch в этом методе не обрабатывает KEYCODE_MENU.

Вы можете получить это поведение довольно легко, сначала вам нужно применить изменение, предложенное с помощью явного:

  • Ручка KEYCODE_MENU

в CordovaLib/src/org/apache/cordova/CoreAndroid.java(около строки 357, setButtonPlumbedToJs) добавьте оператор case после записи KEYCODE_BACK следующим образом:

public void setButtonPlumbedToJs(int keyCode, boolean override) {
  switch (keyCode) {
    case KeyEvent.KEYCODE_VOLUME_DOWN:
    case KeyEvent.KEYCODE_VOLUME_UP:
    case KeyEvent.KEYCODE_BACK:
    case KeyEvent.KEYCODE_MENU:
    // TODO: Why are search and menu buttons handled separately?
      if (override) {
        boundKeyCodes.add(keyCode);
      } else {
        boundKeyCodes.remove(keyCode);
      }
      return;
    default:
      throw new IllegalArgumentException("Unsupported keycode: " + keyCode);
  }
}

Затем убедитесь, что setButtonPlumbedToJs фактически выполняется. Для этого вам нужны еще два изменения.

  1. Добавить обработчик среды

В CordovaLib/src/org/apache/cordova/CoreAndroid.java(около строки 243, overrideButton) сделайте метод похожим на это (добавьте предложение else else if):

public void overrideButton(String button, boolean override) {

  if (button.equals("volumeup")) {
    webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_UP, override);
  }
  else if (button.equals("volumedown")) {
    webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_VOLUME_DOWN, override);
  }
  else if (button.equals("menubutton")) {
    webView.setButtonPlumbedToJs(KeyEvent.KEYCODE_MENU, override);
  }
}
  • Добавить вызов обработчика javascript

В platform_www/cordova.js(вокруг строки 1532, bootstrap) измените эту строку:

cordova.addDocumentEventHandler('menubutton');

:

var menuButtonChannel = cordova.addDocumentEventHandler('menubutton');
menuButtonChannel.onHasSubscribersChange = function() {
  exec(null, null, APP_PLUGIN_NAME, "overrideButton", ['menubutton', this.numHandlers == 1]);
};

Это вызовет метод overrideButton для фреймов, как только обработчик события будет добавлен в "menubutton" .

Это должно сделать это. Я также добавил это решение в качестве комментария к https://issues.apache.org/jira/browse/CB-8921 и может вскоре подать запрос на вытягивание.

Ответ 2

Просто добавьте в функцию setButtonPlumbedToJs одну строку: case KeyEvent.KEYCODE_MENU:

public void setButtonPlumbedToJs(int keyCode, boolean override) {
    switch (keyCode) {
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_BACK:
        case KeyEvent.KEYCODE_MENU:

Итак, в onDispatchKeyEvent переключатель будет работать:

      } else if (boundKeyCodes.contains(keyCode)) {
                String eventName = null;
                switch (keyCode) {
                    case KeyEvent.KEYCODE_VOLUME_DOWN:
                        eventName = "volumedownbutton";
                        break;
                    case KeyEvent.KEYCODE_VOLUME_UP:
                        eventName = "volumeupbutton";
                        break;
                    case KeyEvent.KEYCODE_SEARCH:
                        eventName = "searchbutton";
                        break;
                    case KeyEvent.KEYCODE_MENU:
                        eventName = "menubutton";
                        break;
                    case KeyEvent.KEYCODE_BACK:
                        eventName = "backbutton";
                        break;
                }

Ответ 3

Теперь с cordova-android 5.1, код изменился, и мой патч больше не работал (и, к сожалению, без патча кнопка меню все еще не работает в этой версии).

Поскольку я хотел иметь возможность обновлять платформу без необходимости просматривать код каждый раз, я искал новый способ вернуть кнопку меню снова.

В cordova android 5.1 выясняется, что все здесь в коде java для работы кнопки, за исключением того, что клавиша кнопки меню никогда не добавляется в массив boundKeyCoded.

Оказывается, этот массив необходимо заполнить вызовом из javascript (который делается для кнопки "Назад" и "Громкость", но ни для кнопки поиска, ни для кнопки меню).

Недопустимый код:

exec(null, null, APP_PLUGIN_NAME, 'overrideButton', ['menubutton' , true]);

(вызов js для функции java overrideButton от CoreAndroid.java, чтобы сказать, чтобы добавить кнопку кнопки меню в массив boundKeyCodes.

Я думаю, что этот вызов должен быть добавлен в platform.js, но так как platform.js используется для сборки cordova.js во время добавления платформы, я решил сделать after_platform_add hook, который исправляет кордову .js.

Преимущество этого крючка в том, что нет изменения java, и он должен работать, даже если вы используете другой веб-просмотр, например, пешеходный переход.

Итак, во-первых, в файле config.xml в секции Android добавить hook:

<platform name="android">
    ....
    ....
    <hook type="after_platform_add" src="scripts/android/patch_menubutton.js" />
    ....
    ....
</platform>

Затем в папке с скриптами добавьте файл hook patch_menubutton.js:

#!/usr/bin/env node
module.exports = function(ctx) {
    var fs = ctx.requireCordovaModule('fs'),
        path = ctx.requireCordovaModule('path');
    var CordovaJSPath = path.join(ctx.opts.projectRoot, 'platforms/android/platform_www/cordova.js');
    var data = fs.readFileSync(CordovaJSPath, 'utf8');
    var result = data.replace(new RegExp("cordova\\.addDocumentEventHandler\\('menubutton'\\);", "g"), "cordova.addDocumentEventHandler('menubutton'); exec(null, null, APP_PLUGIN_NAME, 'overrideButton', ['menubutton' , true]);");
    fs.writeFileSync(CordovaJSPath, result, 'utf8');
}

(он ищет инициализацию обработчика события для кнопки меню и добавляет вызов функции overrideButton, как описано в последней части ответа FewKinG)