# EZOpenSDK Android 基础集成

## 〇、工程版本配置（重要）

创建新工程时，必须先检测用户本地的开发环境（Gradle 版本、JDK 版本），然后选择匹配的 AGP 版本。不要硬编码版本号。

### 检测方法
```bash
# 检测 Gradle 版本
ls ~/.gradle/wrapper/dists/

# 检测 JDK 版本（Android Studio 内置）
/Applications/Android\ Studio.app/Contents/jbr/Contents/Home/bin/java -version  # macOS
```

### 版本兼容矩阵

| Gradle 版本 | 最低 AGP 版本 | 推荐 AGP 版本 | 最低 JDK |
|-------------|-------------|-------------|---------|
| 8.0 - 8.3   | 8.1.0       | 8.1.4       | JDK 17  |
| 8.4 - 8.6   | 8.2.0       | 8.3.2       | JDK 17  |
| 8.7 - 8.9   | 8.4.0       | 8.5.2       | JDK 17  |
| 8.10 - 8.11 | 8.6.0       | 8.7.3       | JDK 21  |
| 9.0+        | 8.8.0       | 8.8.0       | JDK 21  |

### 推荐的 settings.gradle 模板
```groovy
pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal()
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "YourProjectName"
include ':app'
```

### 推荐的根目录 build.gradle 模板
```groovy
plugins {
    // 版本号根据上方兼容矩阵选择
    id 'com.android.application' version '8.7.3' apply false
}
```

### 推荐的 gradle-wrapper.properties 模板
```properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# 版本号应与用户本地已有的 Gradle 版本一致
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
```

### 创建工程的正确流程
1. 先执行检测命令，获取用户本地 Gradle 和 JDK 版本
2. 根据兼容矩阵选择 AGP 版本
3. gradle-wrapper.properties 中的 Gradle 版本与用户本地保持一致
4. 再生成工程文件

## 一、Gradle 安装

### 1. 项目根目录 build.gradle
已在上方模板中给出，使用 plugins DSL 风格。

### 2. app 模块 build.gradle
```groovy
android {
    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
    defaultConfig {
        ndk {
            abiFilters "armeabi-v7a", "arm64-v8a"
        }
    }
}

dependencies {
    // 萤石SDK核心模块，必须依赖
    implementation 'io.github.ezviz-open:ezviz-sdk:5.28.3'
    // 4.19.0版本之后需要自行依赖
    implementation 'com.squareup.okhttp3:okhttp:3.12.1'
    implementation 'com.google.code.gson:gson:2.8.5'
}
```

### 3. 混淆配置（proguard-rules.pro）
```proguard
# SDK对外接口
-keep class com.ezviz.opensdk.** { *;}

# 二方库
-dontwarn com.ezviz.**
-keep class com.ezviz.** { *;}
-dontwarn com.ez.**
-keep class com.ez.** { *;}
-dontwarn com.hc.CASClient.**
-keep class com.hc.CASClient.** { *;}
-dontwarn com.videogo.**
-keep class com.videogo.** { *;}
-dontwarn com.hik.TTSClient.**
-keep class com.hik.TTSClient.** { *;}
-dontwarn com.hik.stunclient.**
-keep class com.hik.stunclient.** { *;}
-dontwarn com.hik.streamclient.**
-keep class com.hik.streamclient.** { *;}
-dontwarn com.hikvision.sadp.**
-keep class com.hikvision.sadp.** { *;}
-dontwarn com.hikvision.netsdk.**
-keep class com.hikvision.netsdk.** { *;}
-dontwarn com.neutral.netsdk.**
-keep class com.neutral.netsdk.** { *;}
-dontwarn com.hikvision.audio.**
-keep class com.hikvision.audio.** { *;}
-dontwarn com.mediaplayer.audio.**
-keep class com.mediaplayer.audio.** { *;}
-dontwarn com.hikvision.wifi.**
-keep class com.hikvision.wifi.** { *;}
-dontwarn com.hikvision.keyprotect.**
-keep class com.hikvision.keyprotect.** { *;}
-dontwarn org.MediaPlayer.PlayM4.**
-keep class org.MediaPlayer.PlayM4.** { *;}

# 第三方库
-dontwarn com.sun.jna.**
-keep class com.sun.jna.** { *;}
-keepattributes *Annotation*
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
-dontwarn javax.annotation.**
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
-dontwarn org.codehaus.mojo.animal_sniffer.*
-dontwarn okhttp3.internal.platform.ConscryptPlatform
-dontwarn okio.**
```

## 二、权限配置（AndroidManifest.xml）

### Activity 注册
```xml
<activity
    android:name="com.videogo.main.EzvizWebViewActivity"
    android:screenOrientation="portrait"
    android:configChanges="orientation|keyboardHidden">
</activity>
```

### 权限声明
```xml
<!-- 基础功能 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 配网 -->
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<!-- 对讲 -->
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 视频通话 -->
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.BLUETOOTH" />
```

## 三、SDK 初始化

### 国内客户
```java
// 基线平台（不需要设置域名）
EZOpenSDK.initLib(application, "Your AppKey");

// 私有云平台（额外设置域名）
EzvizApplication.getOpenSDK().setServerUrl("Your apiUrl", "Your authUrl");
```

### 海外客户
```java
EZGlobalSDK.initLib(application, "Your AppKey");
EzvizApplication.getOpenSDK().setServerUrl("Your apiUrl", null);
```

## 四、SDK 授权

SDK 授权分两种模式，二选一。

### 模式一：AccessToken 授权（推荐）
```java
// 设置 accessToken
EzvizApplication.getOpenSDK().setAccessToken("Your accessToken");
```

验证 accessToken 有效性：
```java
private boolean checkAppKeyAndAccessToken() {
    boolean isValid = false;
    try {
        EzvizApplication.getOpenSDK().getDeviceList(0, 1);
        isValid = true;
    } catch (BaseException e) {
        e.printStackTrace();
        Log.e(TAG, "Error code is " + e.getErrorCode());
    }
    return isValid;
}
```

### 模式二：小权限 TKToken 授权
此模式默认关闭，需在 initLib 之前开启：
```java
EZOpenSDK.enableSDKWithTKToken(true);
EZOpenSDK.initLib(application, "Your AppKey");
```

授权流程：
```java
EzvizApplication.getOpenSDK().setHttpToken("Your httpTKToken");
```

TKToken 类型说明：
| token 类型 | 说明 | 使用场景 |
|-----------|------|----------|
| httpToken | 非设备类小权限 token | 入参不含设备序列号的 API |
| deviceToken | 设备级小权限 token | 入参含设备序列号、不含通道号的 API |
| deviceGlobalToken | 通道级 global token | SDK 内部封装接口，取流必须 |
| deviceVideoToken | 通道级 video token | 入参含设备序列号和通道号的 API |
| previewStreamToken | 预览 token | startRealPlay 之前设置 |
| playbackStreamToken | 回放 token | startPlayback(EZDeviceRecordFile) 之前设置 |
| talkStreamToken | 对讲 token | startVoiceTalk 之前设置 |

设置方法：
```java
EzvizApplication.getOpenSDK().setHttpToken(String httpTKToken);
EzvizApplication.getOpenSDK().setDeviceToken(String deviceSerial, String deviceToken);
EzvizApplication.getOpenSDK().setDeviceToken(String deviceSerial, int cameraNo, String deviceGlobalToken, String deviceVideoToken);
EZPlayer.setStreamToken(String streamToken);
```

注意事项：
- IPC 设备对讲使用 0 通道，对讲 streamToken 生成请使用 0 通道
- 双目设备取流使用交织流 0 通道，取流 streamToken 生成请使用 0 通道，deviceGlobalToken 和 deviceVideoToken 保持使用 1 通道

## 五、SDK 账号切换
```java
// 第一步：退出前一个账号
EZOpenSDK.logout();
// 第二步：重新初始化
EZOpenSDK.initLib(application, "Your new AppKey");
// 第三步：重新授权
EzvizApplication.getOpenSDK().setAccessToken("Your new accessToken");
```
必须按顺序完成，否则会有异常。

## 六、SDK 调试模式

应用发布时务必关闭，否则沙盒中会产生大量码流文件。

```java
// 日志开关（建议 debug 打开，release 关闭），在初始化之前调用
EZOpenSDK.showSDKLog(boolean showLog);

// 码流缓存开关（release 必须关闭），在初始化之前调用
EZOpenSDK.setDebugStreamEnable(boolean enable);

// 日志回调（可选）
EZOpenSDK.getInstance().setLogCallback(new EZOpenSDKListener.LogCallback() {
    @Override
    public void onLog(String tag, String msg) {
        // 自定义日志处理
    }
});
```

码流文件路径：
- 视频码流：`/sdcard/Android/data/应用包名/files/streams`
- 对讲码流：`/sdcard/Android/data/应用包名/files/talkback`

## 七、登录页面与中间页

SDK 提供了内置的登录页面和功能页面：
```java
// 打开登录页面
EZOpenSDK.getInstance().openLoginPage();

// 打开云存储页面
EZOpenSDK.getInstance().openCloudPage(String deviceSerial, int cameraNo);

// 打开修改密码页面
EZOpenSDK.getInstance().openChangePasswordPage();
```

## 八、获取 SDK 版本号
```java
String version = EZOpenSDK.getVersion();
```

## 九、各大区域 ApiUrl 和 AuthUrl

| 区域 | ApiUrl | AuthUrl |
|------|--------|---------|
| 国内 | https://open.ys7.com | https://openauth.ys7.com |
| 海外 | https://open.ezvizlife.com | https://openauth.ezvizlife.com |
| 俄罗斯 | https://irusopen.ezvizru.com | https://irusopenauth.ezvizru.com |
| 亚洲 | https://isgpopen.ezvizlife.com | https://isgpopenauth.ezvizlife.com |
| 北美洲 | https://iusopen.ezvizlife.com | https://iusopenauth.ezvizlife.com |
| 南美洲 | https://isaopen.ezvizlife.com | https://isaopenauth.ezvizlife.com |
| 欧洲 | https://ieuopen.ezvizlife.com | https://ieuopenauth.ezvizlife.com |

## 十、Demo 使用指南

Demo 登录页面需要填写：
| 输入项 | 必填 | 说明 |
|--------|------|------|
| 服务器区域 | 是 | 国内选 Asia-China，海外选对应区域 |
| AppKey | 是 | 从开发者网站获取 |
| AccessToken | 是 | 从开发者网站获取 |
| 指定设备 | 否 | 输入序列号可过滤设备列表 |

Demo 文件路径（Android）：
- 调试日志：`/sdcard/Android/data/ezviz.ezopensdk/files/0_OpenSDK`
- 录像下载：`/sdcard/Android/data/ezviz.ezopensdk/cache/0_OpenSDK/Records`
- 崩溃记录：`/sdcard/Android/data/ezviz.ezopensdk/files/0_OpenSDK/crash.txt`
