Android開発でCursorを使ってみた

# はじめに

Android用のSNSアプリ TwitPane, ZonePane などを開発しています。

生成AIのChatGPT(個人的に愛称チャッピーで呼んでいます)はずいぶん前から使っていて開発中の質問やアイデアの壁打ち等に活用しています。

昨年末あたりから爆発的な盛り上がりを見せているエージェント型AIブームに乗り遅れまいと少しずつ調べていましたが、エージェント型AIの代表格のCursorくんが VS Code ベースということでどうにも Android 開発(言うまでもなく Android Studio で開発するのが普通です)では採用できないんじゃないかと二の足を踏んでいました。

しかし、チャッピーや Claude に質問すれば Android 開発のあらゆることを教えてくれるため、ソースコードも編集できるのではないかと考え、試してみました。

# まとめ

長くなったので先にまとめを書きます。

- 今回はわりと単純作業の「build.gradleのkts化」と「文字列のリソース化、翻訳」を Cursor くんに実施してもらった
- Android アプリ開発は Android Studio を使うのが当たり前なので Cursor 導入を諦めていたが修正作業自体は問題なく行えることが分かった
- Cursorくんはコードベース全体を参照して作業してくれるのでさらに多くのタスクを任せられそう


# やってみたこと

## Cursor 導入

- Cursor ダウンロード
- 日本語化プラグイン導入
    - Japanese Language Pack for VS Code
- Androidアプリのフォルダを開く
- いくつかのプラグインが提案されるので指示に従って導入（不要なものもあるかもしれない）
    - Kotlin IDE for Visual Studio Code
    - Language support for Java ™ for Visual Studio Code
    - Gradle for Java
    - Extension Pack for Java


## まずは build.gradle の kts 化から試してみた

`modules/` ディレクトリ配下に 70 ほどある `build.gradle` を `build.gradle.kts` に変換する作業を任せてみました。

- まずは個別のファイルを渡して依頼してみる
>@build.gradle このファイルをkts化してください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/bbc30dfa-1cf1-4158-a15f-a0122c8b24e9.png)

- いい感じだったので残りのファイルもやらせる。まずはディレクトリ構造を理解しているか聞いてみる
>modules 配下の build.gradle を build.gradle.kts に変換したいです。まずは対象ファイル一覧を確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/cb04c9f5-01bf-4645-aed4-979ba664a3be.png)

>まずは auth_api モジュールから始めてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/a89587fd-270c-4470-8b93-bc0b1cec8c3f.png)


- というような流れでどんどん進めてもらった



- 以下、Bluesky でつぶやいていたことをまとめていきます。

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:mm5foq3omooqaqpdjfbjgohe/app.bsky.feed.post/3lk6j2fh3o22d" data-bluesky-cid="bafyreidyqoyfv6z2fgvswwwlgw2jug45uuj73snwlvsu3ga3mqizbnouh4" data-bluesky-embed-color-mode="system"><p lang="ja">cursorくんがbuild.gradleをbuild.gradle.ktsに変換してくれる様子。すごいなー<br><br><a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lk6j2fh3o22d?ref_src=embed">[image or embed]</a></p>&mdash; たけうちひろあき (<a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe?ref_src=embed">@takke.jp</a>) <a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lk6j2fh3o22d?ref_src=embed">2025年3月12日 20:46</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

<iframe width="560" height="315" src="https://www.youtube.com/embed/utEtYGMEWQQ?si=VnCMOeo3b-gZgyDc&amp;start=16" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:mm5foq3omooqaqpdjfbjgohe/app.bsky.feed.post/3lk6kdpyjsc2d" data-bluesky-cid="bafyreiduf7lna7gkdknrccbmlnke3bnub7zm5tmzzhey5s22odqrpqzmmy" data-bluesky-embed-color-mode="system"><p lang="ja">これくらいシンプルな作業でもプロンプトによって(当然だけど)やり方が変わってくる。いくつか試して一番良さそうなのはこの手順。愚直に指示通りにやってくれるのでかわいい。<br><br><a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lk6kdpyjsc2d?ref_src=embed">[image or embed]</a></p>&mdash; たけうちひろあき (<a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe?ref_src=embed">@takke.jp</a>) <a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lk6kdpyjsc2d?ref_src=embed">2025年3月12日 21:09</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/23f4983f-25b7-4486-9921-09e2f55bd99f.png)

- 元の指示では build.gradle を元に build.gradle.kts を作成し、build.gradle を消していたので git の履歴が残らなかった。まず中身を変更してからファイル名を変える手順なら履歴が残るのでその手順で進めてもらった。


- いい流れになってきたので yolo モードでどんどん進めて貰った

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:mm5foq3omooqaqpdjfbjgohe/app.bsky.feed.post/3lk6lenda2k2d" data-bluesky-cid="bafyreiauxguz5rockb2sbtgjtg4rbvswhmx7alkahm4rggs6ejg7imu4g4" data-bluesky-embed-color-mode="system"><p lang="ja">Cursorくんのyoloモードでどんどん変換してもらう様子<br><br><a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lk6lenda2k2d?ref_src=embed">[image or embed]</a></p>&mdash; たけうちひろあき (<a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe?ref_src=embed">@takke.jp</a>) <a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lk6lenda2k2d?ref_src=embed">2025年3月12日 21:27</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

<iframe width="560" height="315" src="https://www.youtube.com/embed/17QFuUr-7GA?si=3oY0iLvtmXBGjnas" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

- 70ほどのbuild.gradleを2時間かからずに変換してくれた。
    - 指示を与えるのが遅かったりyoloモードについて調べたり、途中でCursorの接続が止まってPC再起動したりしながらだったので時間がかかったが、うまくプロンプトを渡せれば1時間かからずにできたと思う
- いずれにしても手作業では面倒すぎてやらなかった作業なので進められて良かった

## Compose関数の共通化

- アプリのオンボーディング画面で使うロゴの合成処理を共通化させてみた
  - Box/Image/Icon で構成されるロゴ合成処理がコピペで2箇所ある状態

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/d46c7f90-9b2d-4164-b79c-9087d382d6aa.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/3eac6ae9-7ac9-4578-aa2e-772c966238fb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/907fde41-edc0-435e-bcb2-1d1a810127b0.png)

- 他の箇所でも使えるようにパラメータを追加させてみた。ちゃんと意図通りに実施してくれた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/68817457-6fe4-4a03-8a09-55dce525c207.png)

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:mm5foq3omooqaqpdjfbjgohe/app.bsky.feed.post/3lkba3ztels2g" data-bluesky-cid="bafyreiaez22l44z22zkjh2sjlmybhr2sql3enggp26uyq5u3x3qfqk35ou" data-bluesky-embed-color-mode="system"><p lang="ja">いよいよぞーぺんのKotlinのロジックをCursorくんに修正させてみてるけど簡単な「ロジック共通化」くらいのタスクならシュッとやってくれる。importが足りなくて追加してくれたりとか割とちゃんとしてる。<br><br><a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lkba3ztels2g?ref_src=embed">[image or embed]</a></p>&mdash; たけうちひろあき (<a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe?ref_src=embed">@takke.jp</a>) <a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lkba3ztels2g?ref_src=embed">2025年3月13日 22:44</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>


## 文字列のリソース化と翻訳

### 文字列のリソース化

- 下記のようなコードがあり、文字列をリソース化したい。Androidアプリ開発中の典型的な手作業の単純作業だけど、うちのアプリでは文字列を専用モジュールに配置していてちょっと特殊。できるかな？

```kotlin
@Composable
private fun OnboardingPage1(theme: Theme) {
  OnboardingPageLayout(
    // ...
    titleText = "ようこそ、ZonePaneへ！",
    items = listOf(
      "ZonePaneは、複数のSNSを1つのアプリで管理できるクライアントアプリです！",
      "快適にSNSを楽しむための機能が満載です！",
    )
  )
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/db609b68-65f8-45e2-869e-8a1bc501ce6c.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/646a8d6f-f51b-4e63-a8f4-6f9099c4ee48.png)

- 「まずは OnboardingPage1 の titleText を実施してください」という指示で items も変換しちゃうのがかわいいというか、リクエスト節約してくれてありがたいというか。。

- 続いて他の関数もリソース化してもらった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/64da999e-dc54-4acc-bb64-bacbb0a2e720.png)

## 文字列リソースの翻訳

- さてリソース化できたので次は翻訳。ざっくりとした指示で実施してくれた。
> 追加したリソースの英語版を values/strings.xml に追加してください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/6e3dd5d5-736e-4e58-bfec-3450204b11d5.png)

- うちのアプリではさらに5言語が必要なので追加してもらった
> 同様に values-es, values-ko, values-zh, values-zh-rHK, values-zh-rTW にも追加してください

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/39221/f77990b6-54fc-48a0-bfe7-70842dec33ed.png)

- こんな感じで翻訳してくれた。

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:mm5foq3omooqaqpdjfbjgohe/app.bsky.feed.post/3lkbbhdmglk2g" data-bluesky-cid="bafyreibfgaao2vo2tkkzoyqfe2whvx4gon2ljrsvusy6ukdst5mhal2gnu" data-bluesky-embed-color-mode="system"><p lang="ja">Androidアプリ開発で一番だるい作業の「文字列のリソース化」をCursorくんが手伝ってくれるようになったのでこれは嬉しい。特別な指示をしていないのにちゃんと PublicResourceString という alias で参照するように作るところが賢い。ちなみに claude-3.7-sonnet を使ってる<br><br><a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lkbbhdmglk2g?ref_src=embed">[image or embed]</a></p>&mdash; たけうちひろあき (<a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe?ref_src=embed">@takke.jp</a>) <a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lkbbhdmglk2g?ref_src=embed">2025年3月13日 23:08</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

### 他の箇所の文字列リソースの翻訳事例

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:mm5foq3omooqaqpdjfbjgohe/app.bsky.feed.post/3lkcqrckhms2g" data-bluesky-cid="bafyreif4trm3hfw3iemnv7o5cogi2radobxchoetmt63yjoq245ldnctae" data-bluesky-embed-color-mode="system"><p lang="ja">Cursorくんに文字列リソースの翻訳をしてもらっている様子。

事前に英語リソース config_auto_save_count_none を自分で追加し、下記の2プロンプトを実行。
「config_auto_save_count_none を調べてください。」
⇒日本語リソースが追加された。
「他の5言語についても config_auto_save_count_none を追加してください。TwitPanePublicResourceモジュール内の文字列です。」
⇒動画の通り、5言語分が追加された。<br><br><a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lkcqrckhms2g?ref_src=embed">[image or embed]</a></p>&mdash; たけうちひろあき (<a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe?ref_src=embed">@takke.jp</a>) <a href="https://bsky.app/profile/did:plc:mm5foq3omooqaqpdjfbjgohe/post/3lkcqrckhms2g?ref_src=embed">2025年3月14日 13:14</a></blockquote><script async src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>