アシスタントアプリとして起動できるAndroidアプリの作成

# はじめに
この記事ではAndroidアプリをアシスタントアプリとして登録したい時に設定するコードについて紹介します。

例としてコードを掲載していますが、まだ開発途中であるため、一部機能が使用できず、また強制停止が頻発します。あくまで参考程度でお使いください。

# Androidスマホのショートカット機能
Androidスマホには、画面の右下から中央下に指をスライドしたり、ホームボタンを長押しするとアシスタントアプリが起動するショートカットが実装されています。また、端末によっては、アシスタントキー(SHARP AQUOS)や、Googleアシスタントボタン(SONY Xperia)などの、アシスタントアプリを起動するためのボタンが存在します。

しかし、あまり使わないんですよね。この機能。
じゃあ別のアプリを割り当てたい。

でも残念なことに、これらのショートカットは、アシスタントアプリとして登録されているアプリでないと、起動させられないんです。
<img width="270" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2921137/dc93adec-330a-95dc-373f-0d6a386676a5.png">
<img width="270" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2921137/626dcfc1-081b-757e-a3dd-ebae67dcc8d3.png">


# アシスタントアプリを作れば良いじゃない
ショートカット機能で起動できるアプリはアシスタントアプリだけ。つまりアシスタントアプリなら起動できるということ。

Androidには、アプリがどういう役割を果たすのかをOS側に伝えるコードがあります。例えば
```xml:AndroidManifest.xml
<action android:name="android.intent.action.BROWSER"/>
```
というコードがアプリのAndroidManifest.xmlに存在した場合、Androidはブラウザアプリとして扱います。

ではアシスタントアプリの場合は何を入力すれば良いのか…？調べてみると、ありました。

https://stackoverflow.com/questions/51034541/set-app-as-default-assist-intent

```xml:AndroidManifest.xml
 <action android:name="android.intent.action.ASSIST"/>
```

ブラウザの場合はBROWSRだった部分がアシスタントの場合はASSISTになるんですね。

つまり、android.intent.action.ASSISTを指定したアプリから、ショートカットしたい別のアプリを開けば、実質的に、ショートカットボタンから好きなアプリを開くことができます。

# 動作を考える
ショートカットからある特定のアプリを開く機能だけが欲しい、というのであれば、実装は楽です。
例えばChromeであれば、コード自体にChromeを開くコードを埋め込んでしまい、作ったアプリを起動した瞬間にChromeを起動する仕様にしてやれば、簡単です。

しかし、時間が経てば、起動させたいアプリは変わるかもしれません。その度にアプリをビルドし直すのは面倒です。

できればアプリ内から、起動するアプリを選べたら便利ですよね。アプリ一覧を表示して、ユーザーに選択してもらい、そのアプリのパッケージを取得し、保存、使用することで、様々なアプリに対応できるようになります。

ただし、今の私にはそんな動作を実現できる実力が無いため、LINEを起動するコードを書きたいと思います。

# 開きたいアプリを開く画面と、開きたいアプリを選択する画面をどうやって分けるか
ランチャーでアプリのアイコンを長押しした時に表示することができる、ウィジェット機能を使っています。

# コード
<details><summary>まだ作成途中ですが一応公開します</summary>

``` Java:MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.preference.PreferenceManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onStart() {
        super.onStart();
        PackageManager pm = getPackageManager();
        Intent intent = pm.getLaunchIntentForPackage("jp.naver.line.android");
        try {
            startActivity(intent);
        } catch (Exception e) {
            Toast.makeText(this, "対象のアプリがありません", Toast.LENGTH_SHORT).show();
        }
    }
}
```

```Java:SettingActivity.java
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class SettingActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button saveButton = (Button)findViewById(R.id.SaveButton);
        saveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                saveButtonClick();
            }
        });
    }

    public void saveButtonClick() {
        EditText editText = (EditText)findViewById(R.id.editText);
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
        sp.edit().putString("SaveString", editText.getText().toString()).commit();
    }
}
```

``` xml:AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
        tools:ignore="QueryAllPackagesPermission" />
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.Translucent.NoTitleBar"
        tools:targetApi="31"        >
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.ASSIST" />
                <action android:name="android.intent.action.SEARCH_LONG_PRESS" />
                <category android:name="android.intent.category.DEFAULT" />
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <meta-data
                android:value=""
                android:name="android.app.shortcuts"
                android:resource="@xml/shortcuts"/>
        </activity>

        <activity
            android:name=".SettingActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

            </intent-filter>

            <meta-data
                android:name="android.app.shortcuts"
                android:resource="@xml/shortcuts"/>
        </activity>
    </application>

</manifest>
```

``` xml:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/text"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.497"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.223" />

    <EditText
        android:id="@+id/editText"
        android:layout_width="243dp"
        android:layout_height="53dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="@string/editText"
        tools:ignore="LabelFor,MissingConstraints"
        tools:layout_editor_absoluteX="84dp"
        tools:layout_editor_absoluteY="225dp"
        android:autofillHints="" />

    <Button
        android:id="@+id/SaveButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/buttonname"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editText"
        app:layout_constraintVertical_bias="0.132" />

</androidx.constraintlayout.widget.ConstraintLayout>
```

``` xml:string.xml
<resources>
    <string name="app_name">OpenApp</string>
    <string name="text">Please write package name</string>
    <string name="buttonname">Save</string>
    <string name="shortcutShortLabel">shortcutShortLabel</string>
    <string name="shortcutLongLabel">shortcutLongLabel</string>
    <string name="editText">jp.naver.line.android</string>
</resources>
```
</details>

# まとめ
アシスタントアプリを作ろうという人があまりいないのか、AndroidManifest.xmlに記述するコードを調べるのに手間取りました。早くアプリを完成させたいです。

最後まで読んでいただきありがとうございました。