MIT App Inventor で試しに作った「ボタンを押したら音声合成でしゃべる Androidアプリ」を Java で作ってみた

## 【追記】 Kotlin版もやってみました
●MIT App Inventor で試しに作った「ボタンを押したら音声合成でしゃべる Androidアプリ」を Kotlin で作ってみた - Qiita
　https://qiita.com/youtoy/items/b41f2f4428f911c9ebe6

## はじめに
以下の記事に書いていた、ビジュアルプログラミングでの Androidアプリ開発。

●ビジュアルプログラミングで Androidアプリ開発ができる「MIT App Inventor」を試す（ボタンを押したら音声でしゃべるアプリを作る） - Qiita
　https://qiita.com/youtoy/items/93f7c786ff9d664d3032

上記の記事で実装した「ボタンが押されたら音声合成でしゃべる」というシンプルな処理は、MIT App Inventor だとブロック 3つで実装できていました（デザイン編集の作業も、ボタンと音声合成のコンポーネントを 1つずつドラッグアンドドロップするだけという手軽さ）。
![MIT_App_Inventorで実装した処理.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9c96af99-3256-9b4c-1b71-5262cba796a9.jpeg)

これについて、「同じ処理をテキストプログラミングでやると、どれくらいの処理を書く必要があるのだろうか？」と思って試してみたのが、今回の記事の内容です。

### 余談
当初、今までにほとんど使ったことがない Kotlin で書いてみようかとも思ったのですが（※ Kotlin に触れる機会を作る意味で）、とりあえず短時間でサクッと試せたほうが良いかと思いなおし、前に Androidアプリ開発で触ったことがあった Java を使っています。

## 新規プロジェクト作成
本来、開発環境がない状態からの手順だと、ここで Android Studio のセットアップ等がありますが、今回は省略します（自分は、開発環境は過去に作ったものが残っていて、長らく使ってなかったもののアップデートはたまに行っていたので、それを活用しました）。

ここから、Android Studio でのプロジェクトの作成を進めていきます。
まずは、プロジェクトの新規作成を選択します。
![Android_Studioの最初の画面.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/de07ec33-6713-a45c-c2fd-e8183dd75ffd.jpeg)

以下は、テンプレートの選択画面です。アプリ上にボタンを 1つ配置できれば良いだけなので、空のアクティビティを選んで次へ進みます。
![プロジェクトテンプレートの選択.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/b78b4fae-a436-ca2d-9e97-866e917dc4cd.jpeg)

こちらはプロジェクトの設定です。設定した箇所は以下のとおりです。
　・プロジェクト名： TTS_test01_Java
　・言語： Java
　・SDKのバージョンの下限： API 28（Android 9.0）
![プロジェクトの設定.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/c578aeb9-9058-d6a8-6d66-0ec00f24ddbd.jpeg)

プロジェクト名は適当につけました。また、言語は冒頭で書いた理由で、Kotlin ではなく Java です。
そして、SDKのバージョンの下限は適当に決めました。自分が持っている端末は Android 10 や 11 のものもあるので、もっとバージョンを上げても良かったのですが、何となくで決めました（このバージョンに依存するような処理は、今回の実装に出てこなそうなのもあり）。

ここまで進めると、以下のような画面になりました。
![新規プロジェクト作成を行った直後の画面.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/4a2d1b1d-a5e1-c692-47be-14163f2f2c20.jpeg)

ここで、とりあえずビルドを行って、この空のアクティビティのアプリを Androidスマホで表示させてみました。表示できたのが確認できたところで、ここから開発を行っていきます。
![5.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/8179ea6b-b869-1cc8-bc38-63f934688ce9.jpeg)

## 実装を行う
ここから、画面の内容と操作が行われた時の処理を作っていきます。

### アプリの画面デザイン
画面デザインと言っても、アプリ上にボタンを 1つ配置するだけです。笑

画面のデザイン編集のため、プロジェクトの「app > res > layout」と進み、「activity_main.xml」を開きます。xmlファイルを開いた直後の画面は以下のとおりです。
![画面デザインの編集用画面.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/3c8558b3-ff13-6f66-d4de-d67b7603f969.jpeg)

以下の画像で示したとおり、「Button」を画面の上にドラッグアンドドロップで配置します。
![ボタンを画面に配置する.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/ac0c05d1-ff28-3767-16d9-b0ab7821d51c.jpeg)

これ以外には、余白等の調整は行っておらず、配置したボタンの id も特に変更もせずデフォルトの button が適用された形のままとしました。

ここで、プロジェクトのサンプルにデフォルトで入っていた、画面中央に「Hello World!」と表示する部分は不要ですが、悪影響もないのでそのまま残してしまいました。

### Javaでの実装
画面ができたところで、「ボタンが押された時に音声合成でしゃべる」という処理を書いていきます。
プロジェクトの「app > java > com.example.tts_test01_java」と進んでいき、「MainActivity（.java）」を開きます。デフォルトの状態は以下のとおりです。
![MainActivityのデフォルト状態.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/9c4e4ae5-004d-8120-22f0-9b219dcb7f09.jpeg)

以下、デフォルトのコードのテキスト版です。

```java
package com.example.tts_test01_java;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}
```

参考にできそうな記事をいくつか探してみて、動作仕様は少し違いものの大部分を流用できそうな以下の記事が見つかったので、こちらを参照しつつ処理を書いていきました。

●[Android] 読み上げ TTS (Text To Speech) の設定
　https://akira-watson.com/android/tts.html

#### importの追加
import の追加について、以下を足しています。
最初の 2つはボタン周りで必要なもの、3つ目は音声合成用、4つ目はログ出力用のものです。

```java
import android.view.View;
import android.widget.Button;
import android.speech.tts.TextToSpeech;
import android.util.Log;
```

#### ボタン・音声合成関連1
以下の冒頭のところで、ボタン関連と音声合成関連で `implements View.OnClickListener, TextToSpeech.OnInitListener` の部分を追加しています。
また、その下でも音声合成に関わる部分で 2行ほど追加しています。 `tts_text` で設定した文字列は、今回の読み上げ対象になるテキストです。

```java
public class MainActivity extends AppCompatActivity
        implements View.OnClickListener, TextToSpeech.OnInitListener {

    private TextToSpeech tts;
    private static final String tts_text = "この文字を読み上げます";
```

#### ボタン・音声合成関連2
onCreate関数の中で、追加している部分は以下となります。
1行目は音声合成関連、2・3行目はボタン関連です。画面デザインを作った際に、追加したボタンの id を button としていたので、以下の 2行目の `R.id.button` の部分でそれを使っています。

```java
        tts = new TextToSpeech(this, this);
        Button ttsButton = findViewById(R.id.button);
        ttsButton.setOnClickListener(this);
```

#### ボタン・音声合成関連3
音声合成を利用する場合、初期化に関わる部分が必要なようだっため、以下の最初の 8行の部分に追加しています。その際の初期化の成功/失敗はログに出力するようにしています。
最後の 4行は、アプリの画面に配置したボタンが押された際に実行される処理を書いた部分です。ボタン押下で音声合成の処理が行われるように書いています。

```java
    @Override
    public void onInit(int status) {
        if (TextToSpeech.SUCCESS == status) {
            Log.d("TTS", "initialized");
        } else {
            Log.e("TTS", "failed to initialize");
        }
    }

    @Override
    public void onClick(View v) {
        tts.speak(tts_text, TextToSpeech.QUEUE_FLUSH, null, "messageID");
    }
```

本当は、もっと書いたほうが良さそうな処理がありそうにも思いますが、ここまでの追加を行ったものをビルドしてエラーがなく動いたので、実装はここまでとしました。

#### ソースコード全体
以上の実装を行った後のソースコードは、以下のとおりです。

```java
package com.example.tts_test01_java;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

import android.view.View;
import android.widget.Button;
import android.speech.tts.TextToSpeech;
import android.util.Log;

public class MainActivity extends AppCompatActivity
        implements View.OnClickListener, TextToSpeech.OnInitListener {

    private TextToSpeech tts;
    private static final String tts_text = "この文字を読み上げます";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tts = new TextToSpeech(this, this);
        Button ttsButton = findViewById(R.id.button);
        ttsButton.setOnClickListener(this);
    }

    @Override
    public void onInit(int status) {
        if (TextToSpeech.SUCCESS == status) {
            Log.d("TTS", "initialized");
        } else {
            Log.e("TTS", "failed to initialize");
        }
    }

    @Override
    public void onClick(View v) {
        tts.speak(tts_text, TextToSpeech.QUEUE_FLUSH, null, "messageID");
    }
}
```

## アプリを動かしてみる
以下の上半分は、今回作ったアプリを Androidスマホで動かしてみた時の様子です。
ちなみに、 MIT App Inventor で作ったアプリが動作している時の様子は以下の下半分になります。アプリの見た目以外に違いは特にないです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">先ほど動画をツイートしていた MIT App Inventor で作った「ボタンを押したら音声合成でしゃべるアプリ」。<br><br>同じものを Java で実装して動かしてみた。<br><br>使ったことがない Kotlin で書いてみようかと思ったものの、サクッと試せればと、前に Androidアプリ開発で触ったことがあった Java のほうで。 <a href="https://t.co/qXZVscsxE2">https://t.co/qXZVscsxE2</a> <a href="https://t.co/D3hvqpvBAX">pic.twitter.com/D3hvqpvBAX</a></p>&mdash; you (@youtoy) <a href="https://twitter.com/youtoy/status/1368496377613881347?ref_src=twsrc%5Etfw">March 7, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## おわりに
今回、「MIT App Inventor でのアプリ実装の手軽さがどれほどのものか」というのを知るために、非常にシンプルな機能のみを MIT App Inventor で実装したものを、Android Studio を使った Java による開発でも行ってみました。
今回の内容であれば、MIT App Inventor のほうがかなり手軽に開発が行えたという感覚でした。

MIT App Inventor で標準実装されているブロックは、カメラやセンサーや Bluetooth、NFC など内蔵ハードウェアに絡むものから、HTTPリクエストといったものなど様々な機能が利用できます。そのため、Androidアプリの小規模なプロトタイプをサクッと作る目的では、有用な場面がありそうです。
当然、Kotlin/Java で実装したほうが良いもの（MIT App Inventor で用意されてい機能を使うものや、ブロックでのプログラムだと見通しが悪くなるようなもの等）もありますが、今後もちょっとしたツールを試しに作ってみて、活用法を探してみようと思います。