0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

びっくりドンキーからAndroidアプリを作って、これからストア公開する話

0
Last updated at Posted at 2026-05-10

びっくりドンキーから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 リポジトリ作成(gh CLI)
  • 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 で完結しました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?