Bardに聞きながらandroidアプリとウィジェットを作ってみた

## 背景

「ChatGPTがすごい」「使ってみるべき」という声をだいぶ前から聞いていて、（分かるけどめんどいな～）と思っていたのですが、年末年始が暇だったので試しにアプリ開発をしつつ試してみました。

ちなみに、自分はweb系のエンジニアで、情報系の大学を出て業務で2,3年PHPを書いてたくらいのレベルの人です。androidアプリは作ったことないし全然知らないです。

また、ChatGPTについては以前少し調べたものの、まだあまり使い方を把握できていないです。あと今回は[Bard](https://bard.google.com/chat)を使ってみました。何となく。

## 先に結果と感想

### 結果
簡単なメモアプリはAIに聞けば作れました。とはいえ、AIが凄いとか便利とかではなく、android studio でちょっと操作すれば出来るレベルのアプリだったのでandroid studioが凄いだけ、または作ったものがしょぼいだけ、という可能性は大いにあります。

**また、ウィジェットも作りたかったのですがAIに聞くだけでは完成できませんでした。** googleで検索したら解決しました。

![Screenshot_20240121-180214.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/0f398eb3-cf63-0c8b-1631-049f9cfdeb32.png)

（時計のマークを押すと現在時刻が一番下の行に追加されるウィジェットです。サイズは1マス分）

ちなみに、今回作ったアプリ、ウィジェットはほぼ同じものを既に他の方が作っていて記事にもなってました。
最初にgoogleで調べてその記事に辿り着いていたらそれだけで終わりだったのですが、気付かずAIに聞きながら格闘してました。AIを試してみることも目的の1つだったとは言え、ちょっと勿体ないことをした気がします。

### 感想

**どうしても「googleでどう検索しよう？」という発想が真っ先に来てしまい、『AIに聞く』ということに慣れず違和感がずっとありました。** 「AIサービスに慣れ親しんでおく」ということは今後のことを考えると早めにしておくべきかもと実感しました。

また、「情報が古い」というAIのデメリットは今回は露骨に感じました。ウィジェットの開発をAIに聞くだけでは完了しなかったのもそれが原因でした。言語やツールの更新が早いものはAIが苦手という話を凄く実感しました。

（AIの使い方としてたまに聞く「アイディア検討時の壁打ち相手」みたいな使い方はgoogleより良いかもしれないですが、今回はそのレベルのことは試せていないです。）

上記も含めて、 **AIの得意不得意は誰かがまとめているはずで、そういう情報収集をして『AIの使い方』を把握して、使うタイミングを増やしていこうかな～と思えたことが一番の収穫かもしれないです。**

あと、使っていて感じたマイナス点をいくつか書いておきます。（他でも言われていることな気はしますが。）
- 応答が遅くて毎回少し待たされるのが気になる
- 冗長な説明が多い
    - 「簡潔に教えて！」などリクエストする必要がある
- 情報元を教えてくれる時と教えてくれない時があり、教えてくれない時のソース確認が面倒

## Bardに聞きながら試したことの一部始終

今回は、簡単なandroidアプリをBardに聞きながら作成しました。また、ウィジェットの作成も実施しています。
簡単に現在時刻をメモできるウィジェットが作りたいと思っていたため、それを目指して進めました。（理由の詳細は[後述](https://qiita.com/drafts/cf57af827f2f4054c60d/edit#%E3%82%A6%E3%82%A3%E3%82%B8%E3%82%A7%E3%83%83%E3%83%88%E4%BD%9C%E6%88%90%E8%83%8C%E6%99%AF)）

その際にBardに何を聞いて、何を実施したかを下記にまとめました。

（この記事ではあくまで「Bardを使ってどんな風に進めたか」について記述していきます。実際のコードなどはあまり出てきません。）

### とりあえず聞いてみる

![スクリーンショット 2024-01-20 134741.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/48ded933-a4ac-1f0b-62e4-482ec4722731.png)

今読むと私の日本語が若干変ですが、だいたいニュアンスは伝わった様子。
このキャプチャの後には「ウィジェットのレイアウト XML ファイルを作成する。」みたいな話から、そのコード例まで書いてくれますが省略してます。分かる人ならこれだけで終わりかもしれないですが、1つずつ詳細を聞いてみます。

### Android Studio をインストールする

![スクリーンショット 2024-01-20 135232.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/56b7dd99-1665-9a69-4945-f9f02bdbf15d.png)

キャプチャは今回も途中までですが割と細かく教えてくれてます。 **ただ、実際に実施すると順番など違いました。** ライセンス条項はダウンロード時に読まされる等々。そのため、雰囲気だけ汲み取って進めます。

インストールは終わったのですが、日本語バージョンをダウンロードした気でいたのですがメニューバーが日本語じゃないため日本語化できるか聞いてみる。

![スクリーンショット 2024-01-20 140032.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/df5e7f03-ad74-9bbf-91c9-546ae8852ed6.png)

「なるほど！」と思って実行しようと思うけれど、見当たらない。。

![スクリーンショット 2024-01-20 140307.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/0029e094-d655-fbfd-4e98-6f39476a1b0c.png)

キャプチャ画像に情報元と思われるページへのリンクがあるのでクリックしてみると、エミュレーター上の言語設定の話をしている。誤情報のため、もう一回聞いてみる。バージョン情報的な意味でgiraffeということまで明記してみる。

![スクリーンショット 2024-01-20 140603.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/4fa485cd-f1c7-8db8-126d-dba173b80b06.png)

さっきとは違うけれど、この手順でも見つけられず。。ググるとプラグインを使う方法しか見当たらないのでプラグインを使って日本語化を実施しました。
（プラグインについての手順も直前の2つの質問で回答に含まれてはいたものの、どちらも古い情報のようで誤情報でした。）

とりあえずインストール完了。先が思いやられる。。

### メモアプリの作成方法を聞いて、そのコードを参考に修正してみる

最初の質問の回答にはアプリの作成方法については詳細が記載されていなかったため、アプリの作成方法について改めて聴いてみます。（ウィジェットは後回し）

![スクリーンショット 2024-01-20 141650.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/4332e016-4403-f91b-f730-e468bd8d5c9a.png)

![スクリーンショット 2024-01-20 141845.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/b07cba32-57f9-e95d-5bfe-6a5f6454068c.png)


この後にサンプルコードなどもありますが省略。プロジェクト作成の手順は問題なし。その後「テキストフィールドとボタンを配置します。」とありますが、その方法を聞いてみる。

![スクリーンショット 2024-01-20 142109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/cf5bf272-8524-c11b-02ab-c4b4a43c6cc5.png)

なるほど。

このあと下記の質問をしましたが、質問の概要だけ記載します。

- 「ConstraintLayoutって何？」
  - Bardのサンプルコードでは`LinearLayout`タグが使っており、android studioで自動生成されたコードでは`androidx.constraintlayout.widget.ConstraintLayout`が使われているという違いがあったため
- 「これって何の言語で書いてある？」
    - android studioが自動生成されたコードがKotlinじゃないかも！？と急に心配になったため
    - コードを張り付けて聞いた
- 「EditTextにidは必須？」
- 「tools:ignore="ExtraText"　は何ですか？」
    - android studioの電球マークをクリックしたら自動生成されたコードについて聞いた
- 「textview と edittext の違いは？」
- 「save ボタンなしでテキスト変更できる仕様にする場合は？」
- 「textwatcher と livedata の違いは？」

**とりあえずメモアプリ的な何かが完成しました！** Bardが色々教えてくれた通りに進めたところ、問題はあまりなく無事に完了した印象です。

### テストしてみる

「とりあえず作ったので動かしてみるぞ！」と思ったものの、エミュレーターが動かない。。ryzen だからか・・？と悩みつつ、大したアプリ作らないためエミュレーターは諦めて、実機で試す方針で進めました。

（なぜか「デバッガも不要でそのまま実機にインストールして実行しちゃおう！」という方針で進めたのですが、後ほどウィジェットでハマってログ欲しくなって実機でのデバッグの設定をしますが、またそれは先の話。。）

![スクリーンショット 2024-01-20 144157.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/a58a86f6-235e-7757-992e-cc8ca959dca6.png)

間違って途中でEnter押しちゃったけれど、汲み取ってくれました。これは流石！教えてもらった通りに進めたところ、無事にアプリは動きました。とりあえずひと段落。

### メモアプリに仕様を追加

下記の仕様を追加してみました。ウィジェットを作った時にするかもしれないことを試してます。

- メモ内容のファイルへの保存
- ボタンを追加し、押すと現在時刻がメモされるようにする
- メモの最大行数を5行にして、上記で追加したボタンを押したときに6行以上の場合は最後の5行を残して前半を削除する

Bardへ質問しながら進めたのですが、割と迷走したので大まかな流れだけ書きます。

#### メモ内容のファイルへの保存

- 「メモの内容をアプリが終了しても保持されるようにするにはどうすれば良いですか？」と聞いてみる
- `PreferenceManager`を使った方法を教えてくれるが、android studioが「非推奨だよ」と言ってくる
- 「PreferenceManager は非推奨のようですが、代替案はありますか？」と聞いてみる
- `Datastore`を使った方法を教えてくれるが理解できず、何度も質問して聞いてみるがイマイチ理解できない
    - 概念的なことがイマイチ理解出来なかった。。
    - 後で冷静に振り返って調べたら理解できた
- 当時は理解しきれなかったため、ファイルにテキスト保存する方針でBardに聞いたりググったりして完了
- アプリ起動時にそのテキストを読み込んで表示することはBardに聞いて完了
    - ファイルが無かった時の例外処理もBardに聞いて実装完了

#### その後

ボタン追加、現在時刻の取得、行削除、はBardに聞きながら無事に実装完了。大きな問題はなし。エラーやワーニングなども都度Bardに聞きながら解消して順調に進みました。

### ウィジェットの追加

アプリの開発が完了して、Bardもandroid studioも少しずつ使い慣れてきたのでウィジェットの追加を開始！

![スクリーンショット 2024-01-20 221954.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3675506/2902892d-3054-f283-d069-f1f644c35987.png)

しかし、色々聞きながら進めるもエラーが出たり、androidの実機の乗せてもウィジェット作成が出来ない。ググってみると「ウィジェットはandroid studioのメニューから追加できます」と書いてありました。
あー、そうですよね。色々な設定ファイルを全部手動で書き換えて頑張るとかじゃないよね、うん。大反省ですね。

参考：[AndroidのWidgetだけ開発](https://qiita.com/wakwak/items/1daf3c764c49fc3ffd8c)

### ウィジェットのテキスト更新

「表示は出来た！さて、あとはウィジェットにボタンを追加して、それを押したら現在時刻をテキストでメモすれば終わりだ！！」
と思ったものの、この後Bardに色々聞くも解決せず、結局google先輩に頼ることに。

#### 原因1. 情報が古い
主な原因は、`val button = remoteViews.findViewById<Button>(R.id.myButton_widget)`を使う方法をずっと提案してきたこと。
android studioが「`findViewById`は古くて使えないよ」と言うのでBardに聞いたら「`getViewById`を使えばいいよ」と答えるのだけれどそれもダメ。何度聞いても新しい解決策を提示してくれないためギブアップ。

ググったら下記が見つかって、その方法で進める。（Intentを用いる方法）

参考：[【Android/Kotlin】ウィジェットの作成とクリック時動作の設定方法](https://qiita.com/hacchi_/items/3c4a72818b9bc69d10aa)

#### 原因2. デバッガを使ってなかった

Intent を使えば良いと分かったものの、何度か試したが動かず。 **デバッガを使ったら一瞬で解決。** 動かない状態だとBardに聞くのも難しいし、、というか開発するならデバッガは使うべきですね。（当たり前）

下記の処理で`FLAG_MUTABLE`の部分を`FLAG_UPDATE_CURRENT`にしてたのですが、そこでエラーが発生しており、「`FLAG_MUTABLE`か`FLAG_IMMUTABLE`を使いなさい！」と書かれてました。

```kotlin
val timePendingIntent =
        PendingIntent.getBroadcast(context, 0, timeIntent, PendingIntent.FLAG_MUTABLE)
```

## 最後に

ちなみに、今回やりたいことは下記のページにほぼ全部書いてありました。（特にウィジェット関連）

参考：[AndroidアプリでWidgetをつくるまでの備忘録](https://zenn.dev/cou_kamiya/articles/2b9efd030c4e82)

作成してみたウィジェットは個人的にしばらく使ってみる予定です。気が向いたらBardに聞きながらアプリを一般公開してみても良いかな？と思ったりするのですが、それはまた別のお話。

### ウィジェット作成背景

なぜこんなウィジェットを作ったかも書いておきます。

タイマーをセットする程ではないけれど、その時の時刻をパッとメモしたい時が個人的にはたまにあるため、そういう時に使えるウィジェットが欲しいと思ったんです。
時刻を日記みたいに記録するアプリはいくつかあるのですが、個人的にはそこまでは必要なくて、ウィジェットでパッとメモして後で見たいだけで使えるアプリが欲しかったんです。

例えば、昼休憩が1時間で自由な時刻にとれる職場で働いているため、昼休憩に入った時間をパッと見れる場所にメモしたい！と思う時があります。休憩開始時にslackで連絡するためslackを見れば分かるのですがslackでそのチャンネルを開くことすら面倒で、androidのウィジェットでメモしてあれば楽だなと思うんです。（今までは付箋アプリのウィジェットでそこに手打ちしてた）

他には、「1時間○○円で自動延長」というような仕組みの店に行った時。カラオケやコンカフェなど。伝票を見れば分かるけれど、スマホでパッと入店時間が確認出来たら楽だと思うんですよね。

仕様は単純だし作るの簡単そうだし、ちょうど良いなと思って作ってみました。もし他にもっと楽な方法を使っている人がいたら教えてほしいです。「確かにそういう時ある！」って人が私以外にも何人かいたらアプリ一般公開の意味があるかもしれないな～と思ったので背景も書いてみました。

### 終わり

誤字脱字、分かりにくい点などあればご指摘いただけたら幸いです。
AIに聞きながらプログラミングしてみたい方、してみようと思っている方などの参考に少しでもなれば幸いです。
ある程度は何とかなる印象ですが、完全初心者は躓くとツライかもしれないかな、、とは思いました。

## バージョン情報

念のため開発時のバージョンを載せておきます。

- Android Studio Hedgehog | 2023.1.1 Patch 1
    - 開発途中でバージョン上げたため途中からgiraffeではない
- その他色々
    - 何のバージョン情報が必要か分かっていないレベルのため、設定ファイルからバージョンっぽいものをコピペしておきます

```kotlin
    id("com.android.application") version "8.2.1" apply false
    id("org.jetbrains.kotlin.android") version "1.9.0" apply false

    compileSdk = 34

    jvmTarget = "1.8"

    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.11.0")
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
    implementation("androidx.datastore:datastore-core-android:1.1.0-alpha07")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")

    distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip
```