How to inject input events into own application on Android? - android

I look for a way how to inject input events such as touch or keyboard events into the own application, from code within that particular app. NOT into an Android OS or to other apps b/c that needs a signature-level permission android.permission.INJECT_EVENTS or rooted device and I have to avoid both. I look for a code that runs on a non-rooted device without anyhow elevated rights.
I'm somehow making a bit of progress with code based on the following concept:
View v = findViewById(android.R.id.content).getRootView();
v.dispatchTouchEvent(e);

I think getRootView and dispatch it should works well, if there is any problem with it. Please just brief it.
Or there is an more high level but tricky way to do this.
in View.java, there is a hide api called getViewRootImpl() which will return a object of android.view.ViewRootImpl of this Activity. And there is a method called enqueueInputEvent(InputEvent event) on it. So that you can use reflection to get this method.
As I have just finished a project which using Keyboard to send touch commands to the screen, I think when you are trying to inject these kind of events to the activity, multiple touch might be a problem. When you try to send another finger to the screen. The event action must be ACTION_POINTER_DOWN with the index shift of the array named properties. And you should also handle the properties and coordses arrays which including the info of all the fingers each time, even if you just need to move only one finger at this time. The finger's id which is set to properties[i].id is used to identify your finger. But the index of the array is not solid.
for (int i = 0; i < size; i++) {
properties[i].id = fingerId;
properties[i].toolType = MotionEvent.TOOL_TYPE_FINGER;
coordses[i].x = currentX;
coordses[i].y = currentY;
coordses[i].pressure = 1;
coordses[i].size = DEFAULT_SIZE;
}
event = MotionEvent.obtain(
mDownTime, SystemClock.uptimeMillis(),
((size - 1) << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
+ MotionEvent.ACTION_POINTER_DOWN,
size,
properties, coordses, DEFAULT_META_STATE, DEFAULT_BUTTON_STATE,
DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID,
DEFAULT_EDGE_FLAGS, InputDevice.SOURCE_TOUCHSCREEN, DEFAULT_FLAGS);

I managed to solve that in a way that it handles not only a Activity root window but also to a windows that are created for various overlays such as menus and dialogs.
The code is here: https://github.com/TeskaLabs/Remote-Access-Android/blob/master/tlra/src/main/java/com/teskalabs/tlra/android/inapp/InAppInputManager.java

In our own application you can just use the input cli command:
public static final void tapButton (int x, int y) throws IOException, InterruptedException {
Runtime.getRuntime().exec("input tap " + x + " " + y);
}

Related

Libgdx for loop lag

I have problems with my game. The game runs ok until the time where I have to reposition the objects, which happens every frame. For example here I have to reposition some parked cars (around 6 cars on each side of the road), the cars are constantly moving down in the screen, then they reset and reposition at the top again. I have used Poolable to save resources. However, the game is still very jumpy when it comes to running these lines of code. Is there any way I could improve the code to prevent lag? Maybe a new thread? If so how would be the right way of creating a new thread and updating it every frame.
private void updateParkedVehicles(float delta){
for (int i = 0; i < parkedCarLeft1Array.size; i++){
parkedCarLeft1Array.get(i).update(delta);
for (int c = 0; c < parkedCarLeft1Array.size; c++){
if (c != i){
if (Intersector.overlaps(parkedCarLeft1Array.get(i).bounds, parkedCarLeft1Array.get(c).bounds)){
parkedCarLeft1Array.get(i).reset();
}
}
}
}
for (int i = 0; i < parkedCarRight1Array.size; i++){
parkedCarRight1Array.get(i).update(delta);
for (int c = 0; c < parkedCarRight1Array.size; c++){
if (c != i){
if (Intersector.overlaps(parkedCarRight1Array.get(i).bounds, parkedCarRight1Array.get(c).bounds)){parkedCarRight1Array.get(i).reset();
}
}
}
}
}
One way to handle items in your game is through the use of the Scene2d.
Scene2D allows you to step out from having to move yourself the items. Instead you give them instructions (as a director would) if and when you need to move them instead of having to handle yourselve every frame update.
in a nutshell, to add an Action you would do this:
if(SomeCondition){
MoveToAction moveCar = new MoveToAction();
moveCar.setPosition(anX,anY);
moveCar.setDuration(aDuration);
myCarActor.addAction(moveCar);
}
That way, the only thing you need to check for is the occurence of the event and you call your function to move the car only once (when the event occurs).
LibGdx will take care of updating your actor each frame.
The only thing you will need to do in your loop is to check the collision. If they occur then you will assign a new Action to your actor.
Another thing to keep in Mind, if you use Scene2D is that you can use the Camera. This means that if you need to scroll, instead of moving each item, you simply move the camera (which has all the convenience method you need to zoom etc).
For more information on Scene2D check out: https://github.com/libgdx/libgdx/wiki/scene2d
For more information specifically on Actions check this out:
https://github.com/libgdx/libgdx/wiki/Scene2d#actions
Hope it helps.
It seems to me that the problem is with double looping through arrays --> O(n^2) complexity!!
It's not clear for me why you need to check overlapping of each car with each car except this car if "the cars are constantly moving down in the screen, then they reset and reposition at the top again". For the described behaviour the simplest way would be to check is the current y position is less that 0 and rest position if so:
for (Car car : parkedCarLeft1Array){
car.update(delta);
if (car.getY <= 0) {
car.reset();
}
}
I assume that you update car position in update() method.

Android How to detect swipe between two touch points on image view

I have an Imageview with different number of touch points on them. It basically an app which is detecting the swipe between 2 touch points and not allowing the user to swipe any other point or in or out of other direction. It should constrict user to just swipe between two touch points.
Just take a look at following picture:
Now the user should start swiping from point 1 to point 2. if the swipe is not started from starting point 1, it should not color the path between point 1 and point 2.
But if the user successfully swipe between the point 1 and point 2 now swipe between point 2 to 3 should be enabled. Thus user should go through Point 1 to 2, Point 2 to 3 , Point 3 to 4 , point 4 to point 5 to complete round 1.
Please tell me how to achieve this functionality . I know about gestures, gesture overlay etc but none of them fits to my condition as they uses general touch events and gesture directions.
Please suggest me the way to achieve this and keep in mind I want to make this app to be able to run on all type of devices , so I can simply give the hard coded x,y values.
Edit : on Demand
I am posting the link of the app on play store who has same functionality , But I do not know How they are achieving this functionality .
https://play.google.com/store/apps/details?id=al.trigonom.writeletters
If each touch point can be created as individual views(e.g. ImageView), then you can create an inViewInBounds() function.
I used code from here to create code where I needed to detect finger press movement over multiple imageViews:
Rect outRect = new Rect();
int[] location = new int[2];
//Determine if a touch movement is currently over a given view.
private boolean inViewInBounds(View view, int x, int y){
view.getDrawingRect(outRect);
view.getLocationOnScreen(location);
outRect.offset(location[0], location[1]);
return outRect.contains(x, y);
}
To use this function, on a parent view to all those child touch points, set the Click and Touch Listeners:
//This might look redundant but is actually required: "The empty OnClickListener is required to keep OnTouchListener active until ACTION_UP"
parentView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {}
});
//All the work gets done in this function:
parentView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int x = (int)event.getRawX();
int y = (int)event.getRawY();
// ** myTouchPoint might be an array that you loop through here...
if ( inViewInBounds(myTouchPoint, x, y) ) doLogic(myTouchPoint);
return false;
}
});
The code above only shows detecting when one of your views are 'touched'.
if none are 'touched' but a view is 'active' (e.g. When a touch is detected, set a variable like: viewLastTouched = myTouchPoint) then you would call something like drawingLine(viewLastTouched, x, y) function - for whatever it needed to do to draw the line and/or detect boundaries etc.
They are not using android native java code to build this app.
The app is running with this code
import Runtime.MMFRuntime;
public class Main extends MMFRuntime {
}
This in turn is from https://github.com/ClickteamLLC/android/blob/master/docs/index.md
This is used to package apps / games written using - http://www.clickteam.com/clickteam-fusion-2-5

Accessing event input nodes in Android withour rooting

I want to be able to inject different events into an Android device. After some search, I found that I can do this by accessing event input nodes in Android OS, which are found in dev/input/eventX. Once these are accessed, read and write operations can take place, and hence I can inject events.
The problem is that these nodes are only accessible in a rooted device. If I try to use them without rooting, the process will fail as mentioned in this article,
http://www.pocketmagic.net/programmatically-injecting-events-on-android-part-2/
I don't want to root the device to preserve its warranty. I've searched the web for possible ways to accessing Android OS, but I only found rooting.
The alternative way which I think it would work is compiling the application as a system application, but I couldn't found whether this will allow it to have access (both read and write privileges) to event input nodes. Will this method provide these privileges?
If not, is there any alternative way to rooting, where I can give system permissions to my application without rooting the device?
Any help is appreciated.
Thanks.
EDIT: To elaborate more, I want to inject different touch events. For example, single touch, swipe, etc.
You can inject input events on a device by executing the /system/bin/input utility that ships with Android. You can see some examples of it being used (via adb) in this question. The input utility does not appear to need any special privileges to execute.
To create a system application, you need access to the signing keys used when the Android OS for your device was built - you can't just modify an ordinary App to give it system privileges. Even if you could, it wouldn't give you root access (although you could probably make it part of the input user group which the /dev/input/eventX devices also appear to allow access to).
If you want to inject touch events, you can either execute the /system/bin/input utility using the exec() method of the Java Runtime class or just use the injectMotionEvent() method in InputManager.
Below is a method taken from the Android source showing how to inject a MotionEvent - you can view the full source for more info.
/**
* Builds a MotionEvent and injects it into the event stream.
*
* #param inputSource the InputDevice.SOURCE_* sending the input event
* #param action the MotionEvent.ACTION_* for the event
* #param when the value of SystemClock.uptimeMillis() at which the event happened
* #param x x coordinate of event
* #param y y coordinate of event
* #param pressure pressure of event
*/
private void injectMotionEvent(int inputSource, int action, long when, float x, float y, float pressure) {
final float DEFAULT_SIZE = 1.0f;
final int DEFAULT_META_STATE = 0;
final float DEFAULT_PRECISION_X = 1.0f;
final float DEFAULT_PRECISION_Y = 1.0f;
final int DEFAULT_DEVICE_ID = 0;
final int DEFAULT_EDGE_FLAGS = 0;
MotionEvent event = MotionEvent.obtain(when, when, action, x, y, pressure, DEFAULT_SIZE,
DEFAULT_META_STATE, DEFAULT_PRECISION_X, DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID,
DEFAULT_EDGE_FLAGS);
event.setSource(inputSource);
Log.i(TAG, "injectMotionEvent: " + event);
InputManager.getInstance().injectInputEvent(event,
InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
}
These methods only allow you to inject events into your own app windows.
If you want to inject events into other windows not owned by your app, you need to declare additional permissions (READ_INPUT_STATE and INJECT_EVENTS) in your app manifest and sign your App with the Android OS signing keys. In other words, the permissions needed to inject events into other apps are never granted to ordinary apps (for obvious reasons).

Android will treat fast single touch as move?

I write a very simple android application, that I can draw something on the pad. Touch the screen with a finger, you will see a green ball, move your finger, you will see a red line.
But I found a very strange thing: If I touch the screen with two fingers one by one very fast, it will draw a line between them! (Imaging you are pressing two keys jkjkjkjkjkjjkjkjkjkjkjkj on the keyboard)
The key code is pretty simple:
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
multiTouch = false;
id = event.getPointerId(0);
PointF p = getPoint(event, 0);
path = new Path();
path.moveTo(p.x, p.y);
paths.add(path);
points.add(copy(p));
break;
case MotionEvent.ACTION_POINTER_DOWN:
multiTouch = true;
for (int i = 0; i < event.getPointerCount(); i++) {
int tId = event.getPointerId(i);
if (tId != id) {
points.add(getPoint(event, i));
}
}
break;
case MotionEvent.ACTION_MOVE:
if (!multiTouch) {
p = getPoint(event, 0);
path.lineTo(p.x, p.y);
}
break;
}
invalidate();
return true;
}
The full source is here: https://github.com/freewind/TouchTest/blob/master/src/com/example/MyImageView.java
And it's a working demo: https://github.com/freewind/TouchTest
Or you can just download the signed apk on your android device, and test it yourself: https://github.com/freewind/TouchTest/blob/master/TouchTest.apk?raw=true
You can see in my code, I have checked if it's multi touch and disabled drawing on that case.
My android version is 4.0, and my code target is 2.3.3
There is a picture on my android pad:
You can see there are some lines but it should not be, there should be a green ball on the left of the red line instead.
I'm not sure why android treat fast single touch as moving, I considered 3 reasons:
My code has something wrong
Android sdk has something wrong
My android pad has something wrong, e.g. missing a ACTION_DOWN event
How to find out the real reason?
UPDATE
One of my friend used his android mobile(android 2.1) to test this app and found there is no red line, another used android 2.3.5 and found there are red lines.
Please review my code, I have checked multi-touch by ACTION_POINTER_DOWN, and will do nothing on ACTION_MOVE if there are more than 1 points. So the id of point is not needed. (Actually, in my first version of this code, I used id but have the same issue).
And I don't think this is an expected behavior, because it made the development of touching programs hard. I found this issue because in my another application(user can drag/zoom/rotate an image by fingers), the image sometimes "jump" on screen.
I even tried a popular game (Fruit Ninja) on my android pad and iTouch, and found android version has the issue but iTouch doesn't.
Now I'm sure there is something wrong (missing an ACTION_UP event when the first finger ups), but I still don't know what causes it. My android pad? Or Android sdk?
That is way it works for multitouch. When you press fast android handle it as gesture, and you will have 2 pressed pointers. To avoid it try to handle action_up, or use action_pointer_down instead
you can check the id of the touch , so that you will handle only the first touch alone.
alternatively , you can monitor all touches and handle them together .

Android ACTION_MOVE Threshold

I'm writing an app that involves writing on the screen using one's finger, or eventually a stylus. I have that part working. On ACTION_DOWN, starts drawing; on ACTION_MOVE, adds line segments; on ACTION_UP, finishes line.
The problem is that after ACTION_DOWN, apparently the pointer needs to move more than 10 pixels away from where it started (basically a 20x20 box around the starting point) in order to begin sending ACTION_MOVE events. After leaving the box, the move events are all quite accurate. (I figured out the 10 pixel thing by testing it.) Since this is meant to be used for writing or drawing, 10 pixels is a fairly significant loss: depending on how small you're trying to write, you can lose the first letter or two. I haven't been able to find anything about it - only a couple posts on a forum or two, like http://android.modaco.com/topic/339694-touch-input-problem-not-detecting-very-small-movements/page_pid_1701028#entry1701028. It seems to be present on some devices or systems and not others. No ideas as to how to get rid of it when you have it, though.
I'm using a Galaxy Tab 10.1, with Android 3.1. I've tried several different things to try to get rid of it: I've tried setting the event's coords to something else to see if I could trick it into thinking the cursor was in a different place; I tried re-dispatching the event with the coords changed (my handler reacted to the new points, but still didn't respond to movements in the 10-pixel radius.) I've searched through source code for any references to the effect, and found none (though I think it's from a different version of Android - code for 3.1 isn't released yet, is it?) I've searched for methods of querying the current state of the pointers, so I could just have a timer catch the changes until the pointer crossed the threshold. Couldn't find any way of getting pointer coords without a corresponding movement event. Nothing worked. Does anybody know anything about this, or have any ideas or work-arounds? Thank you.
-- Update: Drag and drop events show the same threshold.
I agree in part with the post by #passsy but come to a different conclusion. Firstly as mentioned, the mTouchSlop is the value that we are interested in and is exposed via ViewConfiguration.get(context).getScaledTouchSlop();
If you check the Android source for the ViewConfiguraton, the default value for TOUCH_SLOP is 8dip, but the comments mention that this value is a fallback only, and the actual value is defined when the Android OS for that specific device is built. (it may be more or less than this value. It appears to hold true for the Galaxy Tab devices)
More specific to the code sample, the mTouchSlop value is read from the ViewConfiguration when the View is initialised, but the value is only accessed in the onTouchEvent method. If you extend View and override this method (without calling super) then the behaviour of mTouchSlop in the View class is no longer relevant.
More telling (to us) was that when changing the Android settings to overlay touch events on the screen, a touch with a small drag does not register as a motion event, highlighted by the fact that the crosshairs from the Android OS do not move. From this our conclusion is that the minimal drag distance is being enforced at the OS level and your application will never be aware of drag events smaller than the TOUCH_SLOP value. You should also be aware that TOUCH_SLOP should not be used directly and the API deprecates the getTouchSlop method and recommends getScaledTouchSlop which takes the device screen size and pixel density into account. A side effect of this is that the actual minimum stroke length as perceived on different devices may vary. eg on a Galaxy Tab 2.0 7.0" it feels like we are able to draw shorter minimum strokes using the same code base than when running on a Galaxy Tab 2.0 10.1"
You should also be aware that (if you find a way to alter this value), this value determines how Android systems distinguish between taps and strokes. That is if you tap the screen but your finger moves slightly while performing the tap, it will be interpreted as a tap if it moved less than TOUCH_SLOP, but as a stroke if it moved more than TOUCH_SLOP. Therefore setting TOUCH_SLOP to a smaller value will increase the chance that a tap will be interpreted as a stroke.
Our own conclusion is that this minimum distance is not something that can be changed in practice and is something we need to live with.
The problem is on Line 6549 in class View https://github.com/android/platform_frameworks_base/blob/master/core/java/android/view/View.java
if (!pointInView(x, y, mTouchSlop)) {...}
/**
* Utility method to determine whether the given point, in local coordinates,
* is inside the view, where the area of the view is expanded by the slop factor.
* This method is called while processing touch-move events to determine if the event
* is still within the view.
*/
private boolean pointInView(float localX, float localY, float slop) {
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
mTouchSlop is set in the constructor
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
You can extend View and set mTouchSlop to zero. I don't see an other way to set mTouchSlop. There is no function like getApplicationContext.setScaledTouchSlop(int n).
Extend View Class.
Override pointInView method without "#Override" annotaion and set touchSlop = 0:
public boolean pointInView(float localX, float localY, float slop) {
slop = 0;
return localX >= -slop && localY >= -slop && localX < (getWidth() + slop) &&
localY < (getBottom() + slop);
}

Categories

Resources