Loading data/keyboards/Vendor_18d1_Product_0200.kcm 0 → 100644 +48 −0 Original line number Diff line number Diff line # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. type FULL key BUTTON_A { base: fallback DPAD_CENTER } key BUTTON_B { base: fallback BACK } key BUTTON_X { base: fallback DPAD_CENTER } key BUTTON_Y { base: fallback BACK } key BUTTON_THUMBL { base: fallback DPAD_CENTER } key BUTTON_THUMBR { base: fallback DPAD_CENTER } key BUTTON_SELECT { base: fallback MENU } key BUTTON_MODE { base: fallback MENU } data/keyboards/Vendor_18d1_Product_0200.kl 0 → 100644 +71 −0 Original line number Diff line number Diff line # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Keyboard map for the android virtual remote running as a gamepad # key 0x130 BUTTON_A key 0x131 BUTTON_B key 0x133 BUTTON_X key 0x134 BUTTON_Y key 0x136 BUTTON_L2 key 0x137 BUTTON_R2 key 0x138 BUTTON_L1 key 0x139 BUTTON_R1 key 0x13a BUTTON_SELECT key 0x13b BUTTON_START key 0x13c BUTTON_MODE key 0x13d BUTTON_THUMBL key 0x13e BUTTON_THUMBR key 103 DPAD_UP key 108 DPAD_DOWN key 105 DPAD_LEFT key 106 DPAD_RIGHT # Generic usage buttons key 0x2c0 BUTTON_1 key 0x2c1 BUTTON_2 key 0x2c2 BUTTON_3 key 0x2c3 BUTTON_4 key 0x2c4 BUTTON_5 key 0x2c5 BUTTON_6 key 0x2c6 BUTTON_7 key 0x2c7 BUTTON_8 key 0x2c8 BUTTON_9 key 0x2c9 BUTTON_10 key 0x2ca BUTTON_11 key 0x2cb BUTTON_12 key 0x2cc BUTTON_13 key 0x2cd BUTTON_14 key 0x2ce BUTTON_15 key 0x2cf BUTTON_16 # assistant buttons key 0x246 VOICE_ASSIST key 0x247 ASSIST axis 0x00 X axis 0x01 Y axis 0x02 Z axis 0x05 RZ axis 0x09 RTRIGGER axis 0x0a LTRIGGER axis 0x10 HAT_X axis 0x11 HAT_Y media/java/android/media/tv/ITvRemoteServiceInput.aidl +7 −1 Original line number Diff line number Diff line Loading @@ -39,4 +39,10 @@ oneway interface ITvRemoteServiceInput { void sendPointerUp(IBinder token, int pointerId); @UnsupportedAppUsage void sendPointerSync(IBinder token); // API specific to gamepads. Close gamepads with closeInputBridge void openGamepadBridge(IBinder token, String name); void sendGamepadKeyDown(IBinder token, int keyCode); void sendGamepadKeyUp(IBinder token, int keyCode); void sendGamepadAxisValue(IBinder token, int axis, float value); } media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java +155 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.media.tv.remoteprovider; import android.annotation.FloatRange; import android.annotation.NonNull; import android.content.Context; import android.media.tv.ITvRemoteProvider; import android.media.tv.ITvRemoteServiceInput; Loading @@ -24,6 +26,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.LinkedList; import java.util.Objects; /** * Base class for emote providers implemented in unbundled service. Loading Loading @@ -124,27 +127,75 @@ public abstract class TvRemoteProvider { * @param maxPointers Maximum supported pointers * @throws RuntimeException */ public void openRemoteInputBridge(IBinder token, String name, int width, int height, int maxPointers) throws RuntimeException { public void openRemoteInputBridge( IBinder token, String name, int width, int height, int maxPointers) throws RuntimeException { final IBinder finalToken = Objects.requireNonNull(token); final String finalName = Objects.requireNonNull(name); synchronized (mOpenBridgeRunnables) { if (mRemoteServiceInput == null) { Log.d(TAG, "Delaying openRemoteInputBridge() for " + name); Log.d(TAG, "Delaying openRemoteInputBridge() for " + finalName); mOpenBridgeRunnables.add(() -> { try { mRemoteServiceInput.openInputBridge( token, name, width, height, maxPointers); Log.d(TAG, "Delayed openRemoteInputBridge() for " + name + ": success"); finalToken, finalName, width, height, maxPointers); Log.d(TAG, "Delayed openRemoteInputBridge() for " + finalName + ": success"); } catch (RemoteException re) { Log.e(TAG, "Delayed openRemoteInputBridge() for " + finalName + ": failure", re); } }); return; } } try { mRemoteServiceInput.openInputBridge(finalToken, finalName, width, height, maxPointers); Log.d(TAG, "openRemoteInputBridge() for " + finalName + ": success"); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Opens an input bridge as a gamepad device. * Clients should pass in a token that can be used to match this request with a token that * will be returned by {@link TvRemoteProvider#onInputBridgeConnected(IBinder token)} * <p> * The token should be used for subsequent calls. * </p> * * @param token Identifier for this connection * @param name Device name * @throws RuntimeException * * @hide */ public void openGamepadBridge(@NonNull IBinder token, @NonNull String name) throws RuntimeException { final IBinder finalToken = Objects.requireNonNull(token); final String finalName = Objects.requireNonNull(name); synchronized (mOpenBridgeRunnables) { if (mRemoteServiceInput == null) { Log.d(TAG, "Delaying openGamepadBridge() for " + finalName); mOpenBridgeRunnables.add(() -> { try { mRemoteServiceInput.openGamepadBridge(finalToken, finalName); Log.d(TAG, "Delayed openGamepadBridge() for " + finalName + ": success"); } catch (RemoteException re) { Log.e(TAG, "Delayed openRemoteInputBridge() for " + name + ": failure", re); Log.e(TAG, "Delayed openGamepadBridge() for " + finalName + ": failure", re); } }); return; } } try { mRemoteServiceInput.openInputBridge(token, name, width, height, maxPointers); Log.d(TAG, "openRemoteInputBridge() for " + name + ": success"); mRemoteServiceInput.openGamepadBridge(token, finalName); Log.d(TAG, "openGamepadBridge() for " + finalName + ": success"); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } Loading @@ -157,6 +208,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void closeInputBridge(IBinder token) throws RuntimeException { Objects.requireNonNull(token); try { mRemoteServiceInput.closeInputBridge(token); } catch (RemoteException re) { Loading @@ -173,6 +225,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void clearInputBridge(IBinder token) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "clearInputBridge() token " + token); try { mRemoteServiceInput.clearInputBridge(token); Loading @@ -190,6 +243,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendTimestamp(IBinder token, long timestamp) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendTimestamp() token: " + token + ", timestamp: " + timestamp); try { Loading @@ -207,6 +261,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendKeyUp(IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendKeyUp() token: " + token + ", keyCode: " + keyCode); try { mRemoteServiceInput.sendKeyUp(token, keyCode); Loading @@ -223,6 +278,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendKeyDown(IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendKeyDown() token: " + token + ", keyCode: " + keyCode); try { Loading @@ -241,6 +297,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendPointerUp(IBinder token, int pointerId) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendPointerUp() token: " + token + ", pointerId: " + pointerId); try { Loading @@ -262,6 +319,7 @@ public abstract class TvRemoteProvider { */ public void sendPointerDown(IBinder token, int pointerId, int x, int y) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendPointerDown() token: " + token + ", pointerId: " + pointerId); try { Loading @@ -278,6 +336,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendPointerSync(IBinder token) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendPointerSync() token: " + token); try { mRemoteServiceInput.sendPointerSync(token); Loading @@ -286,6 +345,94 @@ public abstract class TvRemoteProvider { } } /** * Send a notification that a gamepad key was pressed. * * Supported buttons are: * <ul> * <li> Right-side buttons: BUTTON_A, BUTTON_B, BUTTON_X, BUTTON_Y * <li> Digital Triggers and bumpers: BUTTON_L1, BUTTON_R1, BUTTON_L2, BUTTON_R2 * <li> Thumb buttons: BUTTON_THUMBL, BUTTON_THUMBR * <li> DPad buttons: DPAD_UP, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT * <li> Gamepad buttons: BUTTON_SELECT, BUTTON_START, BUTTON_MODE * <li> Generic buttons: BUTTON_1, BUTTON_2, ...., BUTTON16 * <li> Assistant: ASSIST, VOICE_ASSIST * </ul> * * @param token identifier for the device * @param keyCode the gamepad key that was pressed (like BUTTON_A) * * @hide */ public void sendGamepadKeyDown(@NonNull IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) { Log.d(TAG, "sendGamepadKeyDown() token: " + token); } try { mRemoteServiceInput.sendGamepadKeyDown(token, keyCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Send a notification that a gamepad key was released. * * @see sendGamepadKeyDown for supported key codes. * * @param token identifier for the device * @param keyCode the gamepad key that was pressed * * @hide */ public void sendGamepadKeyUp(@NonNull IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) { Log.d(TAG, "sendGamepadKeyUp() token: " + token); } try { mRemoteServiceInput.sendGamepadKeyUp(token, keyCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Send a gamepad axis value. * * Supported axes: * <li> Left Joystick: AXIS_X, AXIS_Y * <li> Right Joystick: AXIS_Z, AXIS_RZ * <li> Triggers: AXIS_LTRIGGER, AXIS_RTRIGGER * <li> DPad: AXIS_HAT_X, AXIS_HAT_Y * * For non-trigger axes, the range of acceptable values is [-1, 1]. The trigger axes support * values [0, 1]. * * @param token identifier for the device * @param axis MotionEvent axis * @param value the value to send * * @hide */ public void sendGamepadAxisValue( @NonNull IBinder token, int axis, @FloatRange(from = -1.0f, to = 1.0f) float value) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) { Log.d(TAG, "sendGamepadAxisValue() token: " + token); } try { mRemoteServiceInput.sendGamepadAxisValue(token, axis, value); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } private final class ProviderStub extends ITvRemoteProvider.Stub { @Override public void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) { Loading media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java +48 −0 Original line number Diff line number Diff line Loading @@ -83,4 +83,52 @@ public class TvRemoteProviderTest extends AndroidTestCase { assertTrue(tvProvider.verifyTokens()); } @SmallTest public void testOpenGamepadRemoteInputBridge() throws Exception { Binder tokenA = new Binder(); Binder tokenB = new Binder(); Binder tokenC = new Binder(); class LocalTvRemoteProvider extends TvRemoteProvider { private final ArrayList<IBinder> mTokens = new ArrayList<IBinder>(); LocalTvRemoteProvider(Context context) { super(context); } @Override public void onInputBridgeConnected(IBinder token) { mTokens.add(token); } public boolean verifyTokens() { return mTokens.size() == 3 && mTokens.contains(tokenA) && mTokens.contains(tokenB) && mTokens.contains(tokenC); } } LocalTvRemoteProvider tvProvider = new LocalTvRemoteProvider(getContext()); ITvRemoteProvider binder = (ITvRemoteProvider) tvProvider.getBinder(); ITvRemoteServiceInput tvServiceInput = mock(ITvRemoteServiceInput.class); doAnswer((i) -> { binder.onInputBridgeConnected(i.getArgument(0)); return null; }) .when(tvServiceInput) .openGamepadBridge(any(), any()); tvProvider.openGamepadBridge(tokenA, "A"); tvProvider.openGamepadBridge(tokenB, "B"); binder.setRemoteServiceInputSink(tvServiceInput); tvProvider.openGamepadBridge(tokenC, "C"); verify(tvServiceInput).openGamepadBridge(tokenA, "A"); verify(tvServiceInput).openGamepadBridge(tokenB, "B"); verify(tvServiceInput).openGamepadBridge(tokenC, "C"); verifyNoMoreInteractions(tvServiceInput); assertTrue(tvProvider.verifyTokens()); } } Loading
data/keyboards/Vendor_18d1_Product_0200.kcm 0 → 100644 +48 −0 Original line number Diff line number Diff line # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. type FULL key BUTTON_A { base: fallback DPAD_CENTER } key BUTTON_B { base: fallback BACK } key BUTTON_X { base: fallback DPAD_CENTER } key BUTTON_Y { base: fallback BACK } key BUTTON_THUMBL { base: fallback DPAD_CENTER } key BUTTON_THUMBR { base: fallback DPAD_CENTER } key BUTTON_SELECT { base: fallback MENU } key BUTTON_MODE { base: fallback MENU }
data/keyboards/Vendor_18d1_Product_0200.kl 0 → 100644 +71 −0 Original line number Diff line number Diff line # Copyright (C) 2020 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # # Keyboard map for the android virtual remote running as a gamepad # key 0x130 BUTTON_A key 0x131 BUTTON_B key 0x133 BUTTON_X key 0x134 BUTTON_Y key 0x136 BUTTON_L2 key 0x137 BUTTON_R2 key 0x138 BUTTON_L1 key 0x139 BUTTON_R1 key 0x13a BUTTON_SELECT key 0x13b BUTTON_START key 0x13c BUTTON_MODE key 0x13d BUTTON_THUMBL key 0x13e BUTTON_THUMBR key 103 DPAD_UP key 108 DPAD_DOWN key 105 DPAD_LEFT key 106 DPAD_RIGHT # Generic usage buttons key 0x2c0 BUTTON_1 key 0x2c1 BUTTON_2 key 0x2c2 BUTTON_3 key 0x2c3 BUTTON_4 key 0x2c4 BUTTON_5 key 0x2c5 BUTTON_6 key 0x2c6 BUTTON_7 key 0x2c7 BUTTON_8 key 0x2c8 BUTTON_9 key 0x2c9 BUTTON_10 key 0x2ca BUTTON_11 key 0x2cb BUTTON_12 key 0x2cc BUTTON_13 key 0x2cd BUTTON_14 key 0x2ce BUTTON_15 key 0x2cf BUTTON_16 # assistant buttons key 0x246 VOICE_ASSIST key 0x247 ASSIST axis 0x00 X axis 0x01 Y axis 0x02 Z axis 0x05 RZ axis 0x09 RTRIGGER axis 0x0a LTRIGGER axis 0x10 HAT_X axis 0x11 HAT_Y
media/java/android/media/tv/ITvRemoteServiceInput.aidl +7 −1 Original line number Diff line number Diff line Loading @@ -39,4 +39,10 @@ oneway interface ITvRemoteServiceInput { void sendPointerUp(IBinder token, int pointerId); @UnsupportedAppUsage void sendPointerSync(IBinder token); // API specific to gamepads. Close gamepads with closeInputBridge void openGamepadBridge(IBinder token, String name); void sendGamepadKeyDown(IBinder token, int keyCode); void sendGamepadKeyUp(IBinder token, int keyCode); void sendGamepadAxisValue(IBinder token, int axis, float value); }
media/lib/tvremote/java/com/android/media/tv/remoteprovider/TvRemoteProvider.java +155 −8 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.media.tv.remoteprovider; import android.annotation.FloatRange; import android.annotation.NonNull; import android.content.Context; import android.media.tv.ITvRemoteProvider; import android.media.tv.ITvRemoteServiceInput; Loading @@ -24,6 +26,7 @@ import android.os.RemoteException; import android.util.Log; import java.util.LinkedList; import java.util.Objects; /** * Base class for emote providers implemented in unbundled service. Loading Loading @@ -124,27 +127,75 @@ public abstract class TvRemoteProvider { * @param maxPointers Maximum supported pointers * @throws RuntimeException */ public void openRemoteInputBridge(IBinder token, String name, int width, int height, int maxPointers) throws RuntimeException { public void openRemoteInputBridge( IBinder token, String name, int width, int height, int maxPointers) throws RuntimeException { final IBinder finalToken = Objects.requireNonNull(token); final String finalName = Objects.requireNonNull(name); synchronized (mOpenBridgeRunnables) { if (mRemoteServiceInput == null) { Log.d(TAG, "Delaying openRemoteInputBridge() for " + name); Log.d(TAG, "Delaying openRemoteInputBridge() for " + finalName); mOpenBridgeRunnables.add(() -> { try { mRemoteServiceInput.openInputBridge( token, name, width, height, maxPointers); Log.d(TAG, "Delayed openRemoteInputBridge() for " + name + ": success"); finalToken, finalName, width, height, maxPointers); Log.d(TAG, "Delayed openRemoteInputBridge() for " + finalName + ": success"); } catch (RemoteException re) { Log.e(TAG, "Delayed openRemoteInputBridge() for " + finalName + ": failure", re); } }); return; } } try { mRemoteServiceInput.openInputBridge(finalToken, finalName, width, height, maxPointers); Log.d(TAG, "openRemoteInputBridge() for " + finalName + ": success"); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Opens an input bridge as a gamepad device. * Clients should pass in a token that can be used to match this request with a token that * will be returned by {@link TvRemoteProvider#onInputBridgeConnected(IBinder token)} * <p> * The token should be used for subsequent calls. * </p> * * @param token Identifier for this connection * @param name Device name * @throws RuntimeException * * @hide */ public void openGamepadBridge(@NonNull IBinder token, @NonNull String name) throws RuntimeException { final IBinder finalToken = Objects.requireNonNull(token); final String finalName = Objects.requireNonNull(name); synchronized (mOpenBridgeRunnables) { if (mRemoteServiceInput == null) { Log.d(TAG, "Delaying openGamepadBridge() for " + finalName); mOpenBridgeRunnables.add(() -> { try { mRemoteServiceInput.openGamepadBridge(finalToken, finalName); Log.d(TAG, "Delayed openGamepadBridge() for " + finalName + ": success"); } catch (RemoteException re) { Log.e(TAG, "Delayed openRemoteInputBridge() for " + name + ": failure", re); Log.e(TAG, "Delayed openGamepadBridge() for " + finalName + ": failure", re); } }); return; } } try { mRemoteServiceInput.openInputBridge(token, name, width, height, maxPointers); Log.d(TAG, "openRemoteInputBridge() for " + name + ": success"); mRemoteServiceInput.openGamepadBridge(token, finalName); Log.d(TAG, "openGamepadBridge() for " + finalName + ": success"); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } Loading @@ -157,6 +208,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void closeInputBridge(IBinder token) throws RuntimeException { Objects.requireNonNull(token); try { mRemoteServiceInput.closeInputBridge(token); } catch (RemoteException re) { Loading @@ -173,6 +225,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void clearInputBridge(IBinder token) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "clearInputBridge() token " + token); try { mRemoteServiceInput.clearInputBridge(token); Loading @@ -190,6 +243,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendTimestamp(IBinder token, long timestamp) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendTimestamp() token: " + token + ", timestamp: " + timestamp); try { Loading @@ -207,6 +261,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendKeyUp(IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendKeyUp() token: " + token + ", keyCode: " + keyCode); try { mRemoteServiceInput.sendKeyUp(token, keyCode); Loading @@ -223,6 +278,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendKeyDown(IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendKeyDown() token: " + token + ", keyCode: " + keyCode); try { Loading @@ -241,6 +297,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendPointerUp(IBinder token, int pointerId) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendPointerUp() token: " + token + ", pointerId: " + pointerId); try { Loading @@ -262,6 +319,7 @@ public abstract class TvRemoteProvider { */ public void sendPointerDown(IBinder token, int pointerId, int x, int y) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendPointerDown() token: " + token + ", pointerId: " + pointerId); try { Loading @@ -278,6 +336,7 @@ public abstract class TvRemoteProvider { * @throws RuntimeException */ public void sendPointerSync(IBinder token) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) Log.d(TAG, "sendPointerSync() token: " + token); try { mRemoteServiceInput.sendPointerSync(token); Loading @@ -286,6 +345,94 @@ public abstract class TvRemoteProvider { } } /** * Send a notification that a gamepad key was pressed. * * Supported buttons are: * <ul> * <li> Right-side buttons: BUTTON_A, BUTTON_B, BUTTON_X, BUTTON_Y * <li> Digital Triggers and bumpers: BUTTON_L1, BUTTON_R1, BUTTON_L2, BUTTON_R2 * <li> Thumb buttons: BUTTON_THUMBL, BUTTON_THUMBR * <li> DPad buttons: DPAD_UP, DPAD_DOWN, DPAD_LEFT, DPAD_RIGHT * <li> Gamepad buttons: BUTTON_SELECT, BUTTON_START, BUTTON_MODE * <li> Generic buttons: BUTTON_1, BUTTON_2, ...., BUTTON16 * <li> Assistant: ASSIST, VOICE_ASSIST * </ul> * * @param token identifier for the device * @param keyCode the gamepad key that was pressed (like BUTTON_A) * * @hide */ public void sendGamepadKeyDown(@NonNull IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) { Log.d(TAG, "sendGamepadKeyDown() token: " + token); } try { mRemoteServiceInput.sendGamepadKeyDown(token, keyCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Send a notification that a gamepad key was released. * * @see sendGamepadKeyDown for supported key codes. * * @param token identifier for the device * @param keyCode the gamepad key that was pressed * * @hide */ public void sendGamepadKeyUp(@NonNull IBinder token, int keyCode) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) { Log.d(TAG, "sendGamepadKeyUp() token: " + token); } try { mRemoteServiceInput.sendGamepadKeyUp(token, keyCode); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } /** * Send a gamepad axis value. * * Supported axes: * <li> Left Joystick: AXIS_X, AXIS_Y * <li> Right Joystick: AXIS_Z, AXIS_RZ * <li> Triggers: AXIS_LTRIGGER, AXIS_RTRIGGER * <li> DPad: AXIS_HAT_X, AXIS_HAT_Y * * For non-trigger axes, the range of acceptable values is [-1, 1]. The trigger axes support * values [0, 1]. * * @param token identifier for the device * @param axis MotionEvent axis * @param value the value to send * * @hide */ public void sendGamepadAxisValue( @NonNull IBinder token, int axis, @FloatRange(from = -1.0f, to = 1.0f) float value) throws RuntimeException { Objects.requireNonNull(token); if (DEBUG_KEYS) { Log.d(TAG, "sendGamepadAxisValue() token: " + token); } try { mRemoteServiceInput.sendGamepadAxisValue(token, axis, value); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } } private final class ProviderStub extends ITvRemoteProvider.Stub { @Override public void setRemoteServiceInputSink(ITvRemoteServiceInput tvServiceInput) { Loading
media/lib/tvremote/tests/src/com/android/media/tv/remoteprovider/TvRemoteProviderTest.java +48 −0 Original line number Diff line number Diff line Loading @@ -83,4 +83,52 @@ public class TvRemoteProviderTest extends AndroidTestCase { assertTrue(tvProvider.verifyTokens()); } @SmallTest public void testOpenGamepadRemoteInputBridge() throws Exception { Binder tokenA = new Binder(); Binder tokenB = new Binder(); Binder tokenC = new Binder(); class LocalTvRemoteProvider extends TvRemoteProvider { private final ArrayList<IBinder> mTokens = new ArrayList<IBinder>(); LocalTvRemoteProvider(Context context) { super(context); } @Override public void onInputBridgeConnected(IBinder token) { mTokens.add(token); } public boolean verifyTokens() { return mTokens.size() == 3 && mTokens.contains(tokenA) && mTokens.contains(tokenB) && mTokens.contains(tokenC); } } LocalTvRemoteProvider tvProvider = new LocalTvRemoteProvider(getContext()); ITvRemoteProvider binder = (ITvRemoteProvider) tvProvider.getBinder(); ITvRemoteServiceInput tvServiceInput = mock(ITvRemoteServiceInput.class); doAnswer((i) -> { binder.onInputBridgeConnected(i.getArgument(0)); return null; }) .when(tvServiceInput) .openGamepadBridge(any(), any()); tvProvider.openGamepadBridge(tokenA, "A"); tvProvider.openGamepadBridge(tokenB, "B"); binder.setRemoteServiceInputSink(tvServiceInput); tvProvider.openGamepadBridge(tokenC, "C"); verify(tvServiceInput).openGamepadBridge(tokenA, "A"); verify(tvServiceInput).openGamepadBridge(tokenB, "B"); verify(tvServiceInput).openGamepadBridge(tokenC, "C"); verifyNoMoreInteractions(tvServiceInput); assertTrue(tvProvider.verifyTokens()); } }