Skip to content

Commit

Permalink
Implemented functionality to move mouse pointer with D-PAD on Android…
Browse files Browse the repository at this point in the history
… TV remote.

Implemented a hint how to pull up the keyboard on Android TV when clicking with D-PAD center.
  • Loading branch information
iiordanov committed Mar 31, 2024
1 parent 26351e1 commit 26fa5cb
Show file tree
Hide file tree
Showing 19 changed files with 222 additions and 59 deletions.
8 changes: 5 additions & 3 deletions bVNC/src/main/java/com/iiordanov/bVNC/ConnectionBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.antlersoft.android.dbimpl.NewInstance;
import com.iiordanov.bVNC.input.TouchInputHandlerDirectSwipePan;
import com.iiordanov.util.NetworkUtils;
import com.undatech.opaque.util.GeneralUtils;
import com.undatech.remoteClientUi.R;

import net.sqlcipher.database.SQLiteDatabase;
Expand Down Expand Up @@ -64,13 +65,14 @@ public ConnectionBean get() {

public ConnectionBean(Context context) {
String inputMode = TouchInputHandlerDirectSwipePan.ID;
Boolean preferSendingUnicode = false;

boolean preferSendingUnicode = false;
boolean useDpadAsArrows = true;
if (context == null) {
context = App.getContext();
}

if (context != null) {
useDpadAsArrows = !GeneralUtils.isTv(context);
inputMode = Utils.querySharedPreferenceString(context, Constants.defaultInputMethodTag,
TouchInputHandlerDirectSwipePan.ID);
preferSendingUnicode = Utils.querySharedPreferenceBoolean(context, Constants.preferSendingUnicode);
Expand Down Expand Up @@ -123,7 +125,7 @@ public ConnectionBean(Context context) {
setPrefEncoding(RfbProto.EncodingTight);
setScaleMode(ScaleType.MATRIX);
setInputMode(inputMode);
setUseDpadAsArrows(true);
setUseDpadAsArrows(useDpadAsArrows);
setRotateDpad(false);
setUsePortrait(false);
setUseLocalCursor(Constants.CURSOR_AUTO);
Expand Down
48 changes: 44 additions & 4 deletions bVNC/src/main/java/com/iiordanov/bVNC/RemoteCanvasActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
//
package com.iiordanov.bVNC;

import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.app.Dialog;
Expand Down Expand Up @@ -156,6 +157,7 @@ public class RemoteCanvasActivity extends AppCompatActivity implements
View rootView;
ActionBarHider actionBarHider = new ActionBarHider();
ActionBarShower actionBarShower = new ActionBarShower();
KeyboardIconShower keyboardIconShower = new KeyboardIconShower();
private Vibrator myVibrator;
private RemoteCanvas canvas;
private RemoteConnection remoteConnection;
Expand All @@ -164,6 +166,8 @@ public class RemoteCanvasActivity extends AppCompatActivity implements
private TouchInputHandler[] inputModeHandlers;
private Connection connection;
public RemoteClientsInputListener inputListener;
private ImageButton keyboardIconForAndroidTv;
float keyboardIconForAndroidTvX = Float.MAX_VALUE;

/**
* This runnable enables immersive mode.
Expand Down Expand Up @@ -223,6 +227,7 @@ public void run() {
private void correctAfterRotation() throws Exception {
Log.d(TAG, "correctAfterRotation");
canvas.waitUntilInflated();

// Its quite common to see NullPointerExceptions here when this function is called
// at the point of disconnection. Hence, we catch and ignore the error.
float oldScale = canvas.canvasZoomer.getZoomFactor();
Expand Down Expand Up @@ -282,6 +287,7 @@ public void onCreate(Bundle icicle) {
setContentView(R.layout.canvas);

canvas = findViewById(R.id.canvas);
keyboardIconForAndroidTv = findViewById(R.id.keyboardIconForAndroidTv);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.setDefaultFocusHighlightEnabled(false);
Expand Down Expand Up @@ -512,6 +518,10 @@ void relayoutViews(View rootView) {
// Use the visible display frame of the decor view to compute notch dimensions.
int rootViewHeight = rootView.getHeight();

if (keyboardIconForAndroidTvX == Float.MAX_VALUE) {
keyboardIconForAndroidTvX = keyboardIconForAndroidTv.getX();
}

int layoutKeysBottom = layoutKeys.getBottom();
int toolbarBottom = toolbar.getBottom();
int rootViewBottom = layoutKeys.getRootView().getBottom();
Expand Down Expand Up @@ -1526,6 +1536,13 @@ public void showActionBar() {
handler.postAtTime(actionBarHider, SystemClock.uptimeMillis() + hideToolbarDelay);
}

public void showKeyboardIcon() {
handler.removeCallbacks(keyboardIconShower);
handler.postAtTime(keyboardIconShower, SystemClock.uptimeMillis() + 50);
handler.removeCallbacks(actionBarHider);
handler.postAtTime(actionBarHider, SystemClock.uptimeMillis() + hideToolbarDelay);
}

@Override
public void onTextSelected(String selectedString) {
android.util.Log.i(TAG, "onTextSelected called with selectedString: " + selectedString);
Expand Down Expand Up @@ -1604,16 +1621,24 @@ public RemoteConnection getRemoteConnection() {

private class ActionBarHider implements Runnable {
public void run() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
Log.d(TAG, "ActionBarHider: Hiding ActionBar");
actionBar.hide();
if (GeneralUtils.isTv(RemoteCanvasActivity.this)) {
keyboardIconForAndroidTv.setVisibility(View.GONE);
} else {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
Log.d(TAG, "ActionBarHider: Hiding ActionBar");
actionBar.hide();
}
}
}
}

private class ActionBarShower implements Runnable {
public void run() {
showActionBar();
}

private void showActionBar() {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
Log.d(TAG, "ActionBarShower: Showing ActionBar");
Expand All @@ -1622,5 +1647,20 @@ public void run() {
}
}

private class KeyboardIconShower implements Runnable {
public void run() {
if (GeneralUtils.isTv(RemoteCanvasActivity.this)) {
animateKeyboardIconForAndroidTv();
}
}

private void animateKeyboardIconForAndroidTv() {
keyboardIconForAndroidTv.setVisibility(View.VISIBLE);
Log.d(TAG, "ActionBarHider: keyboardIconForAndroidTv X position to: " + keyboardIconForAndroidTvX);
keyboardIconForAndroidTv.setX(keyboardIconForAndroidTvX);
ObjectAnimator animation = ObjectAnimator.ofFloat(keyboardIconForAndroidTv, "translationX", -100f);
animation.setDuration(1000);
animation.start();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,5 @@
import android.view.KeyEvent;

public interface PointerInputHandler {
boolean onKeyDownEvent(int keyCode, KeyEvent event);

boolean onKeyUpEvent(int keyCode, KeyEvent event);
boolean onKeyAsPointerEvent(int keyCode, KeyEvent event);
}
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,12 @@ public void handleMessage(final Message msg) {
this.post(() -> Toast.makeText(context, Utils.getStringResourceByName(context, messageText),
Toast.LENGTH_LONG).show());
break;
case RemoteClientLibConstants.SHOW_KEYBOARD:
((RemoteCanvasActivity) context).showKeyboard();
break;
case RemoteClientLibConstants.SHOW_KEYBOARD_ICON:
((RemoteCanvasActivity) context).showKeyboardIcon();
break;
default:
android.util.Log.e(TAG, "Not handling unknown messageId: " + msg.what);
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import android.view.MotionEvent
import android.view.View
import com.iiordanov.bVNC.App
import com.iiordanov.bVNC.Constants
import com.iiordanov.bVNC.RemoteCanvasActivity
import com.undatech.opaque.input.InputConstants
import com.undatech.opaque.util.GeneralUtils
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
Expand All @@ -38,6 +40,7 @@ import java.util.concurrent.Executors
class RemoteClientsInputListener(
val activity: Activity,
private val keyInputHandler: KeyInputHandler?,
private val pointerInputHandler: PointerInputHandler?,
private val touchInputHandler: TouchInputHandler?,
val resetOnScreenKeys: (input: Int) -> Int,
private val useDpadAsArrows: Boolean,
Expand All @@ -54,6 +57,7 @@ class RemoteClientsInputListener(
keyCode, evt
) else activity.onKeyUp(keyCode, evt)
} else if (isTv && keyCode == KeyEvent.KEYCODE_BACK) {
Log.i(tag, "Not capturing back button on Android TV")
return false
}
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
public abstract class RemotePointer extends com.undatech.opaque.input.RemotePointer {
public RemotePointer(
RfbConnectable rfbConnectable, Context context, InputCarriable remoteInput,
Viewable canvas, Handler handler, boolean debugLogging
Viewable canvas, Handler handler, boolean useDpadAsPointer, boolean debugLogging
) {
super(rfbConnectable, context, remoteInput, canvas, handler, debugLogging);
super(rfbConnectable, context, remoteInput, canvas, handler, useDpadAsPointer, debugLogging);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ public class RemoteRdpPointer extends RemotePointer {

public RemoteRdpPointer(
RfbConnectable rfbConnectable, Context context, InputCarriable remoteInput,
Viewable canvas, Handler handler, boolean debugLogging
Viewable canvas, Handler handler, boolean useDpadAsPointer, boolean debugLogging
) {
super(rfbConnectable, context, remoteInput, canvas, handler, debugLogging);
super(rfbConnectable, context, remoteInput, canvas, handler, useDpadAsPointer, debugLogging);
}

private void sendButtonDownOrMoveButtonDown(int x, int y, int metaState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ public RemoteSpicePointer(
InputCarriable remoteInput,
Viewable canvas,
Handler handler,
boolean useDpadAsArrows,
boolean debugLogging
) {
super(protocomm, context, remoteInput, canvas, handler, debugLogging);
super(protocomm, context, remoteInput, canvas, handler, useDpadAsArrows, debugLogging);
}

@Override
Expand Down Expand Up @@ -140,6 +141,8 @@ private void clearPointerMaskEvent(int x, int y, boolean isMoving, int combinedM
* @param isMoving
*/
private void sendPointerEvent(int x, int y, int metaState, boolean isMoving) {
GeneralUtils.debugLog(this.debugLogging, TAG, "sendPointerEvent: x: " + x +
", y: " + y + ", metaState: " + metaState + ", isMoving: " + isMoving);

int combinedMetaState = metaState | remoteInput.getKeyboard().getMetaState();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ public RemoteVncPointer(
InputCarriable remoteInput,
Viewable canvas,
Handler handler,
boolean useDpadAsArrows,
boolean debugLogging
) {
super(rfb, context, remoteInput, canvas, handler, debugLogging);
super(rfb, context, remoteInput, canvas, handler, useDpadAsArrows, debugLogging);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import com.iiordanov.bVNC.SSHConnection;
import com.iiordanov.bVNC.Utils;
import com.iiordanov.bVNC.input.KeyInputHandler;
import com.iiordanov.bVNC.input.PointerInputHandler;
import com.iiordanov.bVNC.input.RemoteKeyboard;
import com.undatech.opaque.Connection;
import com.undatech.opaque.InputCarriable;
Expand All @@ -58,7 +59,7 @@
import java.util.Map;
import java.util.Timer;

abstract public class RemoteConnection implements KeyInputHandler, InputCarriable {
abstract public class RemoteConnection implements PointerInputHandler, KeyInputHandler, InputCarriable {

private final static String TAG = "RemoteConnection";

Expand Down Expand Up @@ -381,4 +382,8 @@ public boolean onKeyUpEvent(int keyCode, KeyEvent e) {
public boolean canUpdateColorModelConnected() {
return false;
}

public boolean onKeyAsPointerEvent(int keyCode, KeyEvent event) {
return pointer.hardwareButtonsAsMouseEvents(keyCode, event, 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.iiordanov.bVNC.protocol

import android.content.Context
import android.util.Log
import android.view.KeyEvent
import com.iiordanov.bVNC.App
import com.iiordanov.bVNC.COLORMODEL
import com.iiordanov.bVNC.Constants
Expand Down Expand Up @@ -35,7 +36,7 @@ open class RemoteOpaqueConnection(
!Utils.isFree(context) && connection.isUsbEnabled, App.debugLog
)
rfbConn = spiceComm
pointer = RemoteSpicePointer(spiceComm, context, this, canvas, handler, App.debugLog)
pointer = RemoteSpicePointer(spiceComm, context, this, canvas, handler, !connection.useDpadAsArrows, App.debugLog)
try {
keyboard = RemoteSpiceKeyboard(
context.resources, spiceComm, canvas, this,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class RemoteRdpConnection(
App.debugLog
)
rfbConn = rdpComm
pointer = RemoteRdpPointer(rfbConn, context, this, canvas, handler, App.debugLog)
pointer = RemoteRdpPointer(rfbConn, context, this, canvas, handler, !connection.useDpadAsArrows, App.debugLog)
keyboard = RemoteRdpKeyboard(
rdpComm, canvas, this, handler, App.debugLog,
connection.preferSendingUnicode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class RemoteSpiceConnection(
App.debugLog
)
rfbConn = spiceComm
pointer = RemoteSpicePointer(spiceComm, context, this, canvas, handler, App.debugLog)
pointer = RemoteSpicePointer(spiceComm, context, this, canvas, handler, !connection.useDpadAsArrows, App.debugLog)
keyboard = RemoteSpiceKeyboard(
context.resources, spiceComm, canvas, this,
handler, connection.layoutMap, App.debugLog
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class RemoteVncConnection(
App.debugLog
)
rfbConn = rfb
pointer = RemoteVncPointer(rfbConn, context, this, canvas, handler, App.debugLog)
pointer = RemoteVncPointer(rfbConn, context, this, canvas, handler, !connection.useDpadAsArrows, App.debugLog)
val rAltAsIsoL3Shift = Utils.querySharedPreferenceBoolean(
this.context,
Constants.rAltAsIsoL3ShiftTag
Expand Down
Loading

0 comments on commit 26fa5cb

Please sign in to comment.