I'm wondering how apps like SwipePad and Wave Launcher are able to detect touch gestures/events simply through a service. These apps are able to detect a touch gestures even though it is not in their own Activity. I've looked all over the Internet and haven't found how they can do that.
My main question is how a service can listen in on touch guestures/events just as a regular Activity may receive MotionEvents even though it may not be in the original Activity or context. I'm essentially trying a build an app that will recongize a particular touch gesture from a user regardless which Activity is on top and do something when that gesture is recongized. The touch recongition will be a thread running in the background as a service.
I had this same problem and I've finally figured it out! Thanks to this post: Creating a system overlay window (always on top). You need to use an alert window instead of an overlay (and this also means you can use it in Andoid ICS):
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL|WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
Then just attach a GestureListener in this manner:
GestureDetector gestureDetector = new GestureDetector(this, new AwesomeGestureListener());
View.OnTouchListener gestureListener = new View.OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
};
overlayView.setOnTouchListener(gestureListener);
Yay!
Interesting question. I don't know how they did that and I found google group posts which tell me that there is no global touch listener. But I have an idea anyways...
I found this post where someone succeeds to display a popupwindow from a service. If I would make that popup transparent and fullscreen, I'm sure I could capture the touches since I'm allowed to set a touch interceptor.
Edit: Please report results when you try that, would be interesting to know if this works...
I tested every possible solution but nothing worked some didn't fired touch event which did they frozed the screen .
So I did some reverse engineering and now posting solution which works
WindowManager.LayoutParams params = new android.view.WindowManager.LayoutParams(0, 0, 0, 0, 2003, 0x40028, -3);
View mView = new View(this);
mView.setOnTouchListener(this);
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mView, params);
I tested this on API 21 and a nexus 5 and I was able to log, from a service, when a touch event was fired by the system. However, the data inside the MotionEvent object, for example coordinates, returns 0.0 when OUTSIDE of what seems to be my app's package.
Two sources
integration,
permission
I'm curious as to why the event.getX(), event.getY() and event.getPressure() return 0.0 when not in an activity of the app where the service lives.
Edit
This solution does not prevent other listeners from receiving the touch event capured by the service because of
public boolean onTouch(View v, MotionEvent e){
Log.d(LOG_TAG, "Touch event captured");
return false;
By returning false, it allows other listeners to receive the event.
Edit2
Apparently, the input dispatcher in the OS will set the coordinates and pressure to 0 if the current activity and the listener do not share an UID, here is source
I've searched through many SO threads, but for security reasons, I don't think this is possible for all packages on the system. It certainly is for your own app though.
WindowManager
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
Floating / Overlay layout
floatyView = ((LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate
(R.layout.floating_view, null);
floatyView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(TAG, "event.x " + event.getX());
Log.d(TAG, "event.y " + event.getY());
if (event.getAction() == MotionEvent.ACTION_UP) {
v.performClick();
}
return false;
}
});
Layout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#android:color/transparent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
Related
I am trying to create a calculator. I am working in the latest Android Studio. Like in calculators, all new tokes(numbers, operators) should be shown in the right and if the field is larger than the display, it should scroll to the latest token. I have already browsed and found a way to do the same.
The code for the same is:
private void scrollRight() {
horizontalScrollView = (HorizontalScrollView) findViewById(R.id.horizontalScrollView);
ViewTreeObserver viewTreeObserver = horizontalScrollView.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
horizontalScrollView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
horizontalScrollView.scrollTo(entry.getWidth(), 0);
}
});
}
scrollRight is called by a onClick event which checks if a token is entered and calls this.
Everything is working perfectly, i.e. on every new token the scroll bar is scrolling to the end. But if I try to scroll to the beginning manually, it no longer works. After this every time a new token is pressed, the scroll bar first moves to the end and then back to the beginning.
The only option that remains is to restart the program. I tried debugging the OnGlobalLayout function but the debugger loses all frames while stepping out from the function, so it is difficult to know what exactly is making the scroll bar go to the beginning.
GIF to show the problem:
Please Help!
Try
horizontalScrollView.post(new Runnable() {
#Override
public void run() {
horizontalScrollView.fullScroll(View.FOCUS_RIGHT);
}
});
And call findViewById only once in onCreate.
As the title says, is there a method in libGDX which returns the touch pressure (similar to MotionEvent.getPressure() in Android) ?
Or is there any other way I can get touch pressure in my game which works on both android and iOS(using RoboVM)?
I can't help you with the iOS part
but for android there is a work around
you can do something like this to get the pressure
View view = initializeForView(game, config);
view.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
getInput().onTouch(v, event);
game.setPressure(event.getPressure());
Log.v("log", "hooked " + event.getPressure());
return true;
}
});
and still have the complete event handling of libgdx
I'm having a problem with my on touch listener - it works fine when the animation isn't running but wont work during the animation.
In case it is relevant the only method I'm using is the onFling() method.
I set up the touch listener and animation(to make an image view move down the screen):
image.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
and the Animation:
Animation moveDown = new TranslateAnimation(image.getX(), image.getX(), 0, pxHeight-pxImageHeight/2);
I have an onAnimationListener set up and have tried moving the onTouchListener to different parts of the code (such as onCreate, the onStart method of the animation and others)
In short, I want to be able to swipe my image during the animation of the imageView moving down the screen
Thanks
I am pretty new to android and am trying to build an app where the user can draw a letter, press a button that connects a service that reads it, and then the letter is displayed back to them.
This is my main layout:
!(http://i58.photobucket.com/albums/g271/billmoney3/layout_zps71eb45ca.jpg)
I want the user to be able to draw in the blue area. I made the blue area a custom view called InnerDrawingView. I need help on how to organize the views and the OnTouchListener.
Right now I have this java code:
public class DoodleActivity extends Activity {
Button confirmButton;
EditText drawingResult;
InnerDrawingView innerView;
// on create
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_doodle); // main layout
// where the drawing happens
innerView = (InnerDrawingView) findViewById(R.id.innerDrawingView1);
innerView.setOnTouchListener(handleTouch);
...
...
// handle the touching of the inner view
private OnTouchListener handleTouch = new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
drawingResult.setText("O RLY?!"); // just a test
return true;
}
}; // end of class
Is this the correct way to go about it? What kind of touch listener do I put in the InnerDrawingView class? Can I just call: innerView.onTouch() from inside my main activity's onTouch()? Or the onDraw() method? If someone could direct me to a good paint tutorial also that would help me a lot.
Thanks for the input.
You need to pull x and y from the "event" object. Then the code depends on your needs. You can connect a new point with the previous one (to make a segment) or just put it in the list. Pseudo-code:
public boolean onTouch(View v, MotionEvent event) {
innerView.submitNewPoint(event.getX()), event.getY());
return true;
}
If you want to handle multi-touch events you need to retrieve points count ( event.getPointerCount() ) and do something with coords from event.getX(i)/event.getY(i) (i - index of multi-touch event point).
Of cause you need to implement drawing of the points/segments/? list in the InnerDrawingView.
P.S. do not forget to make you fields private ;)
I want to write an android app that would be a background service that would listen for either a specific gesture or key press in the and then trigger an action. Is it even possible to do such a thing with a service? If so could someone guide me the right direction. I have search high and low can could seem to find an answer.
not hard to read where they touch, but the touches in this way provide only location, not time, not on ups or downs.
in your service
#Override
public void onDestroy() {
super.onDestroy();
if (layout != null) {
((WindowManager) getSystemService(WINDOW_SERVICE)).removeView(layout);
layout = null;
}
}
#Override
public void onCreate() {
super.onCreate();
layout = new RelativeLayout(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
params.setTitle("test");
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(layout, params);
}
and for your touch handler, it will have to be as
public boolean gestureHandler(MotionEvent event, boolean eat) {
if(event.getAction() == MotionEvent.ACTION_OUTSIDE)
Doing this (except on top of apps that cooperate by sending you their input) would be a huge security hole of the kind that the android architecture is designed to prohibit. To do it you would need to modify the platform and have the watching done by something running with access to the raw touch and button input, possibly as part of the driver for that.
In other words you can with difficulty do it on your own rooted phone or on devices you manufacture, but it's not usable for most people.