びっくりドンキーからAndroidアプリを作って、これからストア公開する話
はじめに
事の発端は、愛用していた GitJournal というアプリでした。
GitJournal は GitHub のリポジトリをスマホで Markdown 編集できる神アプリなんですが、ある日アップデートしたら 2リポジトリ目から有料 になっていた。
金額は ¥3,120。
……¥3,120か。
着想からアプリ完成まで2時間。びっくりドンキーで食事しながらの2時間でした。
「¥3,120払うくらいなら、自分で作るか。」
そう思いながらびっくりドンキーでバーグディッシュを注文して、スマホを取り出して、そのままアプリ開発を始めました。
バーグが来る前に、36ファイルのスケルトンができていました。
Claude Cowork(Dispatch)とは
Claude Desktop の Cowork タブ(旧称: Dispatch)は、スマホから自宅や職場のMacをリモート操作して Claude Code を走らせられる機能です。
スマホ(外出先)
↓ Claude Cowork で接続
Mac(自宅)
├── Claude Code が動く
├── MCP 経由で各種サービスとも連携
└── コード生成・ビルド・Git push まで全部やってくれる
自分の構成はこんな感じです:
| 項目 | 内容 |
|---|---|
| スマホ | Android Pixel |
| Mac | Mac mini(自宅常時起動) |
| プラン | Claude Team |
| 繋いでいるMCP | Slack / Notion / GitHub / Filesystem / Gmail / Calendar / Asana / Figma など |
Mac のスリープ設定だけ忘れずに
Cowork の設定パネルにある 「スリープしない」トグルをオンにするだけ。
これさえやれば、あとはスマホの Claude アプリから Cowork タブを開くだけで繋がります。
びっくりドンキーから開発した話
「¥3,120なら自分で作れるだろ」という謎の確信のもと、注文直後にスマホで Cowork に接続。
Claude に投げた最初のプロンプトはこれだけです:
GitHubリポをcloneして手元でMarkdownを編集できる
GitJournalライクなAndroidアプリ作って。
新規リポをymatsuza1128配下に作って。
以上。
Claudeから返ってきた4点の確認質問
ベタな日本語で投げると、Claudeから対象アプリ・リポ可視性・スコープ・認証方式の4点を確認してくれました。
ここで「全部おまかせ」と返したら、後はほぼノータッチで進みました。
まず競合調査から始めてくれた
コードを書き始める前に、Claude が勝手に競合調査をしてくれました。
要約すると:
- GitJournal 一強だが、最終リリースは2024年4月 → メンテが薄い
- Obsidian Git はモバイルで "very unstable / SSH不可" と作者自身が明記
-
JGit on Android の採用例あり(
yappy/ajgit等)→ Kotlin ネイティブなら JGit が第一候補 - 差別化の勝ち筋:GitHub Issues / PR / Actions と地続きの「開発者向けノート」が最も有望・空席
Claude の競合調査まとめより:「汎用クローンは不利。GitHubと一体化した縦特化が勝ち筋(自信度:中)」
¥3,120払いたくないという感情から始まったのに、気づいたら市場分析まで終わっていました。
そのままコードを書き始めた
Claude が自走して進めてくれた内容(自分はスマホで眺めていただけ):
- GitHub リポジトリ作成(
ghCLI) - Kotlin + Jetpack Compose + JGit でスケルトン(36ファイル)
- OAuth Device Flow の設計
- ノート一覧・Markdown エディタ+プレビュー・設定画面の実装
- Phase 2 の GitHub Issue を 11件 自動起票
- リポ名
mzk-journal → github-markdownのリネーム(コード・パッケージ・README 全置換) - WIPコミット → push → NOTES.md に引き継ぎメモ記録
やりとりはこれだけ
バーグを食べながら Claude に送ったメッセージがこちら:
「現状チェック」
「対応必要?」
返ってきた答え:
github-markdown 進捗(180ターン目、進行中)
いまのところ対応不要です。タスクは自走中(ビルド検証フェーズ)。
止まったり、sudoが必要なポイントに当たったら NOTES.md に
「次に頼みたいこと」を書いて停止する設計なので、
その時はこちらで気づいて即お知らせします。
180ターン動いて、「対応不要」。
自分がやったのは注文と会計だけです。
記事を書かせながら、アプリ開発も続けた
途中でふと「この体験、記事にしたいな」と思ったので:
「これを記事化したいから、どういうことをやったか、まとめておいて」
Claude がそのまま体験記のドラフトを .md ファイルで出力してくれました(この記事の素材です)。
ドラフトが出たら即:
「Androidアプリのほう、続けて」
返ってきた答え:
github-markdown 進捗(227ターン目)
🎉 APK build 成功(20MB)
直近:「import修正コミット → Phase 3/4 epic起票」フェーズに入りました。
今日中に手元で動かす目標、達成見込み。
227ターン、APKビルド成功。
記事ドラフトを書かせながら、並行でアプリのビルドが通っていました。
しかも並列で動いていた
Cowork はタスクを並列で走らせられます。このセッションで同時にやっていたこと:
- Slack のスレッド読解・要約・代理返信
- 自分の担当 TODO 横断棚卸し(Slack + Notion + GitHub Issues を横断検索)
- 取引先との MTG 準備メモの作成
アプリ開発・Slack 整理・MTG 準備が全部同時に進んでいました。
作ったアプリ「github-markdown」の紹介
機能
| 機能 | 説明 |
|---|---|
| GitHub 認証 | OAuth Device Flow でログイン |
| リポジトリ一覧 | clone したリポのノート一覧 |
| Markdown エディタ | リアルタイムプレビュー付き |
| GitHub Flavored Markdown 対応 | テーブル・チェックリスト・コードブロック |
| Git 操作 | commit / push をアプリ内から |
技術スタック
言語: Kotlin
UI: Jetpack Compose
Git: JGit(Android で Git を動かすライブラリ)
認証: OAuth Device Flow
ビルド: Gradle
Claude Code が書いたコードの一例
1. Markdown エディタ画面(Jetpack Compose)
Edit / Preview のタブ切り替えで、編集中の Markdown をその場で Markwon にレンダリングさせています。
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditorScreen(relativePath: String, onBack: () -> Unit) {
val container = LocalAppContainer.current
val vm: EditorViewModel = viewModel(
key = "editor:$relativePath",
factory = viewModelFactory {
initializer {
EditorViewModel(relativePath, container.notes, container.git, container.settings)
}
},
)
val state by vm.state.collectAsState()
var tab by remember { mutableStateOf(0) }
Scaffold(
topBar = {
TopAppBar(
title = {
val titlePart = relativePath.substringAfterLast('/')
Text(text = if (state.isDirty) "* $titlePart" else titlePart, maxLines = 1)
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
}
},
actions = {
IconButton(onClick = { vm.saveLocal() }, enabled = state.isDirty) {
Icon(Icons.Default.Save, contentDescription = "Save locally")
}
IconButton(onClick = { vm.pushChanges() }, enabled = !state.isSyncing) {
Icon(Icons.Default.CloudUpload, contentDescription = "Commit & push")
}
},
)
},
) { padding ->
Column(modifier = Modifier.fillMaxSize().padding(padding)) {
TabRow(selectedTabIndex = tab) {
Tab(selected = tab == 0, onClick = { tab = 0 }, text = { Text("Edit") })
Tab(selected = tab == 1, onClick = { tab = 1 }, text = { Text("Preview") })
}
when (tab) {
0 -> EditorPane(content = state.content, onChange = vm::onContentChange)
else -> PreviewPane(markdown = state.content)
}
}
}
}
2. JGit ラッパ:commit & push
GitHub への push は Personal Access Token を UsernamePasswordCredentialsProvider の ユーザー名側 に渡すのが正解(パスワードは空)。地味にハマりがちなところを Claude が当たり前のようにやっていました。
suspend fun commitAndPush(
message: String,
author: PersonIdent,
branch: String,
): CommitInfo? = withContext(Dispatchers.IO) {
Git.open(workTreeProvider()).use { git ->
git.add().addFilepattern(".").call()
// Also stage deletions, which `add .` does not catch in JGit.
git.add().addFilepattern(".").setUpdate(true).call()
val status = git.status().call()
if (status.isClean) return@withContext null
val commit = git.commit()
.setMessage(message)
.setAuthor(author)
.setCommitter(author)
.call()
val pushCmd = git.push()
.setRemote("origin")
.add(branch.ifBlank { "main" })
tokenProvider()?.let { pushCmd.setCredentialsProvider(credentials(it)) }
pushCmd.call()
CommitInfo(
sha = commit.name,
shortMessage = commit.shortMessage,
authorName = commit.authorIdent.name,
authorEmail = commit.authorIdent.emailAddress,
whenEpochSec = commit.commitTime.toLong(),
)
}
}
private fun credentials(token: String) =
UsernamePasswordCredentialsProvider(token, "")
ハマりポイント:外出中は「許可ダイアログ」に答えられない
Cowork を外出先から使って気づいた一番の落とし穴がこれです。
Mac 側で sudo が必要なコマンドや、GUI の許可ダイアログが出る操作は、スマホ側から対応できません。
対処法として Claude が自分で考えてくれたのが:
sudo が必要なルートを回避する
(例: brew install --cask ではなく brew install openjdk)
→ どうしても詰まりそうなら NOTES.md にハンドオフを書いて停止
「外出中なので許可ダイアログには答えられない」と一度伝えておくと、その制約を保持したまま進めてくれます。
ストア公開(予定)
現在、ストア公開に向けて準備中です。Phase 3 として以下を進めています:
- リリースキーストアの生成と保管
- targetSdk を最新に追従
-
.aabでリリースビルドを出してアップロード - プライバシーポリシーと利用規約のドラフト
公開後にこのセクションをアップデートします。
./gradlew bundleRelease
# → app/build/outputs/bundle/release/app-release.aab
まとめ:外出先でアイデアが出たら、そのまま作れる
着想から完成まで、約2時間。 GitJournal に ¥3,120払いたくないという、ちょっとケチな動機から始まったアプリが、びっくりドンキーのランチ中に産声を上げました。
Cowork(Dispatch)を使ってみて変わったこと:
- 「帰ってからやろう」がなくなった。アイデアが出た瞬間に着手できる
- 「とりあえず骨格だけ」が一瞬で済む。一文投げれば36ファイル生えてくる
- スキマ時間の密度が上がった。「現状チェック」「対応必要?」の2単語で180ターン分の進捗が返ってくる
- プロンプトがゴミでも全然やれる。「全部おまかせ」で36ファイル、「続けて」でAPKビルドが通る
¥3,120のアプリ代をケチった結果、自分専用の(そして無料の)アプリができました。
API 換算で試算すると、このセッション単体で消費したトークンは約 ¥1,800 相当。節約額 ¥3,120 に対してギリギリ黒字です。自分は定額プランなのでそもそも気にしていませんが。
ちなみにこの記事の投稿も、Dispatch に一文投げて終わりました。
「このMarkdownをQiitaに投稿して。スクショ3枚は[TODO]のところに差し込んで。タグは Claude / AndroidDev / Kotlin で」
びっくりドンキーで着想 → Dispatch で開発 → Dispatch で記事を書かせる → Dispatch で投稿。
全部 Dispatch で完結しました。