Lunch Log — ランチ記録・Drive自動バックアップ・友達共有Androidアプリ開発まとめ

1. Lunch Log とは

1-1. 開発の背景・課題

毎日のランチをどこで食べたか、いくら使ったかを手軽に記録したいと思ったことはありませんか。既存のグルメアプリは「お店を探す」ことに特化しており、「自分の食事履歴を管理する」用途には不向きです。そこで、写真・位置情報・金額・ハッシュタグをセットで記録し、地図・カレンダー・統計で振り返れるAndroidアプリ「Lunch Log」を開発しました。

さらに、Google Driveへの自動バックアップ友達とのQRコード共有機能を搭載し、スマホの機種変更時も安心してデータを引き継げます。

1-2. 既存アプリ・ツールとの比較

機能Lunch Log食べログGoogleマップメモアプリ
食事記録✅ 専用設計❌ 口コミ向き⚠️ 手動保存⚠️ テキストのみ
位置情報付き記録✅ 自動取得
支出管理✅ 統計・グラフ⚠️ 手動計算
Driveバックアップ✅ 自動⚠️ 手動
友達と共有✅ QRコード✅ SNS✅ リスト共有
広告なし購入✅ IAP対応多数対応

2. 技術スタック

技術スタック UI 層 Jetpack Compose / Material3 / Navigation Compose / Coil / Accompanist ViewModel 層 ViewModel / StateFlow / Kotlin Coroutines / Hilt DI Repository 層 LunchRepository / DriveRepository / WishlistRepository / AdFreeRepository データ層 Room DB (v9) / DataStore Preferences / Google Drive API / Google Play Billing
カテゴリライブラリ・技術バージョン
言語Kotlin最新安定版
UIJetpack Compose / Material3BOM 2024.x
DIHilt2.51
DBRoomv9
設定永続化DataStore Preferences1.1.x
バックグラウンドWorkManager / AlarmManager
地図Google Maps Compose + maps-android-utils
画像読み込みCoil (AsyncImage)3.x
広告Google Mobile Ads SDK23.1.0
課金Google Play Billing Library7.1.1
QRコード生成qrose1.0.1
QRスキャンML Kit + CameraX17.3.0 / 1.4.1
JSONGson

3. アーキテクチャ・データフロー

3-1. アーキテクチャ解説

本アプリは MVVM(Model-View-ViewModel) アーキテクチャを採用しています。UIはJetpack Composeで構築し、StateFlowを通じてViewModelの状態を購読します。データアクセスはRepositoryパターンで抽象化し、ViewModel からはRepositoryのインターフェースのみに依存します。依存性注入はHiltで管理し、テスト容易性も確保しています。

アーキテクチャ・データフロー Google Drive API Places API (Google Maps) AdMob Repository DriveRepository LunchRepository WishlistRepository AdFreeRepository WorkManager AlarmManager Local Storage Room DB / DataStore filesDir/images/ ViewModel MapViewModel EntryViewModel SettingsViewModel StatsViewModel GalleryViewModel ShareViewModel UI MapScreen EntryScreen Settings Gallery Calendar Share/Scan

3-2. APIシーケンス(Driveバックアップ)

APIシーケンス図(Drive バックアップ) UI/ViewModel DriveRepository Google Auth Drive API バックアップ開始 getAccessToken(accountName) token PATCH lunchlog_backup.json 200 OK GET appDataFolder (photo_*) drivePhotoEntryIds POST photo_{entryId}_{index}.jpg fileId isSynced=true 更新 PATCH lunchlog_settings.json バックアップ完了通知

3-3. DBスキーマ

DBスキーマ(Room Database v9) lunch_entries 🔑 idTEXT (UUID, PK) storeNameTEXT photoUriTEXT? (内部URI) photoUrisTEXT (CSV) latitudeDOUBLE longitudeDOUBLE costINTEGER? memoTEXT hashtagsTEXT (CSV) mealTypeTEXT (enum) placeIdTEXT? createdAtINTEGER (Date) isSyncedBOOLEAN (Drive同期済) thumbnailUriTEXT? (200px縮小版) ownerTagTEXT (“self”/友達名) wishlist_entries 🔑 idTEXT (UUID, PK) placeIdTEXT storeNameTEXT addressTEXT latitudeDOUBLE longitudeDOUBLE memoTEXT hashtagsTEXT (CSV) ownerTagTEXT (“self”/友達名) createdAtINTEGER (epoch ms) DataStore Preferences drive_account_name / last_backup_time friend_share_urls / reminder_hour/minute share_file_id / share_owner_name 他

4. 画面構成・機能一覧

画面遷移図 ナビゲーションバー: マップ / ギャラリー / 行きたい / カレンダー / 統計 / 設定 マップ ホーム画面 ギャラリー 写真グリッド 行きたい ウィッシュリスト カレンダー 月間表示 統計 設定 記録追加/編集 EntryScreen 写真ビューア フルスクリーン 友達管理 FriendListScreen 共有 / QR ShareScreen QrScanScreen Drive連携 バックアップ/復元 リマインダー 通知設定
画面主な機能
マップ(ホーム)ランチ記録と行きたいリストをカスタムマーカーで表示。クラスタリング・訪問回数バッジ付き
ギャラリー写真グリッド(3列)。ハッシュタグフィルター・並び替え・フルスクリーンビューア(ピンチズーム対応)
行きたいGoogleマップ共有URLまたは手動検索で登録。自分/友達タブ切り替え。マップ連携
カレンダー月間カレンダー。記録のある日にドット表示。タップで記録一覧確認・追加
統計月別支出グラフ・店舗TOP10・ハッシュタグ内訳・連続記録日数・今月vs先月比較
設定リマインダー・Driveバックアップ/復元・ハッシュタグ管理・IAP広告非表示
記録追加/編集店名・写真・金額・ハッシュタグ・メモ・位置情報を入力。オフライン記録にも対応
共有/QRスキャンDriveにJSON公開→QRコード生成。友達のQRをスキャンして記録を取り込み

5. 使い方ガイド

5-1. 初回セットアップ

  1. アプリをインストールして起動します
  2. マップ画面のFABボタン(+)をタップして最初のランチを記録します
  3. 設定画面からGoogleアカウントを接続してDriveバックアップを有効にします
  4. リマインダーを設定して毎日の記録忘れを防ぎます(推奨:昼12時)
  5. 友達と共有したい場合は「友達と共有」セクションからQRコードを生成します

5-2. 主要機能の使い方

  1. ランチを記録する:マップ画面FAB →店名(または「店名なし」)→写真追加→位置情報自動取得→保存
  2. 過去のお店を再利用する:店名フィールドの時計アイコンをタップ→よく行くお店TOP5から選択→位置情報も自動入力
  3. 行きたいリストに追加する:Googleマップでお店を開いて「共有」→Lunch Logに転送。または行きたい画面から手動検索
  4. Driveバックアップを実行する:設定→Google Drive→「今すぐバックアップ」。自動バックアップも設定可能(6時間〜7日間隔)
  5. 友達の記録を取り込む:設定→友達と共有→「QRコードをスキャン」→友達のQRを読み取る

6. 主な実装上のポイント

6-1. Drive写真バックアップの差分同期

isSyncedフラグだけでは「フラグは立っているが実際にはDriveに写真が存在しない」ケースが発生しました。そこでバックアップ実行時にDriveの実際のファイル一覧を照合し、フラグと実態の不整合を検出して自動再アップロードする方式を採用しています。

// Drive上の写真entryIdを取得して照合
val drivePhotoEntryIds = driveRepository.listPhotoFileIds(token).keys
entries.filter { entry ->
    !entry.isSynced || (entry.allPhotoUris.isNotEmpty() && entry.id !in drivePhotoEntryIds)
}.forEach { entry ->
    // 差分のみアップロード
}

6-2. appDataFolder スコープによる安全なバックアップ

当初はdrive.fileスコープを使用していましたが、再インストール後に新しいOAuthグラントが発行されると旧グラントで作成したファイルがfiles.listに返ってこなくなる問題が発生しました。drive.appdataスコープに切り替えることで、OAuthグラントをまたいでも常に同一アカウントのバックアップデータにアクセスできます。

// appDataFolderへのアップロード
val url = URL("=multipart")
// spaces=appDataFolder でアクセス
val listUrl = URL("=appDataFolder&q=")

6-3. ClusterManagerの2分割によるマーカー色分け

自分と友達のマーカーを混在させると同じクラスターにグループ化されてしまいます。ownClusterManagerfriendClusterManagerの2つを独立して管理することで、それぞれ独自のカスタムレンダラーで色分けしつつ、正しくクラスタリングできます。

// 2つのClusterManagerを独立管理
val ownClusterManager = ClusterManager(context, map)
val friendClusterManager = ClusterManager(context, map)
ownClusterManager.renderer = CustomClusterRenderer(context, map, ownClusterManager, emptyMap())
friendClusterManager.renderer = CustomClusterRenderer(context, map, friendClusterManager, friendColorMap)

6-4. リマインダーの正確なアラーム実装

WorkManagerのPeriodicWorkRequestはバッテリー最適化の影響で正確な時刻に発火しません。AlarmManager.setExactAndAllowWhileIdle()とBroadcastReceiverの組み合わせで毎日自走する自走型アラームを実装しています。Android 12以上で権限が取得できない場合は近似アラームにフォールバックします。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !alarmManager.canScheduleExactAlarms()) {
    alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
} else {
    alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, cal.timeInMillis, pendingIntent)
}

7. 使用技術の解説

Jetpack ComposeはAndroid公式のモダンUIツールキットです。XML不要で、KotlinコードだけでUIを宣言的に記述できます。状態変化を自動的にUIに反映するため、従来のビュー操作が不要になります。

HiltはDagger2をベースにしたAndroid向けDI(依存性注入)フレームワークです。ViewModelやWorkerへの依存を自動的に解決し、テストの差し替えも容易です。

RoomはSQLiteをラップしたAndroid公式ORMです。DAOインターフェースに定義したアノテーションだけでSQL文を自動生成します。FlowやSuspend関数と組み合わせてリアクティブなDBアクセスが可能です。

Google Drive API(appDataFolder)はアプリ専用の隠しストレージです。ユーザーのDriveに保存されますが、ユーザーには見えないため誤削除の心配がありません。アカウントに紐づくため再インストール後も復元できます。

ML Kit Barcode ScanningはGoogleが提供するオンデバイスのQRコード解析SDKです。CameraXと組み合わせてリアルタイムスキャンを実現しています。サーバー通信が不要なためオフラインでも動作します。


8. 開発プロセス

フェーズ期間主な内容
基盤構築〜2026-05-21MVVM/Hilt/Room基盤・マップ表示・記録CRUD・Drive写真バックアップ
UI/UX改善2026-05-22〜24サムネイル事前生成・ギャラリービューア・統計画面リニューアル・ピンチズーム
共有機能2026-05-26〜28QRコード共有・友達マーカー色分け・行きたいリスト・CameraX対応
ToS準拠2026-05-29〜31Places API ToS対応・Powered by Google表示・HTTPS強制・ProGuard設定
安定化2026-06-01〜06Drive appDataFolder移行・友達行きたいリスト共有・権限整理・バグ修正42件
リリース2026-06-07v2.0 Play Store 製品版審査申請

9. 今後の改善候補

  • GCP APIクォータ設定(Places API 200req/日、Maps 1000loads/日)
  • Unit テスト・UIテストの充実
  • Widget(ホーム画面からワンタップ記録)
  • 店舗の写真をDriveに保存する複数写真対応の拡充
  • 月次レポートの自動生成・共有機能
  • Apple Watch / Wear OS への対応

10. 動作要件

  • OS: Android 7.0(API 24)以上
  • Googleアカウント(Driveバックアップ・共有機能に必要)
  • カメラ権限(QRコードスキャン・写真撮影)
  • 位置情報権限(ランチ記録の位置情報自動取得)
  • インターネット接続(Driveバックアップ・マップ表示・Places API)
  • 通知権限(Android 13以上・リマインダー通知)

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA