diff --git a/app/src/main/java/com/termux/x11/LoriePreferences.java b/app/src/main/java/com/termux/x11/LoriePreferences.java index 62f68f89e..c76c284f3 100644 --- a/app/src/main/java/com/termux/x11/LoriePreferences.java +++ b/app/src/main/java/com/termux/x11/LoriePreferences.java @@ -293,6 +293,7 @@ public void onCreatePreferences(@Nullable Bundle savedInstanceState, @Nullable S setNoActionOptionText(findPreference("volumeDownAction"), "android volume control"); setNoActionOptionText(findPreference("volumeUpAction"), "android volume control"); + setNoActionOptionText(findPreference("mediaKeysAction"), "android media control"); } private void setSummary(CharSequence key, int disabled) { diff --git a/app/src/main/java/com/termux/x11/MainActivity.java b/app/src/main/java/com/termux/x11/MainActivity.java index 557f97b69..8204e2b29 100644 --- a/app/src/main/java/com/termux/x11/MainActivity.java +++ b/app/src/main/java/com/termux/x11/MainActivity.java @@ -118,7 +118,7 @@ public void onReceive(Context context, Intent intent) { onPreferencesChanged(""); } else if (ACTION_CUSTOM.equals(intent.getAction())) { android.util.Log.d("ACTION_CUSTOM", "action " + intent.getStringExtra("what")); - mInputHandler.extractUserActionFromPreferences(prefs, intent.getStringExtra("what")).accept(true); + mInputHandler.extractUserActionFromPreferences(prefs, intent.getStringExtra("what")).accept(0, true); } } }; diff --git a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java index 1aa452d9c..d49ebc6b9 100644 --- a/app/src/main/java/com/termux/x11/input/TouchInputHandler.java +++ b/app/src/main/java/com/termux/x11/input/TouchInputHandler.java @@ -43,7 +43,7 @@ import java.util.Arrays; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Consumer; +import java.util.function.BiConsumer; /** * This class is responsible for handling Touch input from the user. Touch events which manipulate @@ -93,12 +93,10 @@ public class TouchInputHandler { private final MainActivity mActivity; private final DisplayMetrics mMetrics = new DisplayMetrics(); - private final Consumer noAction = (down) -> {}; - private Consumer swipeUpAction = noAction; - private Consumer swipeDownAction = noAction; - private Consumer volumeUpAction = noAction; - private Consumer volumeDownAction = noAction; - private Consumer backButtonAction = noAction; + private final BiConsumer noAction = (key, down) -> {}; + private BiConsumer swipeUpAction = noAction, swipeDownAction = noAction, + volumeUpAction = noAction, volumeDownAction = noAction, backButtonAction = noAction, + mediaKeysAction = noAction; private static final int KEY_BACK = 158; @@ -208,7 +206,6 @@ private TouchInputHandler(MainActivity activity, RenderData renderData, refreshInputDevices(); ((InputManager) mActivity.getSystemService(Context.INPUT_SERVICE)).registerInputDeviceListener(new InputManager.InputDeviceListener() { - /** @noinspection DataFlowIssue*/ @Override public void onInputDeviceAdded(int deviceId) { InputDevice dev = InputDevice.getDevice(deviceId); @@ -223,7 +220,6 @@ public void onInputDeviceRemoved(int deviceId) { refreshInputDevices(); } - /** @noinspection DataFlowIssue*/ @Override public void onInputDeviceChanged(int deviceId) { InputDevice dev = InputDevice.getDevice(deviceId); @@ -468,25 +464,27 @@ public void reloadPreferences(Prefs p) { volumeUpAction = extractUserActionFromPreferences(p, "volumeUp"); volumeDownAction = extractUserActionFromPreferences(p, "volumeDown"); backButtonAction = extractUserActionFromPreferences(p, "backButton"); + mediaKeysAction = extractUserActionFromPreferences(p, "mediaKeys"); if(mTouchpadHandler != null) mTouchpadHandler.reloadPreferences(p); } - public Consumer extractUserActionFromPreferences(Prefs p, String name) { + public BiConsumer extractUserActionFromPreferences(Prefs p, String name) { LoriePreferences.PrefsProto.Preference pref = p.keys.get(name + "Action"); if (pref == null) return noAction; switch(pref.asList().get()) { - case "toggle soft keyboard": return (down) -> { if (down) MainActivity.toggleKeyboardVisibility(mActivity); }; - case "toggle additional key bar": return (down) -> { if (down) mActivity.toggleExtraKeys(); }; - case "open preferences": return (down) -> { if (down) mActivity.startActivity(new Intent(mActivity, LoriePreferences.class) {{ setAction(Intent.ACTION_MAIN); }}); }; - case "release pointer and keyboard capture": return (down) -> { if (down) setCapturingEnabled(false); }; - case "toggle fullscreen": return (down) -> { if (down) MainActivity.prefs.fullscreen.put(!MainActivity.prefs.fullscreen.get()); }; - case "exit": return (down) -> { if (down) mActivity.finish(); }; - case "send volume up": return (down) -> mActivity.getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_UP, down); - case "send volume down": return (down) -> mActivity.getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_DOWN, down); + case "toggle soft keyboard": return (key, down) -> { if (down) MainActivity.toggleKeyboardVisibility(mActivity); }; + case "toggle additional key bar": return (key, down) -> { if (down) mActivity.toggleExtraKeys(); }; + case "open preferences": return (key, down) -> { if (down) mActivity.startActivity(new Intent(mActivity, LoriePreferences.class) {{ setAction(Intent.ACTION_MAIN); }}); }; + case "release pointer and keyboard capture": return (key, down) -> { if (down) setCapturingEnabled(false); }; + case "toggle fullscreen": return (key, down) -> { if (down) MainActivity.prefs.fullscreen.put(!MainActivity.prefs.fullscreen.get()); }; + case "exit": return (key, down) -> { if (down) mActivity.finish(); }; + case "send volume up": return (key, down) -> mActivity.getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_UP, down); + case "send volume down": return (key, down) -> mActivity.getLorieView().sendKeyEvent(0, KEYCODE_VOLUME_DOWN, down); + case "send media action": return (key, down) -> mActivity.getLorieView().sendKeyEvent(0, key, down); default: return noAction; } } @@ -572,9 +570,9 @@ private void moveCursorToScreenPoint(float screenX, float screenY) { /** Processes a (multi-finger) swipe gesture. */ private boolean onSwipe() { if (mTotalMotionY > mSwipeThreshold) - swipeDownAction.accept(true); + swipeDownAction.accept(0, true); else if (mTotalMotionY < -mSwipeThreshold) - swipeUpAction.accept(true); + swipeUpAction.accept(0, true); else return false; @@ -759,6 +757,28 @@ private boolean screenPointLiesOutsideImageBoundary(float screenX, float screenY } } + /** + * It is a copy of {@link android.view.KeyEvent#isMediaSessionKey} to be used on Android 30 and below. + * Returns whether this key will be sent to the + * {@link android.media.session.MediaSession.Callback} if not handled. + */ + public static boolean isMediaSessionKey(int keyCode) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY: + case KeyEvent.KEYCODE_MEDIA_PAUSE: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_HEADSETHOOK: + 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: + return true; + } + return false; + } + public boolean sendKeyEvent(KeyEvent e) { int k = e.getKeyCode(); @@ -769,11 +789,19 @@ public boolean sendKeyEvent(KeyEvent e) { return false; } + if (isMediaSessionKey(k)) { + if (mediaKeysAction == noAction) + return false; + + mediaKeysAction.accept(k, e.getAction() == KeyEvent.ACTION_DOWN); + return true; + } + if (k == KEYCODE_VOLUME_DOWN) { if (volumeDownAction == noAction) return false; - volumeDownAction.accept(e.getAction() == KeyEvent.ACTION_DOWN); + volumeDownAction.accept(k, e.getAction() == KeyEvent.ACTION_DOWN); return true; } @@ -781,7 +809,7 @@ public boolean sendKeyEvent(KeyEvent e) { if (volumeUpAction == noAction) return false; - volumeUpAction.accept(e.getAction() == KeyEvent.ACTION_DOWN); + volumeUpAction.accept(k, e.getAction() == KeyEvent.ACTION_DOWN); return true; } @@ -795,7 +823,7 @@ public boolean sendKeyEvent(KeyEvent e) { } if (e.getScanCode() == KEY_BACK && e.getDevice().getKeyboardType() != KEYBOARD_TYPE_ALPHABETIC || e.getScanCode() == 0) { - backButtonAction.accept(e.getAction() == KeyEvent.ACTION_DOWN); + backButtonAction.accept(k, e.getAction() == KeyEvent.ACTION_DOWN); return true; } } diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 20a4f291b..45973b678 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -74,6 +74,10 @@ @array/userActionsValues send volume up + + @array/userActionsValues + send media action + auto portrait diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d60963993..051ad26f1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -109,6 +109,7 @@ E.g. META for META key, \n Notification tap Notification first button Notification second button + Media keys Requires "display resolution mode" to be "exact" or "custom" Requires intercepting system shortcuts with Dex mode or with Accessibility service diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 3d9a1dfad..0015779ba 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -72,5 +72,6 @@ +