# 画面機能仕様書 (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)

### レイアウト

アプリ全体を `app.dart` の `MaterialApp` ごと `Transform.rotate(angle: pi)` で 180° 回転する．これにより画面・ダイアログ・スナックバー等すべての UI が自動的に回転されるため，個々のウィジェットで回転を意識する必要がない．以下は**コード上の配置順**（回転前）を示す．操作者が逆さに置いた端末を見ると，上下が反転して表示される．

```text
操作者の視点（回転後）            コード上の配置（回転前）
┌─────────────────────┐      ┌─────────────────────┐
│ （黒い空白）          │      │  BottomNavBar        │
│  アタッチメントで隠れる │      │  明るさスライダー      │
├─────────────────────┤      │                     │
│                     │      │  カメラプレビュー      │
│  カメラプレビュー      │      │  （中央クロップ）      │
│  （中央クロップ）      │      │                     │
│                     │      ├─────────────────────┤
│  明るさスライダー      │      │ （黒い空白）          │
│  BottomNavBar        │      │  アタッチメントで隠れる │
└─────────────────────┘      └─────────────────────┘
```

※ アタッチメント（SmTIAS）により画面の約 1/3 が隠れるため，その領域は黒い空白とし，UI 要素は見える領域に配置する．

### UI 要素

| 要素 | 仕様 |
| --- | --- |
| カメラプレビュー | インカメラのライブ映像．鏡像（左右反転）＋上下反転で表示．画面いっぱいに拡大し中央をクロップ |
| シャッターボタン | 丸型ボタン（画面下部中央）．タップで撮影実行．連続撮影可 |
| タイマーボタン | 画面左下に配置．ON（黄色）/ OFF（グレー）を切り替える．ON 時はシャッター押下後 3 秒のカウントダウン表示の後に撮影 |
| 明るさスライダー | ナビバーの直下に配置．画面の輝度を手動で調整する（デフォルト: 0.8） |
| BottomNavigationBar | 撮影タブ（アクティブ）/ 一覧タブ．背景色は黒系で統一 |

### 動作仕様

- **起動時**: カメラの初期化を行い，プレビューを開始する
- **即時撮影**: タイマー OFF 時，シャッターボタンタップで即座に撮影する
- **タイマー撮影**: タイマー ON 時，シャッターボタンタップで 3, 2, 1 のカウントダウン後に撮影する（手ブレ・振動防止用）
- **連続撮影**: 前回の保存完了を待ってから次の撮影が可能
- **保存中表示**: カメラの画像ストリーム（`startImageStream`）から最新フレームを保持し，撮影開始時に Android ネイティブの `YuvImage.compressToJpeg()` で JPEG 変換して表示する（オーバーレイなし・瞬時）
- **フィードバック**: 撮影成功時にスナックバーで「保存しました: [ファイル名]」を表示する
- **エラー時**: カメラ初期化失敗やストレージ書き込み失敗時は，エラーメッセージをスナックバーで表示する

## 一覧画面 (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 ライトはアタッチメント側で制御） |
| 画像フォーマット | Camera2 API で YUV_420_888 生データを取得し，PNG に直接変換して保存（JPEG 非経由） |

### プレビュー表示

- `camera` パッケージの `CameraPreview` ウィジェットを使用する
- UI 全体を `Transform.rotate(angle: pi)` で 180° 回転する
- カメラ映像に `Matrix4.diagonal3Values(-scale, -scale, 1.0)` を適用する
  - X 軸反転: 鏡像表示（操作者が自然に見える向き）
  - Y 軸反転: 端末が逆さのため上下を補正
- プレビューは画面いっぱいに拡大し，はみ出す部分はクロップする（`ClipRect` + スケール計算）

### 撮影フロー

```text
シャッターボタンタップ
    │
    ▼
プレビュー停止（CameraController.dispose()）
    │
    ▼
Camera2 API でフロントカメラを開く（YUV_420_888，フル解像度）
    │
    ▼
AE/AF 安定のため 1 秒間プレビューフレームを流す
    │
    ▼
STILL_CAPTURE リクエストで YUV フレームを 1 枚取得
    │
    ▼
YUV → RGB 変換（BT.601 係数，isolate で実行）
    │
    ▼
90° 時計回り回転（センサー向き補正）
    │
    ▼
PNG エンコード（image パッケージ，ロスレス）
    │
    ▼
ファイル名生成（命名規則に従う）
    │
    ▼
Pictures/MiniTIAS/ に保存
    │
    ▼
MediaStore 通知（MediaScannerConnection でスキャン）
    │
    ▼
Camera2 を閉じ，プレビューを再開
    │
    ▼
スナックバーで保存完了を通知
```

### ライフサイクル管理

- `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)

### 画面輝度

- アプリ起動時に画面の輝度をデフォルト値（0.8）に設定する
- ユーザーが明るさスライダーで任意に調整できる
- アプリ終了時にシステムの輝度設定に復帰する
- アタッチメント装着時に光センサーで画面が暗くなることへの対策

### パフォーマンス

- 撮影から保存完了まで 2 秒以内を目標とする
- 一覧画面のサムネイル読み込みは非同期で行い，画面表示をブロックしない

### データ保全

- 撮影データは端末ローカルにのみ保存する（クラウド同期なし）
- PNG（非圧縮）形式で保存し，画質劣化を防ぐ

### 対応端末

- 試験端末: AQUOS sense3（SH-02M，Android 9）
- minSdkVersion: 21（Android 5.0）
- targetSdkVersion: Flutter デフォルト（最新安定版に追従）
