I'm currently diving into Android development and recently came up against a difficulty. I'd like to create an overlay app, which lies on top of all other apps. It should listen for a three finger swipe, whereas all other Touch Events should be handled by the OS (e.g. an underlying app).
Is that even possible?
I already found out that I need to add the LayoutParam TYPE_PHONE instead of SYSTEM_ALERT since the latter one will consume all touch events. So my class looks like this now:
package [...]
public class Overlay extends Service {
private TView mView;
private ThreeFingerSwipeDetector detector = new ThreeFingerSwipeDetector() {
@Override
public void onThreeFingerTap() {
Toast.makeText(getBaseContext(), "Three Fingers recognized.", Toast.LENGTH_SHORT).show();
}
};
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Toast.makeText(getBaseContext(), "Launched! :-)", Toast.LENGTH_SHORT).show();
mView = new TView(this);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
// PHONE, because SYSTEM_ALERT will consume _ALL_ touch events
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.RIGHT | Gravity.TOP;
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.addView(mView, params);
}
@Override
public void onDestroy() {
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.removeView(mView);
mView = null;
super.onDestroy();
}
// inner class
class TView extends ViewGroup {
public TView(Context context) {
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
@Override
protected void onLayout(boolean arg0, int arg1, int arg2, int arg3, int arg4) {
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(detector.onTouchEvent(event)) {
return true;
}
//TODO: what am I supposed to do when it's not a 3 finger swipe (aka the detector doesn't recognize it as one)
// something like super.onInterceptTouchEvent(event) or so?
}
}
}
The detector will correctly detect a 3 finger swipe but my app in its current state will block all other user interaction with the underlying UI until I kill the app.
Is there any system call that can be called with MotionEvent as parameter where my TODO annotation is now? Or do I want something that is just not possible?
Best regards and thank you very much!
Edit: The ThreeFingerSwipeDetector Class looks like this:
[Import, ...]
public abstract class ThreeFingerSwipeDetector {
public boolean onTouchEvent(MotionEvent event) {
int t = event.getActionMasked();
switch(event.getActionMasked()) {
case MotionEvent.ACTION_POINTER_DOWN:
if(event.getPointerCount() == 3)
onThreeFingerTap();
return true;
case MotionEvent.ACTION_DOWN:
if(event.getPointerCount() == 3) {
onThreeFingerTap();
return true;
}
}
return false;
}
public abstract void onThreeFingerTap();
}
Actually it's not a swipe recognizer yet but it only checks for three finger taps for now. Anyways the logic in there should be quite the same.