# EZOpenSDK iOS AI云存储

## 功能概述

AI云存储功能提供基于AI能力的云存储录像智能检索，用户可通过自然语言对话的方式查找云存储中的录像片段，并直接播放。SDK 对外提供 `EZAICloudStorageView` 视图管理器，开发者只需将其嵌入到回放页面的容器中即可完成集成。

## 核心类

| 类名 | 说明 |
|------|------|
| `EZAICloudStorageView` | AI云存储视图管理器，管理AI对话UI、录像卡片展示、播放状态 |
| `EZOpenSDK` | SDK 主入口，提供 `showAICloudStorage:deviceSerial:cameraNo:verifyCode:` 工厂方法 |
| `EZCloudRecordFile` | 云录像文件对象，回调中返回给调用方用于启动回放 |
| `EZAICloudStorageViewDelegate` | AI云存储回调协议 |

## ⛔ 强制约束

### 1. 必须通过 EZOpenSDK 类方法创建

- **禁止** 直接 `[[EZAICloudStorageView alloc] initWithContainerView:...]` 并手动调用 `show`（虽然可以工作，但不推荐）
- **推荐** 通过 `[EZOpenSDK showAICloudStorage:containerView deviceSerial:serial cameraNo:no verifyCode:code]` 创建，内部会自动调用 `show`

### 2. 必须实现 EZAICloudStorageViewDelegate 代理

- **禁止** 不设置 delegate 直接使用（录像点击无法传递给调用方）
- **必须** 实现 `onAICloudRecordFileClick:` 和 `onAICloudStorageViewNeedVerifyCode` 两个代理方法

### 3. 必须在 dealloc 中释放资源

- **必须** 在页面销毁时调用 `releaseView` 释放资源
- **禁止** 仅设置为 nil 而不调用 releaseView

### 4. 验证码回调必须优先查本地缓存

- 收到 `onAICloudStorageViewNeedVerifyCode` 回调时，**必须** 先查本地是否已有验证码
- 有缓存则直接调用 `reloadEncryptedCovers:`
- 无缓存才弹出输入框

### 5. 播放状态必须同步给 AI 云存储视图

- 回放成功时 **必须** 调用 `[mAICloudStorageView updatePlaybackState:YES]`
- 回放完成/停止时 **必须** 调用 `[mAICloudStorageView updatePlaybackState:NO]`
- 不同步状态会导致录像卡片上的播放动画不正确

---

## 集成步骤

### 第一步：添加容器视图

在回放页面的 Storyboard 或代码中，预留一个 UIView 作为 AI 云存储 UI 的容器：

```ObjC
@property (weak, nonatomic) IBOutlet UIView *mAICloudStorageContainer;///< AI云存储UI容器
@property (nonatomic, strong) EZAICloudStorageView *mAICloudStorageView;///< AI云存储视图管理器
@property (nonatomic, assign) BOOL mIsAICloudStoragePlayback;///< 当前播放是否由AI云存储触发
```

### 第二步：创建并展示AI云存储

```ObjC
#import "EZAICloudStorageView.h"

// 调用EZOpenSDK的API展示AI云存储
// verifyCode 为设备验证码，可为 nil（为 nil 时加密封面需用户输入验证码后才加载）
self.mAICloudStorageView = [EZOpenSDK showAICloudStorage:self.mAICloudStorageContainer
                                            deviceSerial:deviceSerial
                                                cameraNo:cameraNo
                                              verifyCode:verifyCode];

// 设置代理
self.mAICloudStorageView.delegate = self;
```

### 第三步：实现代理协议

```ObjC
@interface EZPlaybackViewController ()<EZAICloudStorageViewDelegate>
@end

@implementation EZPlaybackViewController

#pragma mark - EZAICloudStorageViewDelegate

- (void)onAICloudRecordFileClick:(EZCloudRecordFile *)recordFile {
    // 录像片段被点击，开始回放
    self.mIsAICloudStoragePlayback = YES;
    [_player stopPlayback];
    [_player startPlaybackFromCloud:recordFile];
}

- (void)onAICloudStorageViewNeedVerifyCode {
    // 封面加密，需要验证码
    // 如已有验证码，直接使用
    if (self.verifyCode.length > 0) {
        [self.mAICloudStorageView reloadEncryptedCovers:self.verifyCode];
        return;
    }
    // 否则弹出验证码输入框
    [self showVerifyCodeInputDialog:^(NSString *code) {
        self.verifyCode = code;
        [self.player setPlayVerifyCode:code];
        [self.mAICloudStorageView reloadEncryptedCovers:code];
    }];
}

@end
```

### 第四步：显示/隐藏控制

```ObjC
// 隐藏（不销毁，可通过 resume 恢复）
[self.mAICloudStorageView hide];

// 恢复已隐藏的页面（不重新请求接口，不重建View）
[self.mAICloudStorageView resume];

// 判断是否正在展示
BOOL isShowing = self.mAICloudStorageView.isShowing;
```

### 第五步：更新播放状态

```ObjC
// 播放成功时通知显示播放动画
[self.mAICloudStorageView updatePlaybackState:YES];

// 如果有加密封面尚未加载，此时验证码已获取，刷新封面
if ([self.mAICloudStorageView hasEncryptedCoversNotLoaded]) {
    [self.mAICloudStorageView reloadEncryptedCovers:self.verifyCode];
}

// 播放完成/停止时重置播放图标
[self.mAICloudStorageView updatePlaybackState:NO];
self.mIsAICloudStoragePlayback = NO;
```

### 第六步：释放资源

```ObjC
- (void)dealloc {
    [self.mAICloudStorageView releaseView];
}
```

---

## 按钮点击的标准处理逻辑

AI云存储按钮点击时的三种状态处理：

```ObjC
- (void)aiCloudStorageBtnClicked {
    if (self.mAICloudStorageView && self.mAICloudStorageView.isShowing) {
        // 已展示则隐藏
        [self.mAICloudStorageView hide];
    } else if (self.mAICloudStorageView) {
        // 已创建过，恢复展示（错误状态会重新请求接口）
        [self.mAICloudStorageView resume];
    } else {
        // 首次创建，调用EZOpenSDK的API展示AI云存储
        self.mAICloudStorageView = [EZOpenSDK showAICloudStorage:self.mAICloudStorageContainer
                                                    deviceSerial:self.cameraInfo.deviceSerial
                                                        cameraNo:self.cameraInfo.cameraNo
                                                      verifyCode:self.verifyCode];
        self.mAICloudStorageView.delegate = self;
    }
}
```

---

## API 参考

### EZOpenSDK

| 方法 | 说明 |
|------|------|
| `+[EZOpenSDK showAICloudStorage:deviceSerial:cameraNo:verifyCode:]` | 创建并展示AI云存储视图，返回 `EZAICloudStorageView` 实例 |

### EZAICloudStorageView

| 属性/方法 | 说明 |
|-----------|------|
| `delegate` | 回调代理（weak） |
| `isShowing` | 是否正在展示（只读属性） |
| `show` | 展示AI云存储UI（判断设备是否支持，不支持则展示升级提示页） |
| `hide` | 隐藏UI（不销毁，可通过 resume 恢复） |
| `resume` | 恢复已隐藏的页面（不重新请求接口，不重建View） |
| `releaseView` | 释放AI云存储UI资源 |
| `updatePlaybackState:` | 更新播放状态（YES=播放中显示动画，NO=停止显示静态图标） |
| `reloadEncryptedCovers:` | 设置验证码并刷新加密封面 |
| `hasEncryptedCoversNotLoaded` | 是否有加密封面尚未加载 |

### EZAICloudStorageViewDelegate

| 代理方法 | 说明 |
|----------|------|
| `onAICloudRecordFileClick:` | 录像片段被点击，调用方应启动录像回放 |
| `onAICloudStorageViewNeedVerifyCode` | 加密封面需要验证码，调用方应弹出输入框并调用 `reloadEncryptedCovers:` |

---

## import 参考

```ObjC
#import "EZAICloudStorageView.h"
#import "EZCloudRecordFile.h"
```

---

## 注意事项

1. `showAICloudStorage:deviceSerial:cameraNo:verifyCode:` 方法调用后会自动判断设备是否支持AI云存储，支持则展示对话页面，不支持则展示升级提示页。开发者无需自行判断设备是否支持。
2. `verifyCode` 参数用于加载加密封面。若设备未加密或暂时没有验证码，可传 `nil`。后续可通过 `reloadEncryptedCovers:` 补充。
3. AI云存储视图内部会管理自身UI（聊天对话、录像卡片等），调用方只需处理录像点击播放和验证码输入两个代理回调。
4. 调用 `hide` 不会销毁视图，下次通过 `resume` 可恢复展示，避免重复创建和请求接口。
5. 页面销毁时必须调用 `releaseView` 释放资源。
6. AI云存储触发的回放使用 `EZCloudRecordFile` 对象，与普通云存储录像回放流程一致，直接传给 `[player startPlaybackFromCloud:cloudFile]` 即可。
