/**
* Copyright (C) 2013- Iordan Iordanov
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
package com.undatech.opaque.input;
import android.view.MotionEvent;
import android.os.Vibrator;
import com.undatech.opaque.R;
import com.undatech.opaque.RemoteCanvas;
import com.undatech.opaque.RemoteCanvasActivity;
import com.undatech.opaque.input.RemotePointer;
public class InputHandlerTouchpad extends InputHandlerGeneric {
static final String TAG = "InputHandlerTouchpad";
public static final String ID = "Touchpad";
float sensitivity = 0;
boolean acceleration = false;
public InputHandlerTouchpad(RemoteCanvasActivity activity, RemoteCanvas canvas, Vibrator myVibrator) {
super(activity, canvas, myVibrator);
acceleration = activity.getAccelerationEnabled();
sensitivity = activity.getSensitivity();
}
/*
* (non-Javadoc)
* @see com.undatech.opaque.input.InputHandler#getDescription()
*/
@Override
public String getDescription() {
return canvas.getResources().getString(R.string.input_method_touchpad_description);
}
/*
* (non-Javadoc)
* @see com.undatech.opaque.input.InputHandler#getId()
*/
@Override
public String getId() {
return ID;
}
/*
* (non-Javadoc)
* @see android.view.GestureDetector.SimpleOnGestureListener#onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float)
*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
RemotePointer p = canvas.getPointer();
final int action = e2.getActionMasked();
final int meta = e2.getMetaState();
// If we are scaling, allow panning around by moving two fingers around the screen
if (inScaling) {
float scale = canvas.getZoomFactor();
activity.showKbdIcon();
canvas.relativePan((int)(distanceX*scale), (int)(distanceY*scale));
} else {
// TODO: This is a workaround for Android 4.2
boolean twoFingers = false;
if (e1 != null)
twoFingers = (e1.getPointerCount() > 1);
if (e2 != null)
twoFingers = twoFingers || (e2.getPointerCount() > 1);
// onScroll called while scaling/swiping gesture is in effect. We ignore the event and pretend it was
// consumed. This prevents the mouse pointer from flailing around while we are scaling.
// Also, if one releases one finger slightly earlier than the other when scaling, it causes Android
// to stick a spiteful onScroll with a MASSIVE delta here.
// This would cause the mouse pointer to jump to another place suddenly.
// Hence, we ignore onScroll after scaling until we lift all pointers up.
if (twoFingers||inSwiping||scalingJustFinished)
return true;
activity.showKbdIcon();
// If the gesture has just began, then don't allow a big delta to prevent
// pointer jumps at the start of scrolling.
if (!inScrolling) {
inScrolling = true;
distanceX = getSign(distanceX);
distanceY = getSign(distanceY);
distXQueue.clear();
distYQueue.clear();
}
distXQueue.add(distanceX);
distYQueue.add(distanceY);
// Only after the first two events have arrived do we start using distanceX and Y
// In order to effectively discard the last two events (which are typically unreliable
// because of the finger lifting off).
if (distXQueue.size() > 2) {
distanceX = distXQueue.poll();
distanceY = distYQueue.poll();
} else {
return true;
}
// Make distanceX/Y display density independent.
distanceX = sensitivity * distanceX / displayDensity;
distanceY = sensitivity * distanceY / displayDensity;
// Compute the absolute new mouse position.
int newX = (int) (p.getX() + getDelta(-distanceX));
int newY = (int) (p.getY() + getDelta(-distanceY));
p.moveMouseButtonUp(newX, newY, meta);
}
canvas.movePanToMakePointerVisible();
return true;
}
/*
* (non-Javadoc)
* @see android.view.GestureDetector.SimpleOnGestureListener#onDown(android.view.MotionEvent)
*/
@Override
public boolean onDown(MotionEvent e) {
panRepeater.stop();
return true;
}
/*
* (non-Javadoc)
* @see com.undatech.opaque.input.InputHandlerGeneric#getX(android.view.MotionEvent)
*/
protected int getX (MotionEvent e) {
RemotePointer p = canvas.getPointer();
if (dragMode || rightDragMode || middleDragMode) {
float distanceX = e.getX() - dragX;
dragX = e.getX();
// Compute the absolute new X coordinate.
return (int) (p.getX() + getDelta(distanceX));
}
dragX = e.getX();
return p.getX();
}
/*
* (non-Javadoc)
* @see com.undatech.opaque.input.InputHandlerGeneric#getY(android.view.MotionEvent)
*/
protected int getY (MotionEvent e) {
RemotePointer p = canvas.getPointer();
if (dragMode || rightDragMode || middleDragMode) {
float distanceY = e.getY() - dragY;
dragY = e.getY();
// Compute the absolute new Y coordinate.
return (int) (p.getY() + getDelta(distanceY));
}
dragY = e.getY();
return p.getY();
}
/**
* Computes how far the pointer will move.
* @param distance
* @return
*/
private float getDelta(float distance) {
float delta = (float) (distance * Math.cbrt(canvas.getZoomFactor()));
return computeAcceleration(delta);
}
/**
* Computes the acceleration depending on the size of the supplied delta.
* @param delta
* @return
*/
private float computeAcceleration (float delta) {
float origSign = getSign(delta);
delta = Math.abs(delta);
if (delta <= 15) {
delta = delta * 0.75f;
} else if (acceleration && delta <= 70.0f ) {
delta = delta * delta / 20.0f;
} else if (acceleration) {
delta = delta * 4.5f;
}
return origSign * delta;
}
}