クソアプリ Advent Calendar 2022 4日目の記事です。
Official髭男dismの「Subtitle」という曲が心にとても沁みます。
歌詞の一部を引用します。
言葉はまるで雪の結晶
君にプレゼントしたとして
時間が経ってしまえば大抵
記憶から溢れ落ちて溶けていって
消えてしまう
うっかりしたことが、フジテレビ系ドラマ「silent」を見逃してしまったこと。なんでも「泣けるドラマ」だそうで話題ですね。このドラマの主題歌でもあります。
何度も聴いているうちに、思い付いたのがこんなAndroidアプリです。
相手のメッセージが溶けて消えるチャットアプリです。相手の発言は、表示されるや否や、溶けて消えます。
開発環境
Windows10で、Android Studioはバージョン2021.2.1です。
会話する「相手」は、株式会社リクルートが提供するAI・機械学習ソリューション「A3RT」のプロダクトの一つ「Talk API」です。
https://a3rt.recruit.co.jp/product/talkAPI/
ライブラリー「converter-moshi」を利用しました。
このライブラリーには、JSONパーサー「Moshi」と、HTTPクライアント「Retrofit」を含んでいるので、以下の一行をbuild.gradleファイルに記述してsync nowすれば済みます。
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
ありがとうございます、Square様!
Talk API利用方法
メールアドレスを登録して、APIのキーを発行してもらいます。
そのAPIキーを使って仕様の通りにリクエストを送信すれば、日常会話で応答してくれます。
「最近めっきり寒くなってきましたね」に対して「好きですよね」というように、
時折、頓珍漢な返答をしてくれるので、クスッとなります。
メールアドレス1つでAPIキーは貰えますし、APIキーさえあればcurlコマンドでも会話を楽しめますので、簡単ですよ。
ありがとうございます、株式会社リクルート様!
参考にしたQiita記事
湯婆婆Androidアプリを作ってみましたという自分の記事を参考にしました。
字がふわ~っと消えるアニメーションを今回も適用したかったので。
この湯婆婆Androidアプリは、字(正確に言えば、String
を1個ずつandroid.graphics
パッケージのCanvas
やPaint
を駆使してBitmap
オブジェクトに変換したもの)を、android.animation
パッケージのAPIを使って「上へ向かって」ふわ~っと浮かび上がらせるアニメーションですが、今回の溶けて消えるチャットアプリは、向きが「右斜め下へ向かって」なので、X軸Y軸のパラメータをいじればいいだけですので、当記事では詳しい説明は割愛します(湯婆婆Androidアプリの記事を参照してください)。
プログラム
Activityクラス1個と、リスト表示するのでRecyclerViewを使うことにしたので、それのAdapterクラスとViewHolderクラスも作りました。
以下、コードをすべて掲載すると長くなってしまうので、ポイントだけ残して、割愛できるところは省略しています。
RetrofitのAPIインタフェース
public interface TalkApi {
@FormUrlEncoded
@POST("/talk/v1/smalltalk")
Call<TalkApiResponse> postRequest(@Field("apikey") String apikey, @Field("query") String query);
}
Talk APIのリクエストパラメータの説明によると、POSTリクエストであること、「apikey」と「query」というパラメータが必要だとのことなので、このようなメソッド定義となりました。
JSONデータクラス
Talk APIが返却してくれる雑談応答は、例えば以下のようなJSONです。
{"status": 0, "message": "ok", "results": [{"perplexity": 4.643262344645122, "reply": "趣味を楽しんでいる人は素敵ですよね"}]}
なので、以下のようなデータクラスを作りました。
public class TalkApiResponse {
public int status;
public String message;
public List<Result> results;
public class Result {
public double perplexity;
public String reply;
}
}
getter/setterメソッドを作るのが面倒くさくてpublic
まみれです。Moshiに甘え切って完全に委ねます。
しかも、欲しい情報はただ一つ、reply
だけなんですけどね。
Activityクラスの概観
このアプリの挙動としては、自分のメッセージを入力して送信ボタンをクリックしたら、自分のメッセージを表示させたそばから、Talk APIにリクエストを送信し、レスポンスされたメッセージを表示します。そんなActivityです。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText inputEditText = findViewById(R.id.inputEditText);
Button sendButton = findViewById(R.id.sendButton);
// RecyclerView関連
RecyclerView recyclerView = findViewById(R.id.listView);
ChatAdapter adapter = new ChatAdapter(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
// ライブラリー「Retrofit」と「Moshi」を使います
TalkApi api = new Retrofit.Builder()
.client(new OkHttpClient.Builder().build())
.baseUrl("https://api.a3rt.recruit.co.jp")
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(TalkApi.class);
// ボタン押下
sendButton.setOnClickListener(v -> {
String inputStr = inputEditText.getText().toString();
// 自分のメッセージを表示
adapter.add(inputStr);
// Talk APIへPOSTリクエストして、レスポンスを受信する
Call<TalkApiResponse> talkApiResponseCall = api.postRequest("Talk APIからもらったAPIキー", inputStr);
talkApiResponseCall.enqueue(new Callback<TalkApiResponse>() {
@Override
public void onResponse(Call<TalkApiResponse> call, Response<TalkApiResponse> response) {
String reply = response.body().results.get(0).reply;
// 相手のメッセージを表示
adapter.add(reply);
}
@Override
public void onFailure(Call<TalkApiResponse> call, Throwable throwable) {
// エラー処理
}
});
});
}
}
Callback
インタフェースの匿名オブジェクトのonResponse
メソッド内で、レスポンスのステータスで成功したか否かチェックを怠っていたり、onFailure
メソッドも空っぽというように、いろいろとちょっとお行儀が悪い箇所が多いのですが、勘弁してください。
このActivityのレイアウトリソースファイルには<RecyclerView>
を配置しておきます。
RecyclerView.Adapterクラスの概観
public class ChatAdapter extends RecyclerView.Adapter<ChatAdapter.FukidashiViewHolder> {
private final Context context;
private final List<String> msgList;
public ChatAdapter(Context context) {
this.context = context;
this.msgList = new ArrayList<>();
}
@Override
public int getItemCount() {
return msgList.size();
}
@NonNull
@Override
public FukidashiViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new FukidashiViewHolder(
LayoutInflater.from(parent.getContext()).inflate(R.layout.fukidashi, parent, false)
);
}
@Override
public void onBindViewHolder(@NonNull FukidashiViewHolder holder, int position) {
if (自分のメッセージ) {
// 自分の吹き出しに普通に表示
} else {
// 相手(Talk API)のメッセージ
// 文字列を1字ずつバラにする
String[] strings = new String[message.length()];
for (int i = 0; i < message.length(); i++) {
strings[i] = String.valueOf(message[1].charAt(i));
}
// 1字ずつをBitmapにして、ImageViewに貼り付けて、GridLayputに配置する
ImageView[] imageViews = new ImageView[strings.length];
for (int i = 0; i < strings.length; i++) {
imageViews[i] = new ImageView(context);
imageViews[i].setImageBitmap(convertTextToBitmap(strings[i]));
holder.otherMsgArea.addView(imageViews[i]);
}
// 溶かす
for (ImageView imageView : imageViews) {
meltingAnimation(imageView);
}
}
}
// Activityから貰う新メッセージ
public void add(String newMsg) {
msgList.add(newMsg);
}
}
一行分のレイアウトリソースファイルは「fukidashi.xml」と名付けました。そこに吹き出しを配置しています。吹き出しの画像は9-patch画像です。黒い吹き出しが自分のメッセージ用、緑の吹き出しが相手のメッセージ用です。
コード中にある、以下のメソッドは、
-
convertTextToBitmap
メソッドは、String
をBitmap
に変換して返すメソッドです。 -
meltingAnimation
メソッドは、ImageView
を右斜め下にふわ~っと消していくアニメーションをするメソッドです。
これらのメソッドは湯婆婆Androidアプリを作ってみましたという自分の記事にあるのとほぼ同じなので、ここでは説明は割愛します。
- 自分のメッセージであれば、普通に出す。
- 相手(Talk API)のメッセージであれば、表示したそばから溶けるように消す。
そんな処理の振り分けのif構文を、RecyclerView.Adapter
のonBindViewHolder
メソッドの中で処理しています。
それでは最後にお聴きください
湯婆婆Androidアプリと同様に、「表示させた文字を、消す」シリーズのアプリを作ってみました。
まるで役に立ちません。古今東西のアプリとかシステムとかソフトウェアで、表示させた文字がふわ~っと消えるだなんてものは、見たことがありません。
でも、こうして『消えていくもの』を、冒頭で紹介したOfficial髭男dismの「Subtitle」だけでなく、多くのアーティストたちが歌い上げています。
手を伸ばし抱き止めた激しい光の束
輝いて消えてった 未来のために
LiSAの「炎」です。鬼も滅しないといけないですし。
大切な人も恋も愛も
性も、どうしよう
いつまでたっても守りきれないよ
いつかは消えてしまう
あいみょんの「あした世界が終わるとしても」です。世界が終わるのなら、そりゃ消えますよね。
悲しみっていつかは 消えてしまうものなのかなぁ…
タメ息は少しだけ 白く残ってすぐ消えた
SMAPの「夜空ノムコウ」です。全ては思うほどうまくいかないようです。
砂まじりの茅ケ崎
人も波も消えて
サザンオールスターズの「勝手にシンドバッド」です。
砂や波、といえば、もう一曲。
愛はまるで砂の城ね
出来た瞬間波がさらう
斉藤由貴の「砂の城」です。おっとこれは、城は消えても、光る砂が残るわ。光る砂が残るわ。
以上、記憶に消えない、心に残る名曲集でした。