web-dev-qa-db-ja.com

サービスはどのようにタッチジェスチャ/イベントをリッスンできますか?

SwipePadやWave Launcherのようなアプリは、単にサービスを介してタッチジェスチャ/イベントを検出できるのではないかと思っています。これらのアプリは、独自のアクティビティに含まれていなくても、タッチジェスチャを検出できます。私はすべてのインターネットを見てきたが、彼らがそれを行う方法を見つけていません。

私の主な質問は、元のアクティビティまたはコンテキストにない場合でも、通常のアクティビティがMotionEventsを受け取るのと同じように、サービスがタッチのゲスト/イベントをリッスンする方法です。基本的に、どのアクティビティが一番上にあるかに関係なく、ユーザーからの特定のタッチジェスチャを認識し、そのジェスチャが認識されたときに何かを実行するアプリを作成しようとしています。タッチ認識は、サービスとしてバックグラウンドで実行されるスレッドになります。

60
Brian

私はこれと同じ問題を抱えていて、ついにそれを見つけました!この投稿に感謝: システムオーバーレイウィンドウの作成(常に上) 。オーバーレイの代わりにアラートウィンドウを使用する必要があります(また、これは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);

次に、この方法でGestureListenerを添付します。

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);

わーい!

18
pypmannetjies

興味深い質問。私は彼らがそれをどのようにしたのか分かりません、そしてグーグルグループの投稿を見つけました。それはグローバルなタッチリスナーがないことを教えてくれます。しかし、とにかくアイデアがあります...

私は この投稿 が誰かがサービスからポップアップウィンドウを表示することに成功した場所を見つけました。そのポップアップを透明でフルスクリーンにすると、 タッチインターセプター を設定できるので、タッチをキャプチャできると確信しています。

Edit:試してみて結果を報告してください。これが機能するかどうかを知るのは興味深いでしょう...

5
Knickedi

私はすべての可能な解決策をテストしましたが、何も機能せず、タッチイベントを発生させず、画面を凍結しました。

だから私はいくつかのリバースエンジニアリングを行い、今では動作するソリューションを投稿しました

  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);
3
Mr Coder

これをAPI 21とネクサス5でテストし、システムからタッチイベントが発生したときに、サービスからログを記録することができました。ただし、MotionEventオブジェクト内のデータ(座標など)は、アプリのパッケージと思われるものの外側にある場合、0.0を返します。

2つのソース 統合許可

Event.getX()、event.getY()、およびevent.getPressure()が、サービスが存在するアプリのアクティビティ内にないときに0.0を返す理由について興味があります。

編集

このソリューションは、他のリスナーがサービスによってキャプチャされたタッチイベントを受信することを妨げません。

public boolean onTouch(View v, MotionEvent e){
    Log.d(LOG_TAG, "Touch event captured");
    return false;      

Falseを返すことにより、他のリスナーがイベントを受信できるようになります。

Edit2

どうやら、現在のアクティビティとリスナーがUIDを共有しない場合、OSの入力ディスパッチャーは座標と圧力を0に設定します。ここに source

3
Clocker

私は多くのSOスレッドを検索しましたが、セキュリティ上の理由から、これはシステム上のすべてのパッケージで可能だとは思いません。

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);

フローティング/オーバーレイレイアウト

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;
        }
    });

レイアウト

<?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"/>
0
John61590