Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

僕はただAndroidで自Serviceから他Activityにタッチイベントを発生させたいだけだったのに(ラノベ調

More than 1 year has passed since last update.

※最初に
この仕組みは使用者(ユーザ)にも実装者にもリスクがあるものと思ってます
自身まだ完全に内容や動きを理解しているわけではなく、あくまで参考記事として受け取ってもらえると助かります。

目的

わたくしめは趣味でソシャゲを嗜んでおります。
ゲームといえども効率化って大事だよね!

みんなも昔連打コンとか使ってたよね!

以上

実際に成功した実装内容について

たどり着く前に紆余曲折あったのですが結論から先に

ユーザ補助機能を使いましょう

ただし!この機能は非常に強力でありながら大変リスキーな機能となっております。
使用の際(特にアプリを配布したりする場合)はご自身でもよく調査してから使用する事を限りなくおすすめします。

ユーザ補助機能とは

目の見えない人とか一部の操作(スワイプとかダブルタップ)が身体的理由等で使えない人に向けた補助サービス用機能とういう解釈です。

ユーザ補助機能一部でどういった事ができるようになるのかは下記の記事がわかりやすいと思います。
https://www.slideshare.net/r-ralph/accessibilityservice

ようはなんでもできます(たぶん)

実装

①AndroidManifest修正
②Manifest用のXML作成
③Activity(Fragment)で権限許可リクエストをなげる
④AccessibilityService
の立ち上げ
⑤GestureDescription.Builder().addStroke でタップイベントを発生させる

①AndroidManifest修正

<service android:name=“xxx.xxxx.xxx.xxxService”
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/service_config" />
</service>

AndroidManifestのapplication部に記述してください

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
    tools:ignore="ProtectedPermissions" />

場合によっては

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

が必要かも
FOREGROUND_SERVICEはServiceを使おうとおもったらついてまわってくる権限です

@xml/service_configは次の項ででるやつです

②Manifest用のXML作成

res/xml内に下記ファイルを追加

<?xml version="1.0" encoding="utf-8"?>
    <accessibility-service
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackAllMask"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:canPerformGestures="true"
    android:description="@string/accessibility_desciription
"
    />

各設定の詳細はここ見てください
https://developer.android.com/reference/android/accessibilityservice/AccessibilityServiceInfo

タッチ挙動させる場合特に重要なのは
canPerformGestures
こやつです

③Activity(Fragment)で権限許可リクエストをなげる

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    // OVERLAY許可
    if (!Settings.canDrawOverlays(this)) {
        val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()))
        this.startActivityForResult(intent, REQUEST_SYSTEM_OVERLAY)
    }

    val intent2 = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
    intent2.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
    intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

    this.startActivity(intent2)

}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    when (requestCode) {
        REQUEST_SYSTEM_OVERLAY -> {
            if (resultCode == Activity.RESULT_OK) {


            } else {
                finish()
            }
        }
        else -> {
            finish()
        }
    }
}

いろいろやっつけですまんの

いっこ注意点としてはACTION_ACCESSIBILITY_SETTINGSはstartActivityForResult
で投げても意味がありません
(たぶん
なので設定がONかOFFか調べるのも結構苦労する

④AccessibilityService

の立ち上げ

    val settingValue = Settings.Secure.getString(
            this.getContentResolver(),
            Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES
    )

    if (settingValue.isNotEmpty()) {
        val simpleStringSplitter = TextUtils.SimpleStringSplitter(':')

        simpleStringSplitter.setString(settingValue)

        val name : String = packageName + "/" +  xxxService::class.java.canonicalName
        while (simpleStringSplitter.hasNext()) {

            if (simpleStringSplitter.next().equals(name)) {
                startService(Intent(this, xxxService::class.java))
                return

            }

        }

    }

ユーザ補助の設定がONがどうか調査しつつServiceを立ち上げ

⑤GestureDescription.Builder().addStroke でタップイベントを発生させる

class xxxService :AccessibilityService() {

継承はAccessibilityServiceを継承する事。

                var point : Point = Point()

                mWindowManager.getDefaultDisplay().getSize(point)

                val gestureBuilder = GestureDescription.Builder()
                val path = Path()

                path.moveTo(tapPosX,tapPosY)

                gestureBuilder.addStroke(GestureDescription.StrokeDescription(path,0L,10L))
                val gestureDescription = gestureBuilder.build()

                val flg : Boolean = mAccessibilityService.dispatchGesture(gestureDescription,object : AccessibilityService.GestureResultCallback(){
                    override fun onCompleted(gestureDescription:GestureDescription) {
                        super.onCompleted(gestureDescription);
                    }

                    override fun onCancelled(gestureDescription: GestureDescription?) {
                        super.onCancelled(gestureDescription)
                    }
                },null)

実際のタップしたときのイベントはこんな感じです。

これとは別にVerによってはnotificationを設定しないとそもそもServiceが立ち上がらないのでご注意を

課題

・AccessibilityServiceの場合Activityから終了させる事ができない。(Service自身が自身を終了させる必要がある)
・Serviceは今表示されているActivityが何かを保証していない
・Serviceはたまにシステムから消される
・重い処理を書きづらい
・AccessibilityEventの全容がわからない

いやー!理解せず使って連打処理とか仕込んで意図せず課金しちゃったり電話しちゃったりしたら大変だね!

なので個人用には使えるけどアプリ配布まではまだまだ勉強不足かなといった感じです

ここに至るまで

超大変だった。
ぐぐって出てきたものも色々試してみたんですけど中々やりたい事に辿りつけなかったですね

ただ調査開始から試した事、最後にこれに至ったまでを話すと長くなるので割愛します。

最後に

「Serviceは今表示されているActivityが何かを保証していない」に関してはいくつか解決策を見つけたので時間があればまた記事にするかも

ご意見等ありましたらお手柔らかにお願いします

wamochi
畑はDB関連です(自称) PHP,Java(Android),Kotlinなんか触ってます
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away