# 画面機能仕様書 (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 デフォルト（最新安定版に追従）
