Use Handlers instead of Timers for one-shot events

This commit is contained in:
Cameron Gutman
2022-09-16 00:08:48 -05:00
parent 9cf27d8fb1
commit 03f9ea8435
5 changed files with 74 additions and 140 deletions

View File

@@ -95,7 +95,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
private int lastButtonState = 0;
// Only 2 touches are supported
private Timer touchTimer;
private final TouchContext[] touchContextMap = new TouchContext[2];
private long threeFingerDownTime = 0;
@@ -479,15 +478,14 @@ public class Game extends Activity implements SurfaceHolder.Callback,
inputManager.registerInputDeviceListener(keyboardTranslator, null);
// Initialize touch contexts
touchTimer = new Timer("TouchTimer", true);
for (int i = 0; i < touchContextMap.length; i++) {
if (!prefConfig.touchscreenTrackpad) {
touchContextMap[i] = new AbsoluteTouchContext(conn, i, streamView, touchTimer);
touchContextMap[i] = new AbsoluteTouchContext(conn, i, streamView);
}
else {
touchContextMap[i] = new RelativeTouchContext(conn, i,
REFERENCE_HORIZ_RES, REFERENCE_VERT_RES,
streamView, prefConfig, touchTimer);
streamView, prefConfig);
}
}
@@ -1031,10 +1029,6 @@ public class Game extends Activity implements SurfaceHolder.Callback,
protected void onDestroy() {
super.onDestroy();
if (touchTimer != null) {
touchTimer.cancel();
}
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
if (controllerHandler != null) {
inputManager.unregisterInputDeviceListener(controllerHandler);

View File

@@ -7,9 +7,6 @@ import android.view.View;
import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.MouseButtonPacket;
import java.util.Timer;
import java.util.TimerTask;
public class AbsoluteTouchContext implements TouchContext {
private int lastTouchDownX = 0;
private int lastTouchDownY = 0;
@@ -22,13 +19,33 @@ public class AbsoluteTouchContext implements TouchContext {
private boolean cancelled;
private boolean confirmedLongPress;
private boolean confirmedTap;
private TimerTask longPressTimerTask;
private TimerTask tapDownTimerTask;
private final Runnable longPressRunnable = new Runnable() {
@Override
public void run() {
// This timer should have already expired, but cancel it just in case
cancelTapDownTimer();
// Switch from a left click to a right click after a long press
confirmedLongPress = true;
if (confirmedTap) {
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
}
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
}
};
private final Runnable tapDownRunnable = new Runnable() {
@Override
public void run() {
// Start our tap
tapConfirmed();
}
};
private final NvConnection conn;
private final int actionIndex;
private final View targetView;
private final Timer timer;
private final Handler handler;
private final Runnable leftButtonUpRunnable = new Runnable() {
@@ -49,12 +66,11 @@ public class AbsoluteTouchContext implements TouchContext {
private static final int TOUCH_DOWN_DEAD_ZONE_TIME_THRESHOLD = 100;
private static final int TOUCH_DOWN_DEAD_ZONE_DISTANCE_THRESHOLD = 20;
public AbsoluteTouchContext(NvConnection conn, int actionIndex, View view, Timer timer)
public AbsoluteTouchContext(NvConnection conn, int actionIndex, View view)
{
this.conn = conn;
this.actionIndex = actionIndex;
this.targetView = view;
this.timer = timer;
this.handler = new Handler(Looper.getMainLooper());
}
@@ -138,69 +154,22 @@ public class AbsoluteTouchContext implements TouchContext {
lastTouchUpTime = eventTime;
}
private synchronized void startLongPressTimer() {
private void startLongPressTimer() {
cancelLongPressTimer();
longPressTimerTask = new TimerTask() {
@Override
public void run() {
synchronized (AbsoluteTouchContext.this) {
// Check if someone cancelled us
if (longPressTimerTask == null) {
return;
}
// Uncancellable now
longPressTimerTask = null;
// This timer should have already expired, but cancel it just in case
cancelTapDownTimer();
// Switch from a left click to a right click after a long press
confirmedLongPress = true;
if (confirmedTap) {
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_LEFT);
}
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
}
}
};
timer.schedule(longPressTimerTask, LONG_PRESS_TIME_THRESHOLD);
handler.postDelayed(longPressRunnable, LONG_PRESS_TIME_THRESHOLD);
}
private synchronized void cancelLongPressTimer() {
if (longPressTimerTask != null) {
longPressTimerTask.cancel();
longPressTimerTask = null;
}
private void cancelLongPressTimer() {
handler.removeCallbacks(longPressRunnable);
}
private synchronized void startTapDownTimer() {
private void startTapDownTimer() {
cancelTapDownTimer();
tapDownTimerTask = new TimerTask() {
@Override
public void run() {
synchronized (AbsoluteTouchContext.this) {
// Check if someone cancelled us
if (tapDownTimerTask == null) {
return;
}
// Uncancellable now
tapDownTimerTask = null;
// Start our tap
tapConfirmed();
}
}
};
timer.schedule(tapDownTimerTask, TOUCH_DOWN_DEAD_ZONE_TIME_THRESHOLD);
handler.postDelayed(tapDownRunnable, TOUCH_DOWN_DEAD_ZONE_TIME_THRESHOLD);
}
private synchronized void cancelTapDownTimer() {
if (tapDownTimerTask != null) {
tapDownTimerTask.cancel();
tapDownTimerTask = null;
}
private void cancelTapDownTimer() {
handler.removeCallbacks(tapDownRunnable);
}
private void tapConfirmed() {

View File

@@ -8,9 +8,6 @@ import com.limelight.nvstream.NvConnection;
import com.limelight.nvstream.input.MouseButtonPacket;
import com.limelight.preferences.PreferenceConfiguration;
import java.util.Timer;
import java.util.TimerTask;
public class RelativeTouchContext implements TouchContext {
private int lastTouchX = 0;
private int lastTouchY = 0;
@@ -21,7 +18,6 @@ public class RelativeTouchContext implements TouchContext {
private boolean confirmedMove;
private boolean confirmedDrag;
private boolean confirmedScroll;
private TimerTask dragTimerTask;
private double distanceMoved;
private double xFactor, yFactor;
private int pointerCount;
@@ -33,9 +29,27 @@ public class RelativeTouchContext implements TouchContext {
private final int referenceHeight;
private final View targetView;
private final PreferenceConfiguration prefConfig;
private final Timer timer;
private final Handler handler;
private final Runnable dragTimerRunnable = new Runnable() {
@Override
public void run() {
// Check if someone already set move
if (confirmedMove) {
return;
}
// The drag should only be processed for the primary finger
if (actionIndex != maxPointerCountInGesture - 1) {
return;
}
// We haven't been cancelled before the timer expired so begin dragging
confirmedDrag = true;
conn.sendMouseButtonDown(getMouseButtonIndex());
}
};
// Indexed by MouseButtonPacket.BUTTON_XXX - 1
private final Runnable[] buttonUpRunnables = new Runnable[] {
new Runnable() {
@@ -79,8 +93,7 @@ public class RelativeTouchContext implements TouchContext {
public RelativeTouchContext(NvConnection conn, int actionIndex,
int referenceWidth, int referenceHeight,
View view, PreferenceConfiguration prefConfig,
Timer timer)
View view, PreferenceConfiguration prefConfig)
{
this.conn = conn;
this.actionIndex = actionIndex;
@@ -88,7 +101,6 @@ public class RelativeTouchContext implements TouchContext {
this.referenceHeight = referenceHeight;
this.targetView = view;
this.prefConfig = prefConfig;
this.timer = timer;
this.handler = new Handler(Looper.getMainLooper());
}
@@ -187,47 +199,16 @@ public class RelativeTouchContext implements TouchContext {
}
}
private synchronized void startDragTimer() {
private void startDragTimer() {
cancelDragTimer();
dragTimerTask = new TimerTask() {
@Override
public void run() {
synchronized (RelativeTouchContext.this) {
// Check if someone already set move
if (confirmedMove) {
return;
}
// The drag should only be processed for the primary finger
if (actionIndex != maxPointerCountInGesture - 1) {
return;
}
// Check if someone cancelled us
if (dragTimerTask == null) {
return;
}
// Uncancellable now
dragTimerTask = null;
// We haven't been cancelled before the timer expired so begin dragging
confirmedDrag = true;
conn.sendMouseButtonDown(getMouseButtonIndex());
}
}
};
timer.schedule(dragTimerTask, DRAG_TIME_THRESHOLD);
handler.postDelayed(dragTimerRunnable, DRAG_TIME_THRESHOLD);
}
private synchronized void cancelDragTimer() {
if (dragTimerTask != null) {
dragTimerTask.cancel();
dragTimerTask = null;
}
private void cancelDragTimer() {
handler.removeCallbacks(dragTimerRunnable);
}
private synchronized void checkForConfirmedMove(int eventX, int eventY) {
private void checkForConfirmedMove(int eventX, int eventY) {
// If we've already confirmed something, get out now
if (confirmedMove || confirmedDrag) {
return;

View File

@@ -14,7 +14,6 @@ import android.view.MotionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
/**
* This is a digital button on screen element. It is used to get click and double click user input.
@@ -42,21 +41,16 @@ public class DigitalButton extends VirtualControllerElement {
void onRelease();
}
/**
*
*/
private class TimerLongClickTimerTask extends TimerTask {
@Override
public void run() {
onLongClickCallback();
}
}
private List<DigitalButtonListener> listeners = new ArrayList<>();
private String text = "";
private int icon = -1;
private long timerLongClickTimeout = 3000;
private TimerLongClickTimerTask longClickTimerTask = null;
private final Runnable longClickRunnable = new Runnable() {
@Override
public void run() {
onLongClickCallback();
}
};
private final Paint paint = new Paint();
private final RectF rect = new RectF();
@@ -175,13 +169,8 @@ public class DigitalButton extends VirtualControllerElement {
listener.onClick();
}
if (longClickTimerTask != null) {
longClickTimerTask.cancel();
longClickTimerTask = null;
}
longClickTimerTask = new TimerLongClickTimerTask();
virtualController.getTimer().schedule(longClickTimerTask, timerLongClickTimeout);
virtualController.getHandler().removeCallbacks(longClickRunnable);
virtualController.getHandler().postDelayed(longClickRunnable, timerLongClickTimeout);
}
private void onLongClickCallback() {
@@ -200,10 +189,7 @@ public class DigitalButton extends VirtualControllerElement {
}
// We may be called for a release without a prior click
if (longClickTimerTask != null) {
longClickTimerTask.cancel();
longClickTimerTask = null;
}
virtualController.getHandler().removeCallbacks(longClickRunnable);
}
@Override

View File

@@ -5,6 +5,8 @@
package com.limelight.binding.input.virtual_controller;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.Button;
@@ -42,6 +44,7 @@ public class VirtualController {
private final ControllerHandler controllerHandler;
private final Context context;
private final Timer timer;
private final Handler handler;
private TimerTask retransmitTimerTask;
private FrameLayout frame_layout = null;
@@ -58,6 +61,7 @@ public class VirtualController {
this.frame_layout = layout;
this.context = context;
this.timer = new Timer("OSC timer", true);
this.handler = new Handler(Looper.getMainLooper());
buttonConfigure = new Button(context);
buttonConfigure.setAlpha(0.25f);
@@ -92,8 +96,8 @@ public class VirtualController {
}
Timer getTimer() {
return timer;
Handler getHandler() {
return handler;
}
public void hide() {