diff --git a/CLAUDE.md b/CLAUDE.md index f432105..3518627 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,10 +1,10 @@ -# プロジェクト名 +# MiniTIAS - +千葉大学 TIAS(舌画像撮影・解析システム)のスマートフォン版.Flutter で開発し,初期は撮影・保存に特化する. ## 開発進捗 -現在の進捗: GUIDE_01 に従いプロジェクト立ち上げ中 +現在の進捗: 仕様設計フェーズ進行中(SPEC_01 作成済み).次は規約整備・開発計画フェーズ. ※ ステップ完了時にここを更新すること. ## 必須ルール(コード実装時) @@ -40,12 +40,14 @@ ### 02_ENV(環境) - +- 技術スタック: docs/02_ENV/ENV_01_技術スタック.md +- 環境構築手順: docs/02_ENV/ENV_02_環境構築手順.md +- 管理者用環境構築手順: docs/02_ENV/ENV_03_管理者用環境構築手順.md ### 03_PLAN(計画) - +- 要件定義書: docs/03_PLAN/PLAN_01_要件定義書.md ### 04_SPEC(仕様・設計) - +- 画面機能仕様書: docs/04_SPEC/SPEC_01_画面機能仕様書.md diff --git "a/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" new file mode 100644 index 0000000..b68984e --- /dev/null +++ "b/docs/04_SPEC/SPEC_01_\347\224\273\351\235\242\346\251\237\350\203\275\344\273\225\346\247\230\346\233\270.md" @@ -0,0 +1,317 @@ +# 画面機能仕様書 (Screen & Function Specification) + +本ドキュメントでは,MiniTIAS アプリの画面設計,画面遷移,カメラ制御,ファイル管理,およびアーキテクチャを定義する. + +## 画面一覧 (Screen List) + +| ID | 画面名 | 概要 | +| --- | --- | --- | +| S-01 | 撮影画面 | インカメラのライブプレビューを表示し,撮影を行う | +| S-02 | 一覧画面 | 撮影済み画像のサムネイル一覧を表示し,確認・削除を行う | + +※ 初期バージョンでは 2 画面構成とする. + +## 画面遷移 (Screen Transition) + +```text +[アプリ起動] + │ + ▼ +┌──────────┐ BottomNavigationBar ┌──────────┐ +│ S-01 │ ◄──────────────────────────────► │ S-02 │ +│ 撮影画面 │ タブ切り替え │ 一覧画面 │ +└──────────┘ └──────────┘ + │ + │ サムネイルタップ + ▼ + ┌──────────┐ + │ 拡大表示 │ + │ (ダイアログ) │ + └──────────┘ +``` + +- アプリ起動時のデフォルト画面は **撮影画面(S-01)** とする +- 画面切り替えには `BottomNavigationBar` を使用する(タブ 2 つ: 撮影 / 一覧) +- 画像の拡大表示はダイアログ(`showDialog`)で実装し,独立画面としない + +## 撮影画面 (S-01: Capture Screen) + +### レイアウト + +```text +┌─────────────────────────┐ +│ BottomNavBar │ ← 画面下部(通常位置) +├─────────────────────────┤ +│ │ +│ │ +│ カメラプレビュー │ ← 上下反転表示(180°回転) +│ (画面全体) │ +│ │ +│ │ +├─────────────────────────┤ +│ [シャッターボタン] │ ← 画面上部(端末を逆さに置くため) +└─────────────────────────┘ +``` + +※ 端末を逆さに置いて使用するため,UI 全体を 180° 回転する.操作者から見ると通常の上下配置に見える. + +### UI 要素 + +| 要素 | 仕様 | +| --- | --- | +| カメラプレビュー | インカメラ(前面カメラ)のライブ映像.UI 全体を 180° 回転して表示 | +| シャッターボタン | 丸型ボタン.タップで撮影実行.連続タップ可(連写対応) | +| BottomNavigationBar | 撮影タブ(アクティブ)/ 一覧タブ | + +### 動作仕様 + +- **起動時**: カメラの初期化を行い,プレビューを開始する +- **撮影**: シャッターボタンタップで静止画をキャプチャし,PNG 形式で保存する +- **連続撮影**: 前回の保存完了を待たずに次の撮影が可能.撮影中はシャッターボタンの操作を受け付ける +- **フィードバック**: 撮影成功時にスナックバーで「保存しました: [ファイル名]」を表示する +- **エラー時**: カメラ初期化失敗やストレージ書き込み失敗時は,エラーメッセージをスナックバーで表示する + +## 一覧画面 (S-02: Gallery Screen) + +### レイアウト + +```text +┌─────────────────────────┐ +│ BottomNavBar │ +├─────────────────────────┤ +│ ┌─────┐ ┌─────┐ ┌─────┐│ +│ │ │ │ │ │ ││ +│ │ img │ │ img │ │ img ││ +│ │ │ │ │ │ ││ +│ └─────┘ └─────┘ └─────┘│ +│ ┌─────┐ ┌─────┐ ┌─────┐│ +│ │ │ │ │ │ ││ +│ │ img │ │ img │ │ img ││ +│ │ │ │ │ │ ││ +│ └─────┘ └─────┘ └─────┘│ +│ ... │ +├─────────────────────────┤ +│ [画面上部エリア] │ ← 逆さ配置のため上部が操作側 +└─────────────────────────┘ +``` + +※ 撮影画面と同様に UI 全体を 180° 回転して表示する. + +### UI 要素 + +| 要素 | 仕様 | +| --- | --- | +| サムネイルグリッド | 3 列のグリッド表示.新しい画像が先頭(降順) | +| サムネイル | 正方形クロップ.タップで拡大表示 | +| ファイル名ラベル | 各サムネイル下部にファイル名を表示 | +| BottomNavigationBar | 撮影タブ / 一覧タブ(アクティブ) | + +### 拡大表示ダイアログ + +| 要素 | 仕様 | +| --- | --- | +| 画像 | 元画像をフル解像度で表示.ピンチ操作でズーム可能 | +| ファイル名 | ダイアログ上部にファイル名を表示 | +| 削除ボタン | ダイアログ内に配置.タップで削除確認ダイアログを表示 | +| 閉じるボタン | ダイアログ外タップまたは閉じるボタンで閉じる | + +### 動作仕様 + +- **画面表示時**: `Pictures/MiniTIAS/` ディレクトリ内の PNG ファイルを走査し,一覧を構築する +- **リフレッシュ**: 撮影画面から戻った際に一覧を再取得する +- **削除**: 削除確認ダイアログで「削除」を選択すると,ファイルをストレージから削除し,一覧から除去する +- **空状態**: 画像が 0 件の場合は「撮影した画像がありません」のメッセージを表示する + +## カメラ制御仕様 (Camera Control) + +### カメラ設定 + +| 項目 | 値 | +| --- | --- | +| 使用カメラ | フロントカメラ(インカメラ) | +| 解像度 | `ResolutionPreset.max`(カメラが対応する最大解像度) | +| フォーカス | オートフォーカス(デフォルト動作) | +| フラッシュ | OFF(LED ライトはアタッチメント側で制御) | +| 画像フォーマット | カメラ出力は JPEG → アプリ内で PNG に変換して保存 | + +### プレビュー表示 + +- `camera` パッケージの `CameraPreview` ウィジェットを使用する +- UI 全体を `Transform.rotate(angle: pi)` で 180° 回転する +- プレビューのアスペクト比はカメラのネイティブ比率を維持する + +### 撮影フロー + +```text +シャッターボタンタップ + │ + ▼ +CameraController.takePicture() + │ + ▼ +一時ファイル(XFile)取得 + │ + ▼ +PNG エンコード(image パッケージ) + │ + ▼ +ファイル名生成(命名規則に従う) + │ + ▼ +Pictures/MiniTIAS/ に保存 + │ + ▼ +MediaStore 通知(ギャラリーアプリへの反映) + │ + ▼ +スナックバーで保存完了を通知 +``` + +### ライフサイクル管理 + +- `WidgetsBindingObserver` を使用し,アプリのライフサイクルを監視する +- **バックグラウンド遷移時**(`paused`): カメラリソースを解放する(`CameraController.dispose()`) +- **フォアグラウンド復帰時**(`resumed`): カメラを再初期化する +- 画面遷移でカメラを使用しない画面に移動した場合もカメラリソースを解放する + +## ファイル管理仕様 (File Management) + +### 保存先 + +- パス: `Pictures/MiniTIAS/`(Android 共有ストレージ) +- Android 10 以上では MediaStore API を使用する(`saver_gallery` パッケージで抽象化) +- ディレクトリが存在しない場合は自動作成する + +### ファイル命名規則 + +- 基本形式: `MiniTIAS_YYYYMMDD_HHmmss.png` +- 同秒重複時: `MiniTIAS_YYYYMMDD_HHmmss_1.png`,`MiniTIAS_YYYYMMDD_HHmmss_2.png`,... +- タイムスタンプはデバイスのローカル時刻を使用する + +### 重複回避ロジック + +```text +ファイル名候補 = "MiniTIAS_{timestamp}.png" + +IF ファイル名候補が存在しない: + RETURN ファイル名候補 + +suffix = 1 +WHILE "MiniTIAS_{timestamp}_{suffix}.png" が存在する: + suffix += 1 + +RETURN "MiniTIAS_{timestamp}_{suffix}.png" +``` + +### 削除 + +- 一覧画面の拡大表示ダイアログから削除を実行する +- 削除前に確認ダイアログを表示する(「この画像を削除しますか?」) +- ファイルシステムから物理削除する(ゴミ箱機能なし) +- 削除後,MediaStore からも当該エントリを除去する + +## パーミッション管理 (Permission Handling) + +### 必要なパーミッション + +| パーミッション | 用途 | 対象 API レベル | +| --- | --- | --- | +| `CAMERA` | カメラプレビュー・撮影 | 全バージョン | +| `WRITE_EXTERNAL_STORAGE` | 画像保存 | API 28 以下 | +| `READ_EXTERNAL_STORAGE` | 画像一覧取得 | API 28 以下 | +| `READ_MEDIA_IMAGES` | 画像一覧取得 | API 33 以上 | + +※ API 29〜32 では Scoped Storage により,アプリが MediaStore 経由で保存したファイルは追加権限なしでアクセス可能. + +### パーミッション要求フロー + +```text +アプリ起動 + │ + ▼ +カメラ権限を確認 + │ + ├── 許可済み → カメラプレビュー開始 + │ + └── 未許可 → 権限リクエストダイアログ表示 + │ + ├── 許可 → カメラプレビュー開始 + │ + └── 拒否 → 「カメラの権限が必要です」メッセージ表示 + + 設定画面への誘導ボタン +``` + +- ストレージ権限は撮影時・一覧表示時にそれぞれ確認する +- `permission_handler` パッケージを使用する + +## アーキテクチャ (Architecture) + +### 状態管理 + +- `provider` パッケージを使用する +- 画面ごとに ChangeNotifier を作成し,ビジネスロジックを UI から分離する + +### Provider 構成 + +| Provider | 責務 | +| --- | --- | +| `CameraProvider` | カメラの初期化・プレビュー制御・撮影実行・ライフサイクル管理 | +| `GalleryProvider` | 画像一覧の取得・キャッシュ・削除操作 | + +### ディレクトリ構成 + +```text +lib/ +├── main.dart # アプリエントリポイント,Provider 登録 +├── app.dart # MaterialApp 定義,テーマ設定 +├── providers/ +│ ├── camera_provider.dart # カメラ制御の状態管理 +│ └── gallery_provider.dart # 画像一覧の状態管理 +├── screens/ +│ ├── capture_screen.dart # 撮影画面 (S-01) +│ └── gallery_screen.dart # 一覧画面 (S-02) +├── widgets/ +│ ├── camera_preview.dart # カメラプレビューウィジェット +│ ├── shutter_button.dart # シャッターボタン +│ ├── image_grid.dart # サムネイルグリッド +│ └── image_detail_dialog.dart # 拡大表示ダイアログ +└── services/ + ├── file_service.dart # ファイル保存・命名・削除 + └── permission_service.dart # パーミッション管理 +``` + +### レイヤー構成 + +```text +┌─────────────────────────────────┐ +│ Screens(画面) │ UI 層: ウィジェット構築のみ +├─────────────────────────────────┤ +│ Widgets(部品) │ 再利用可能な UI コンポーネント +├─────────────────────────────────┤ +│ Providers(状態管理) │ ビジネスロジック・状態保持 +├─────────────────────────────────┤ +│ Services(サービス) │ 外部リソースとのインタフェース +└─────────────────────────────────┘ +``` + +- **Screens** は Provider を `context.watch` / `context.read` で参照し,直接 Service を呼ばない +- **Providers** は Service を呼び出し,結果を状態として保持する +- **Services** はプラットフォーム API やファイルシステムとの橋渡しを行う + +## 非機能要件 (Non-functional Requirements) + +### パフォーマンス + +- 撮影から保存完了まで 2 秒以内を目標とする +- 一覧画面のサムネイル読み込みは非同期で行い,画面表示をブロックしない + +### データ保全 + +- 撮影データは端末ローカルにのみ保存する(クラウド同期なし) +- PNG(非圧縮)形式で保存し,画質劣化を防ぐ + +### 対応端末 + +- 試験端末: AQUOS sense3(SH-02M,Android 9) +- minSdkVersion: 21(Android 5.0) +- targetSdkVersion: Flutter デフォルト(最新安定版に追従)