# EZOpenSDK Android AI云存储

## 功能概述

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

## 核心类

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

## ⛔ 强制约束

### 1. 必须通过 EZOpenSDK.showAICloudStorage 创建

- **禁止** 直接 `new EZAICloudStorageView(...)` 并手动调用 `show()`（虽然可以工作，但不推荐）
- **推荐** 通过 `getOpenSDK().showAICloudStorage(activity, container, deviceSerial, cameraNo, verifyCode)` 创建，内部会自动调用 `show()`

### 2. 必须实现 OnAICloudStorageCallback 回调

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

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

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

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

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

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

- 回放成功时 **必须** 调用 `updatePlaybackState(true)`
- 回放完成/停止时 **必须** 调用 `updatePlaybackState(false)`
- 不同步状态会导致录像卡片上的播放动画不正确

---

## 集成步骤

### 第一步：布局中添加容器

在回放页面的布局文件中，预留一个 FrameLayout 作为 AI 云存储 UI 的容器：

```xml
<!-- AI云存储内容容器（覆盖在录像列表上方） -->
<FrameLayout
    android:id="@+id/ai_cloud_storage_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:visibility="gone" />
```

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

```java
// 获取容器
FrameLayout container = findViewById(R.id.ai_cloud_storage_container);

// 调用EZOpenSDK的API展示AI云存储
// verifyCode 为设备验证码，可为 null（为 null 时加密封面需用户输入验证码后才加载）
EZAICloudStorageView aiCloudStorageView = getOpenSDK().showAICloudStorage(
        activity, container, deviceSerial, cameraNo, verifyCode);

// 设置回调
aiCloudStorageView.setCallback(callback);
```

### 第三步：实现回调接口

```java
public class EZPlayBackListActivity implements EZAICloudStorageView.OnAICloudStorageCallback {

    @Override
    public void onAICloudRecordFileClick(EZCloudRecordFile recordFile) {
        // 录像片段被点击，开始回放
        mIsAICloudStoragePlayback = true;
        mPlaybackPlayer.stopPlayback();
        mPlaybackPlayer.startPlayback(recordFile);
    }

    @Override
    public void onAICloudStorageViewNeedVerifyCode() {
        // 封面加密，需要验证码
        // 先查本地缓存是否已有该设备的验证码
        String cachedVerifyCode = getCachedVerifyCode(deviceSerial);
        if (!TextUtils.isEmpty(cachedVerifyCode)) {
            mAICloudStorageView.reloadEncryptedCovers(cachedVerifyCode);
            return;
        }
        // 弹出输入框让用户输入验证码
        showVerifyCodeInputDialog(verifyCode -> {
            if (mPlaybackPlayer != null) {
                mPlaybackPlayer.setPlayVerifyCode(verifyCode);
            }
            mAICloudStorageView.reloadEncryptedCovers(verifyCode);
        });
    }
}
```

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

```java
// 隐藏（不销毁，可通过 resume 恢复）
aiCloudStorageView.hide();

// 恢复已隐藏的页面（若之前因接口异常显示错误页，恢复时会重新请求接口）
aiCloudStorageView.resume();

// 判断是否正在展示
boolean isShowing = aiCloudStorageView.isShowing();
```

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

```java
// 播放成功时通知显示播放动画
aiCloudStorageView.updatePlaybackState(true);

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

// 播放完成/停止时重置播放图标
aiCloudStorageView.updatePlaybackState(false);
mIsAICloudStoragePlayback = false;
```

### 第六步：释放资源

```java
@Override
protected void onDestroy() {
    super.onDestroy();
    if (mAICloudStorageView != null) {
        mAICloudStorageView.releaseView();
        mAICloudStorageView = null;
    }
}
```

---

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

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

```java
private void onAICloudStorageBtnClick() {
    if (mAICloudStorageView != null && mAICloudStorageView.isShowing()) {
        // 已展示则隐藏
        mAICloudStorageView.hide();
    } else if (mAICloudStorageView != null) {
        // 已创建过，恢复展示（错误状态会重新请求接口）
        mAICloudStorageView.resume();
    } else {
        // 首次创建，调用EZOpenSDK的API展示AI云存储
        String verifyCode = getCachedVerifyCode(deviceSerial);
        mAICloudStorageView = getOpenSDK().showAICloudStorage(this,
                mAICloudStorageContainer, deviceSerial, cameraNo, verifyCode);
        mAICloudStorageView.setCallback(this);
    }
}
```

---

## API 参考

### EZOpenSDK

| 方法 | 说明 |
|------|------|
| `showAICloudStorage(Activity activity, ViewGroup container, String deviceSerial, int cameraNo, String verifyCode)` | 创建并展示AI云存储视图，返回 `EZAICloudStorageView` 实例 |

### EZAICloudStorageView

| 方法 | 说明 |
|------|------|
| `setCallback(OnAICloudStorageCallback callback)` | 设置回调接口 |
| `show()` | 展示AI云存储UI（判断设备是否支持，不支持则展示升级提示页） |
| `hide()` | 隐藏UI（不销毁，可通过 resume 恢复） |
| `resume()` | 恢复已隐藏的页面（错误状态会重新请求接口） |
| `isShowing()` | 是否正在展示 |
| `updatePlaybackState(boolean isPlaying)` | 更新播放状态（true=播放中显示动画，false=停止显示静态图标） |
| `reloadEncryptedCovers(String verifyCode)` | 设置验证码并刷新加密封面 |
| `hasEncryptedCoversNotLoaded()` | 是否有加密封面尚未加载 |
| `releaseView()` | 释放AI云存储UI资源 |
| `isEncrypt(EZCloudRecordFile recordFile)` | 静态方法，判断云录像封面是否加密 |

### OnAICloudStorageCallback

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

---

## import 参考

```java
import com.videogo.aicloud.EZAICloudStorageView;
import com.videogo.openapi.bean.EZCloudRecordFile;
```

---

## 注意事项

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