APP Android SDK

介绍

iFLYOS 提供了开源的 APP Android SDK 用于执行对设备的授权及部分远程控制操作。模块中包含了账号模块、绑定模块、接口模块、声波配网及 WebViewBridge 模块。

更新日志

日期 更新内容
20190128 增加直接设置第三方 Token 的方式
20190225 更新媒体相关接口
1.内容发现相关接口修改
2. 我的收藏相关接口修改
3. 控制设备播放接口修改
20190308 更新酷狗绑定相关内容,补充部分接口
20190701 1. 开源配网相关代码,精简 AAR 体积
2. 新增部分接口
3. 开源蓝牙配网文档

快速接入

接入要求

设备系统版本 Android 5.1 或以上
Android Studio 3.1.2 或以上

导入依赖

这里下载最新的 SDK demo,将 aar 文件导入到应用模块中作为 libs 引用,build.gradle 中声明

dependencies {
    implementation(name: 'iflyos-app-sdk', ext: 'aar')

    // SDK 依赖项
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
    implementation 'com.google.code.gson:gson:2.8.5'
}

声明权限

SDK 模块需要在AndroidManifest.xml中声明以下权限

<uses-permission android:name="android.permission.INTERNET" />

混淆规则

在工程的 proguard-project.txt 里添加以下相关规则

-keep class com.iflytek.home.sdk.**{*;}
-keep public class com.iflytek.home.app.R$*{
  public static final int *;
}
-keep class cn.iflyos.open.library.**{*;}
-keepattributes *Annotation*
-keep class com.squareup.okhttp3.**{*;}
-dontwarn okio.**
-keep interface com.squareup.okhttp3.**{*;}
-dontwarn com.squareup.okhttp3.**

初始化

在你的应用入口处调用

IFlyHome.init(context, appId, loginWay)

此处为 Kotlin 实现,如果使用 Java 调用,请使用 IFlyHome.INSTANTCE,下文同。

参数 说明 类型
context 上下文对象 Context
appId 用于 SDK 的 app id,请参阅申请方式 String
loginWay 声明 SDK 使用的登录方式。
STANDARD 代表默认的登录方式,通过调用打开网页来登录。
CUSTOM_TOKEN 表示通过设置第三方 Token 的方式,如何使用请联系支持邮箱
enum

WebViewBridge

SDK 中大部分带 UI 交互页面均为 Web 页面,你可以通过调用接口注册你的 WebView 控件,SDK 会为其绑定 WebViewBridge 及相关定制。

注册 WebView

简单地,你可以通过调用以下方法对 WebView 进行注册

val webViewTag = IFlyHome.register(webView, object: IFlyHomeCallback() {
    // 表示网页顶栏主题色更新
    override fun updateHeaderColor(color: String) {}

    // 表示网页标题更新
    override fun updateTitle(title: String) {}

    // 表示网页请求在新 Activity 中打开指定页面
    override fun openNewPage(tag: String) {}

    // 表示网页请求关闭页面
    override fun closePage() {}

    override fun getWebViewClient(): WebViewClient? {}

    override fun getWebChromeClient(): WebChromeClient? {}
})

如果你需要自定义 WebViewClient 或者 WebChromeClient,为了不影响 SDK 对 WebView 的绑定,请在 IFlyHomeCallback 中作为返回值即可,此方法并不要求必须实现。

返回结果中的 webViewTag 可以让你在打开 IFLYOS 网页时传入,告诉 SDK 你想在哪个 WebView 中打开页面。

如果 SDK 通过 openNewPage(tag) 传递了参数 tag,则应当使用以下方式注册 WebView,SDK 将在 WebView 绑定完成后自动跳转到指定的访问页面。

IFlyHome.register(webView, iFlyHomeCallback, tag)

打开页面

你可以通过以下方法打开 IFLYOS 提供的指定页面

val result = IFlyHome.openWebPage(webViewTag, pageIndex, params)
// webViewTag 在注册 WebView 时返回
// pageIndex 可用参数:
//   IFlyHome.ACCOUNTS,
//   IFlyHome.CLOCKS,
//   IFlyHome.CONTROLLED_DEVICES,
//   IFlyHome.SKILLS,
//   IFlyHome.PERSONAL_SOUNDS
//   IFlyHome.WAKEUP_WORDS
//   IFlyHome.TIME_TO_SLEEP
//   IFlyHome.SPEAKER
//   IFlyHome.BLUETOOTH
//   IFlyHome.CHECK_UPDATE
// params 可为空,当 pageIndex 为以下参数时,params 中需要传入 deviceId 参数:
//   IFlyHome.WAKEUP_WORDS
//   IFlyHome.SPEAKER
//   IFlyHome.BLUETOOTH
//   IFlyHome.TIME_TO_SLEEP
//   IFlyHome.CHECK_UPDATE

参数说明

字段 类型 必须出现 说明
webViewTag String 在注册 WebView 时返回的标识
pageIndex String 含义参考 IFLYOS 提供页面
params Map<String, String> 请求参数

result 含义参考 状态码。 当 pageIndex 为以下参数时, params 中需要传入 deviceId 参数:

  • IFlyHome.WAKEUP_WORDS
  • IFlyHome.SPEAKER
  • IFlyHome.BLUETOOTH
  • IFlyHome.TIME_TO_SLEEP
  • IFlyHome.CHECK_UPDATE

生命周期

当 WebView 可见时应调用

IFlyHome.resumeWebView(webViewTag)

当 WebView 不可见时应调用

IFlyHome.pauseWebView(webViewTag)

账号模块

在调用登录后,SDK 内部维护了调用各个接口及打开各种网页所需要的登录状态。

登录

目前支持两种登录方式。根据初始化 SDK 时传入的 loginWay,应当选择不同的登录方式。

STANDARD

val result = IFlyHome.openLogin(object : IFlyHomeLoginCallback {
    // 登录失败回调
    override fun onLoginFailed(type: Int, error: Throwable?) {}

    // 登录成功回调
    override fun onLoginSuccess() {}

    // SDK 期望在新 Activity 中打开登录页
    // 但如果你在当前页内实现了 WebView 控件,则在此回调中使用 tag 注册 WebView 即可
    override fun openNewPage(tag: String): Boolean {}
})

CUSTOM_TOKEN

val result = IFlyHome.setCustomToken(customToken)

登录失败的参数 type 含义可查阅登录错误状态码。如果没有根据你选用的 loginWay 调用对应的登录方式,则接口会返回 RESULT_ILLEGAL_PARAMS

注销

使用以下方式可以注销 SDK 登录状态

val result = IFlyHome.logout(object : IFlyHomeLogoutCallback {
    override fun onLogoutSuccess() {}

    override fun onLogoutFailed(t: Throwable?) {}
})

绑定模块

设备请求认证可以生成一个授权 URL,你需要将这个 URL 调用以下方法打开认证

val result = IFlyHome.openAuthorizePage(webView, url, object: AuthResultCallback {
    override fun onSuccess() {
        // 授权成功
    }

    override fun onFailed(message: String) {
        // 授权失败
    }
})

回调参数可不传,若用户打开授权页面后时直接关闭网页,则不会调用任何回调。

注意:允许授权后,视网络情况而定,设备也许不能立即通过认证并连接到 IVS,此时关闭授权成功页面可能需要过一段时间才能从用户绑定设备列表接口获取到该设备

接口模块

SDK 提供了以下可供调用的接口,所有的接口都是异步请求,在回调中返回响应体。

接口的响应内容格式各有异同,但如果请求出错,都会以下述格式返回错误信息,你可以用以下信息用于做相关提示。

{
    "code": "code", // 错误码
    "message": "message内容" // 错误信息描述
}

由于接口数量过多,建议在顶部的目录概述中先找到所需的接口名称,点击跳转到指定位置。

获取用户已绑定设备列表

val result = IFlyHome.getUserDevices(callback)

参数说明

callback 为网络请求的回调接口,包含以下回调,后不赘述

interface ResponseCallback {
    fun onResponse(response: Response<String>)

    fun onFailure(call: Call<String>, t: Throwable)
}

响应内容

{
    "user_devices": [
        {
            "name": "{Device name}",
            "image": "{Device image url}",
            "device_id": "{Device id}",
            "client_id": "{Client id}",
            "brand": {
                "name": "{Brand name}",
                "logo": "{Brand logo url}",
                "id": 1
            }
        }
    ]
}

取消绑定设备

val result = IFlyHome.deleteUserDevice(deviceId, callback)

响应内容

{
    "device_id": "..."
}

device_id 若与传入参数一致则表示取消绑定成功。传参不合法则可能返回 400 状态码。

获取音频内容列表

val result = getMusicGroups(deviceId, callback)

参数说明

字段 类型 必须出现 说明
deviceId String 设备 ID

响应内容

{
    "groups": [
        {
            "type": "charts",
            "has_more": true, // 表示此 group 是否除了 items 之外还有更多榜单/歌单
            "name": "热门榜单",
            "section": "1",
            "items": [
                {
                    "name": "咪咕热销榜",
                    "image": "https://ss1.baidu.com/6ONXsjip0QIZ8tyhnq/it/u=3526416775,3196812106&fm=58&bpow=512&bpoh=512",
                    "id": "10002",
                    "description": "热销音乐",
                    "source_type": "migu"

                }, ...
            ],
            "descriptions": [
                "嘿,小飞,最近有什么新歌", ...
            ]
        }, ...
    ],
    "banners": [
        {
            "image": "http://p1.music.126.net/HJNFDYEAfvQlrQR_9q33zw==/109951163315205415.jpg"
        }, ...
    ]
}

获取内容分组全部列表

val requestResult = IFlyHome.getGroupSection(sectionId, callback)

参数说明

字段 类型 必须出现 说明
sectionId String 分组 ID,在此接口获取的 groupsitemsectionId

响应内容

{
    "section_id": "2",
    "name": "热门节目",
    "items": [
        {
            "name": "抗癌:第一时间的抉择",
            "description": null,
            "image": "https://ttfm2018pub-oss-cdn.tingtingfm.com/cover/2018/0911/4d/db/4ddbe486d0dd4d14ab58430059a1474a.jpg?x-oss-process=image/resize,h_800,w_800",
            "id": "29"
        },
        {
            "name": "男人战争",
            "description": null,
            "image": "https://ttfm2018pub-oss-cdn.tingtingfm.com/cover/2018/0919/67/c5/67c56824b1708d1190dc7b6bf913c071.jpg?x-oss-process=image/resize,h_800,w_800",
            "id": "46"
        },
        {
            "name": "思维风暴:让你的大脑动起来",
            "description": null,
            "image": "https://bookpic.lrts.me/8e8c8b793de141159c3e575c190ec60b_180x254.jpg",
            "id": "50"
        },
        ...
    ],
    "descriptions": [
        "嘿,小飞,最近有什么歌"
    ]
}

获取指定歌单/榜单下的歌曲列表

val result = IFlyHome.getSongs(groupId, page, limit, callback)

参数说明

字段 类型 必须出现 说明
groupId String 歌单/榜单 ID
page Int 页码
limit Int 单页条目数量

响应内容

{
    "songs": [
        {
            "name": "我们(电影《后来的我们》主题曲)",
            "id": "c229e159dd60591d776ff08afbb66a83",
            "artist": "陈奕迅",
            "liked": true,
            "available": true
        }, ...
    ],
    "name": "咪咕影视榜",
    "image": "http://p1.music.126.net/HJNFDYEAfvQlrQR_9q33zw==/109951163315205415.jpg"
}

搜索音乐

val result = IFlyHome.searchMusic(deviceId, keyword, page, limit, callback)

参数说明

字段 类型 必须出现 说明
deviceId String 设备 ID
keyword String 搜索关键字

响应内容

{
    "total": 3,
    "page": 1,
    "limit": 10,
    "results": [
        {
            "name": "我们(电影《后来的我们》主题曲)",
            "id": "c229e159dd60591d776ff08afbb66a83",
            "artist": "陈奕迅"
        }, ...
    ]
}

获取当前支持信源

val result = IFlyHome.getCollectionSource(callback)

响应内容

{
    "sources": [
        {
            "name": "酷狗音乐",
            "icon": "https://..",
            "source_type": "kugou",
            "user_collections_count": 10
        },
        {
            "name": "咪咕音乐",
            "icon": "https://..",
            "source_type": "migu",
            "user_collections_count": 20
        }
    ]
}

获取指定信源下,用户已收藏音乐列表

val result = IFlyHome.getCollectionFromSource(deviceId, sourceType, callback)

参数说明

字段 类型 必须出现 说明
deviceId String 设备 ID,若不传,则所有收藏中的歌曲都无法播放,且字段中 availablefalseerror_reason 中会携带错误原因
sourceType String 信源类型,来自该接口的响应

响应内容

{
    "total": 7,
    "collections": [
        {
            "name": "我们的爱没有错(《泡沫之夏》电视剧主题曲)",
            "id": "02a24b7e00d53c879fd10a783c16f7ca",
            "artist": "胡夏",
            "available": true,
            "error_reason": ""
        }, ...
    ]
}

收藏音乐

val result = IFlyHome.likeMusic(mediaId, sourceType, callback)

参数说明

字段 类型 必须出现 说明
mediaId String 音乐 ID
sourceType String 信源类型,来自该接口的响应

响应内容

{
    "message": "收藏成功",
    "code": "0000"
}

若收藏出错,message 字段会有相关错误提示。后同。

取消收藏音乐

val result = IFlyHome.unlike(mediaId, sourceType, callback)

获取设备媒体控制状态

val result = IFlyHome.getMusicControlState(deviceId, callback)

响应内容

{
    "speaker": {
        "volume": 50,
        "mute": false
    },
    "music_player": {
        "playing": true
    },
    "music": {
        "srouce": "咪咕音乐",
        "name": "告白气球",
        "image": "http://open.migu.cn:8100/material/pics/album/l/2017/04/28/1611190117569581.jpg?m",
        "id": "236945c65a1493344ae190d2cfe80217",
        "liked": false,
        "artist": "周杰伦",
        "source_icon": "https://...",
        "source_description": "由 咪咕音乐 提供"
    }
}

控制设备播放音乐

val result = IFlyHome.musicControlPlay(deviceId, mediaId, sourceType, callback)

控制设备播放用户收藏音乐列表

val result = IFlyHome.musicControlPlayCollections(deviceId, mediaId,  sourceType, callback)

参数说明

字段 类型 必须出现 说明
mediaId String 音乐 ID,表示从列表中的该音乐开始播放,若列表中找不到该 mediaId 或传 null 则默认第一首开始播放

控制设备播放榜单/歌单

val result = IFlyHome.musicControlPlayGroup(deviceId, mediaId, groupId, callback)

参数说明

字段 类型 必须出现 说明
mediaId String 音乐 ID,表示从列表中的该音乐开始播放,若列表中找不到该 mediaId 或传 null 则默认第一首开始播放

控制设备停止播放

val result = IFlyHome.musicControlStop(deviceId, callback)

控制设备继续播放

val result = IFlyHome.musicControlResume(deviceId, callback)

控制设备播放上一首

val result = IFlyHome.musicControlPrevious(deviceId, callback)

控制设备播放下一首

val result = IFlyHome.musicControlNext(deviceId, callback)

控制设备音量

val result = IFlyHome.musicControlSetVolume(deviceId, volume, callback)

参数说明

字段 类型 必须出现 说明
volume Int 音量值 0~100

获取设备详情信息

val result = IFlyHome.getDeviceDetail(deviceId, callback)

获取当前登录用户资料

val result = IFlyHome.getUserInfo(callback)

获取当前授权 Token

若未登录时调用此接口,则会返回 null

val token = IFlyHome.getToken()

响应说明

名称 类型 说明
alias string 设备别名,没有设置时为null
sleep string 定时休眠配置,关闭时为null
sleep_config.enable boolean 定时休眠配置,是否开启
sleep_config.start boolean 定时休眠开始时间
sleep_config.end boolean 定时休眠结束时间
bluetooth_enable boolean 蓝牙是否开启
speaker string 发音人名称
wakeword string 唤醒词
new_version boolean 是否有新版本
infrared object 设备红外控制参数,字段不存在时红外配置
infrared.client_id object 红外控制设备client_id
infrared.logo object 红外控制设备logo
reboot boolean 设备重启,默认为true
reset_network boolean 重新配网,默认为true
restore_factory boolean 恢复出厂默认为true
music_access object 音乐使用权限
music_access.name string 显示名称
music_access.value string 显示值
music_access.redirect_url string 跳转地址
status string 设备在离线状态
children_mode boolean 儿童模式是否开启

重置设备网络

val result = IFlyHome.resetNetwork(deviceId, callback)

重启设备

val result = IFlyHome.rebootDevice(deviceId, callback)

设备恢复出厂

val result = IFlyHome.restoreFactory(deviceId, callback)

设置设备别名

val result = IFlyHome.updateDeviceAlias(deviceId, alias, callback)

alias 的长度限制为 20.

为设备添加红外遥控

val result = IFlyHome.infraredDevicesAdd(deviceId, extends, callback)

参数说明

字段 类型 必须出现 说明
deviceId String 支持红外遥控的设备 id
extends JSONObject 红外码相关数据(设备厂商定义规则)

红外遥控设备发送红外码

val result = IFlyHome.infraredDevicesAccept(deviceId, extends, callback)

调用接口后,推送给设备一个发送红外码的指令。参数定义参考为设备添加红外遥控的参数说明

获取当前账号可绑定的设备信息

val result = IFlyHome.getClientInfos(callback)

相应说明

返回数据为列表,列表中每项数据含义如下表

字段 类型 必须出现 说明
client_image String 设备图片 url
client_name String 设备名称
client_id String 设备 clientId

获取指定 clientId 设备的设备信息

val result = IFlyHome.getClientInfo(clientId, callback)

设置设备是否开启儿童模式

val result = IFlyHome.updateDeviceInfo(deviceId, alias, true, callback)

参数说明

字段 类型 必须出现 说明
deviceId String 要更改儿童模式的设备 id
alias String 设备别名
isChildrenMode Boolean 是否开启儿童模式

配网模块(已开源)

扫码配网模块

在 SDK Demo 中,使用 zxing 来执行扫码的操作,然后对扫码得到的数据进行访问。

扫码得到的数据是一个进行授权的 URL,通过 SDK 的接口来访问这个 URL,可以打开授权页面。

IFlyHome.openAuthorizaPage(webView, url) // 此处的 WebView 应当是调用过 IFlyHome.register 注册过的 WebView

声波配网模块

SDK Demo 中提供了一个通用的、开源的声波播放器。下面介绍如果使用这部分开源代码。

初始化

val player = SoundWavePlayer(context)

播放

player.prepare(true, ssid, password, object : SoundWavePlayer.PrepareListener {
    override fun onPrepared(player: SoundWavePlayer) {
        player.start()
    }
})

参数说明

字段 类型 必须出现 说明
reAuth Boolean 是否需要重新授权
ssid String WiFi 名称
password String WiFi 密码,为空时视为无密码 WiFi 网络
PrepareListener Function 播放器设置回调

注意:需要在 PrepareListener 回调时再调用播放器的 play

停止

player.stop()

蓝牙配网模块

蓝牙配网设备端的逻辑可参考此处描述声明

当设备端进入配网模式后,App 可以搜索到指定的标识设备的 BLE 广播。

注意

在 Android 上搜索 BLE 设备,需要获取相关的 位置权限。这部分逻辑可以参考 Demo 中的源码,但我们建议你实现自己的权限申请。

搜索 BLE 设备

参照 BleScanActivity 中,搜索 BLE 设备的相关逻辑。

// 绑定 BLE 服务后,开始扫描
bleService.startScan()

// Demo 包名内的 BleDeviceStorage 会存储扫描到的设备及相关的设备信息
BleDeviceStorage.addCallback(callback)

// callback 中要实现
override fun onDeviceScanned(deviceInfos: HashSet<BleDeviceStorage.DeviceInfo>) {
    // 据此可以得到当前扫描到的所有设备,并做相关的显示
    deviceInfos.map{ deviceInfo -> 
        val device: BluetoothDevice = deviceInfo.device // 搜索到的 Ble 设备
        val clientId: String = deviceInfo.clientId      // 该 Ble 设备的 client id

        // 获取可用于 UI 显示的设备信息
        BleDeviceStorage.getClientInfo(clientId) { clientInfo ->
            val clientImage = clientInfo.clientImage    // 设备图片 url
            val clientName = clientInfo.clientName      // 设备名称
            val clientId = clientInfo.clientId          // 设备的 client id
        }
    }
}

连接到 BLE 设备

BLE 对设备操作的相关代码位于 HandleBleDeviceActivity

  1. 请求连接
val connect = Intent(this, BleService::class.java)
connect.action = BleService.ACTION_CONNECT
connect.putExtra(BleService.EXTRA_DEVICE, device)
startService(connect)
  1. 连接结果回调
// 注册以下广播则可以获取相关的状态回调
val intentFilter = IntentFilter()
intentFilter.addAction(BleService.ACTION_DEVICE_CONNECTED)
intentFilter.addAction(BleService.ACTION_DEVICE_CONNECT_FAILED)
intentFilter.addAction(BleService.ACTION_DEVICE_DISCONNECTED)
registerReceiver(bleReceiver, intentFilter)

// 特别地,连接失败时,可以获取相关的错误原因,此部分错误原因为 Ble 原生数据,与 iFLYOS 无关
...
val result = intent.getIntExtra(BleService.EXTRA_REASON, -1)
// result 所有可能的常量,在 GattError 类中有定义
...

发送相关数据

连接设备成功后,首先需要从设备读取设备的唯一标识

val requestDeviceId = Intent(context, BleService::class.java)
requestDeviceId.action = BleService.ACTION_REQUEST_DEVICE_ID
startService(requestDeviceId)

// 广播接收器中接收 BleService.ACTION_DEVICE_ID_RECEIVED
...
val deviceId = intent.getStringExtra(BleService.EXTRA_DEVICE_ID)
...

根据 deviceId 申请供设备授权使用的 deviceCode

IFlyHome.getDeviceCode(clientId, deviceId, object : ResponseCallback {
    ...
    override fun onResponse(response: Response<String>) {
        if (response.isSuccessful) {
            val json = JsonParser().parse(response.body()).asJsonObject
            val deviceCode = json.get("device_code").asString
        }
    }
    ...
})

发送配网的相关数据给设备

val setup = Intent(context, BleService::class.java)
setup.action = BleService.ACTION_SETUP
setup.putExtra(BleService.EXTRA_DEVICE_CODE, deviceCode)    // 上文中申请到的 deviceCode
setup.putExtra(BleService.EXTRA_SSID, "SSID")               // WiFi 名称
setup.putExtra(BleService.EXTRA_PASSWORD, "PASSWORD")       // WiFi 密码,传空则代表无密码的 WiFi,但部分设备可能不支持不安全的无线网络
startService(setup)

登录错误状态码

状态码 说明
0 网络错误
1 用户主动拒绝授权

IFLYOS 提供页面

参数变量 说明
ACCOUNTS 内容账号页面,显示与 IFLYOS 账号绑定的第三方账号
CLOCKS 我的闹钟页面,提供绑定设备上的闹钟查看和设置功能
CONTROLLED_DEVICES 被控设备页面,显示可被操作的被控智能家居设备
SKILLS 语音技能相关页面,查看设备可用的技能及介绍
PERSONAL_SOUNDS 个性音库页面,控制可设置到设备上的发音人音库,实验性功能
WAKEUP_WORDS 指定设备自定义设备唤醒词,需指定 deviceId 参数
TIME_TO_SLEEP 指定设备设置定时休眠,需指定 deviceId 参数
SPEAKER 指定设备设置 TTS 发音人,需指定 deviceId 参数
BLUETOOTH 指定设备设置蓝牙开关,需指定 deviceId 参数
CHECK_UPDATE 指定设备检查更新,需指定 deviceId 参数
DIALOGUE 指定设备的对话页,显示当前设备发生的交互内容,需指定 deviceId 参数

状态码

返回结果 常量 含义
0 RESULT_UNINITIALIZED SDK 未初始化
1 RESULT_OK 调用成功
-1 RESULT_NO_WEB_VIEW 无法找到指定的 WebView
-2 RESULT_ILLEGAL_PARAMS 传入参数不合法
-3 RESULT_UN_LOGIN SDK 未登录