Bug 730192 - allow headset controls to function by passing media keyevents through to Android OS; r=jchen

This commit is contained in:
Bob 2016-03-01 07:49:54 -05:00
parent 782f52a633
commit bd8cdd84ef
6 changed files with 107 additions and 6 deletions

View File

@ -98,7 +98,7 @@ final class GeckoEditable extends JNIObject
private native void onKeyEvent(int action, int keyCode, int scanCode, int metaState,
long time, int unicodeChar, int baseUnicodeChar,
int domPrintableKeyValue, int repeatCount, int flags,
boolean isSynthesizedImeKey);
boolean isSynthesizedImeKey, KeyEvent event);
private void onKeyEvent(KeyEvent event, int action, int savedMetaState,
boolean isSynthesizedImeKey) {
@ -123,7 +123,7 @@ final class GeckoEditable extends JNIObject
// e.g. for Ctrl+A, Android returns 0 for unicodeChar,
// but Gecko expects 'a', so we return that in baseUnicodeChar.
event.getUnicodeChar(0), domPrintableKeyValue, event.getRepeatCount(),
event.getFlags(), isSynthesizedImeKey);
event.getFlags(), isSynthesizedImeKey, event);
}
@WrapForJNI
@ -1125,6 +1125,31 @@ final class GeckoEditable extends JNIObject
});
}
@WrapForJNI @Override
public void onDefaultKeyEvent(final KeyEvent event) {
if (DEBUG) {
// GeckoEditableListener methods should all be called from the Gecko thread
ThreadUtils.assertOnGeckoThread();
StringBuilder sb = new StringBuilder("onDefaultKeyEvent(");
sb.append("action=").append(event.getAction()).append(", ")
.append("keyCode=").append(event.getKeyCode()).append(", ")
.append("metaState=").append(event.getMetaState()).append(", ")
.append("time=").append(event.getEventTime()).append(", ")
.append("repeatCount=").append(event.getRepeatCount()).append(")");
Log.d(LOGTAG, sb.toString());
}
geckoPostToIc(new Runnable() {
@Override
public void run() {
if (mListener == null) {
return;
}
mListener.onDefaultKeyEvent(event);
}
});
}
// InvocationHandler interface
static String getConstantName(Class<?> cls, String prefix, Object value) {

View File

@ -7,6 +7,8 @@ package org.mozilla.gecko;
import org.mozilla.gecko.annotation.WrapForJNI;
import android.view.KeyEvent;
/**
* Interface for the Editable to listen on the Gecko thread, as well as for the IC thread to listen
* to the Editable.
@ -35,4 +37,5 @@ interface GeckoEditableListener {
void notifyIMEContext(int state, String typeHint, String modeHint, String actionHint);
void onSelectionChange(int start, int end);
void onTextChange(CharSequence text, int start, int oldEnd, int newEnd);
void onDefaultKeyEvent(KeyEvent event);
}

View File

@ -18,6 +18,7 @@ import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
import android.content.Context;
import android.content.res.Configuration;
import android.media.AudioManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@ -382,6 +383,16 @@ class GeckoInputConnection
getComposingSpanEnd(editable));
}
@Override
public void onDefaultKeyEvent(final KeyEvent event) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
GeckoInputConnection.this.performDefaultKeyAction(event);
}
});
}
private static synchronized Handler getBackgroundHandler() {
if (sBackgroundHandler != null) {
return sBackgroundHandler;
@ -680,6 +691,8 @@ class GeckoInputConnection
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_SEARCH:
// ignore HEADSETHOOK to allow hold-for-voice-search to work
case KeyEvent.KEYCODE_HEADSETHOOK:
return false;
}
return true;
@ -721,6 +734,37 @@ class GeckoInputConnection
return event;
}
// Called by OnDefaultKeyEvent handler, up from Gecko
/* package */ void performDefaultKeyAction(KeyEvent event) {
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_MEDIA_STOP:
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
case KeyEvent.KEYCODE_MEDIA_REWIND:
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_CLOSE:
case KeyEvent.KEYCODE_MEDIA_EJECT:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
// Forward media keypresses to the registered handler so headset controls work
// Does the same thing as Chromium
// https://chromium.googlesource.com/chromium/src/+/49.0.2623.67/chrome/android/java/src/org/chromium/chrome/browser/tab/TabWebContentsDelegateAndroid.java#445
// These are all the keys dispatchMediaKeyEvent supports.
if (AppConstants.Versions.feature19Plus) {
// dispatchMediaKeyEvent is only available on Android 4.4+
Context viewContext = getView().getContext();
AudioManager am = (AudioManager)viewContext.getSystemService(Context.AUDIO_SERVICE);
am.dispatchMediaKeyEvent(event);
}
break;
}
}
private boolean processKey(final int action, final int keyCode, final KeyEvent event) {
if (keyCode > KeyEvent.getMaxKeyCode() || !shouldProcessKey(keyCode, event)) {

View File

@ -768,6 +768,14 @@ auto GeckoEditable::NotifyIMEContext(int32_t a0, mozilla::jni::String::Param a1,
return mozilla::jni::Method<NotifyIMEContext_t>::Call(GeckoEditable::mCtx, nullptr, a0, a1, a2, a3);
}
constexpr char GeckoEditable::OnDefaultKeyEvent_t::name[];
constexpr char GeckoEditable::OnDefaultKeyEvent_t::signature[];
auto GeckoEditable::OnDefaultKeyEvent(mozilla::jni::Object::Param a0) const -> void
{
return mozilla::jni::Method<OnDefaultKeyEvent_t>::Call(GeckoEditable::mCtx, nullptr, a0);
}
constexpr char GeckoEditable::OnImeAcknowledgeFocus_t::name[];
constexpr char GeckoEditable::OnImeAcknowledgeFocus_t::signature[];

View File

@ -1597,6 +1597,22 @@ public:
auto NotifyIMEContext(int32_t, mozilla::jni::String::Param, mozilla::jni::String::Param, mozilla::jni::String::Param) const -> void;
struct OnDefaultKeyEvent_t {
typedef GeckoEditable Owner;
typedef void ReturnType;
typedef void SetterType;
typedef mozilla::jni::Args<
mozilla::jni::Object::Param> Args;
static constexpr char name[] = "onDefaultKeyEvent";
static constexpr char signature[] =
"(Landroid/view/KeyEvent;)V";
static const bool isStatic = false;
static const mozilla::jni::ExceptionMode exceptionMode =
mozilla::jni::ExceptionMode::ABORT;
};
auto OnDefaultKeyEvent(mozilla::jni::Object::Param) const -> void;
struct OnImeAcknowledgeFocus_t {
typedef GeckoEditable Owner;
typedef void ReturnType;
@ -1691,10 +1707,11 @@ public:
int32_t,
int32_t,
int32_t,
bool> Args;
bool,
mozilla::jni::Object::Param> Args;
static constexpr char name[] = "onKeyEvent";
static constexpr char signature[] =
"(IIIIJIIIIIZ)V";
"(IIIIJIIIIIZLandroid/view/KeyEvent;)V";
static const bool isStatic = false;
static const mozilla::jni::ExceptionMode exceptionMode =
mozilla::jni::ExceptionMode::ABORT;

View File

@ -350,7 +350,7 @@ public:
int32_t aMetaState, int64_t aTime, int32_t aUnicodeChar,
int32_t aBaseUnicodeChar, int32_t aDomPrintableKeyValue,
int32_t aRepeatCount, int32_t aFlags,
bool aIsSynthesizedImeKey);
bool aIsSynthesizedImeKey, jni::Object::Param originalEvent);
// Synchronize Gecko thread with the InputConnection thread.
void OnImeSynchronize();
@ -2445,7 +2445,7 @@ nsWindow::GeckoViewSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
int32_t aScanCode, int32_t aMetaState, int64_t aTime,
int32_t aUnicodeChar, int32_t aBaseUnicodeChar,
int32_t aDomPrintableKeyValue, int32_t aRepeatCount, int32_t aFlags,
bool aIsSynthesizedImeKey)
bool aIsSynthesizedImeKey, jni::Object::Param originalEvent)
{
RefPtr<nsWindow> kungFuDeathGrip(&window);
window.UserActivity();
@ -2480,6 +2480,10 @@ nsWindow::GeckoViewSupport::OnKeyEvent(int32_t aAction, int32_t aKeyCode,
mozilla::UniquePtr<WidgetEvent>(event.Duplicate()));
} else {
window.DispatchEvent(&event, status);
if (status != nsEventStatus_eConsumeNoDefault) {
mEditable->OnDefaultKeyEvent(originalEvent);
}
}
if (window.Destroyed() ||