diff --git a/.github/app-config.json.asc b/.github/app-config.json.asc index f193b1fd3..faf543b1e 100644 --- a/.github/app-config.json.asc +++ b/.github/app-config.json.asc @@ -1,11 +1,11 @@ -----BEGIN PGP MESSAGE----- -jA0EBwMChg7dzm2rd9PK0sBVAX7mxIWl9n4tMd22ELDgOek0ir3NrXDSX3G0yFVZ -ZZGyb/n+6IFdrEyz/SRrqzhNkwIt/pAK9g+rn4XtLaaDi2LDe0IUtTqwboOQk/zP -zcf0pGrWnnJ/I/23S1ssjxRAybMH3ex7DzOUdznlBcm4Ulb4VYf0ebLLC8GZoKlL -zHzVQdXBhnxuvZgdibXMoPuVpcYo+oNJr3xIvSj7tOotZYHtR0wzV1p8WRRHm0uh -a5jpLHAK7tBr//MUm4LWgrzqtTx8M04PvMGYFxMl8LHWj6/f+IayjW119u/ioFDW -DHnK2+eNGx++3OJnFME/c1fJWI3gY+dj+hQ3lEL0HIGiYiqiZHH9WkRgpnhy/lrO -Uz7C7/O5dA== -=nWF9 +jA0ECQMCmNne972Tfxz00sBVATDk4Yf+5tpSQCXP4EQ4Wn784roCRy30KqFRFpxM +KOZVXbJSl8ODNnT8j92D5R5DdThGPtartYzALtr83W6FeydOpFLaqk1XcsY6YnKz +7OUJXG4jjexlQ3Xn2BxmQ3gCopt0nYDhL9sf0E1+YmNJi3m18BcObdkcBIUVlWVo +0ke4PurfJTzAod1vC61TSkmqOyU8bDNoPPofzKLPEcx0Xv9wNplJGjvr92so7iv6 +Rpq2rtJ85xHXstHbUuUY6pMyQfrKX8Z034uBMbscKDWLp68yPCf29eBWVFJdxELQ +WgNOnnFUln0lQHwNXK/yUAaUcgPttEg8dAUW/eWs3TvYv/Vpria2Vm8ziTmZQO4P +v9xZ/yWhGA== +=9ckr -----END PGP MESSAGE----- diff --git a/.github/workflows/opensource.yml b/.github/workflows/opensource.yml index 6de83cd1f..8bbe973d6 100644 --- a/.github/workflows/opensource.yml +++ b/.github/workflows/opensource.yml @@ -11,8 +11,8 @@ on: - '*' env: - IOT_SONATYPE_USERNAME: ${{ secrets.IOT_SONATYPE_USERNAME }} - IOT_SONATYPE_PASSWORD: ${{ secrets.IOT_SONATYPE_PASSWORD }} + IOT_SONATYPE_USERNAME: ${{ secrets.NEW_IOT_SONATYPE_USERNAME }} + IOT_SONATYPE_PASSWORD: ${{ secrets.NEW_IOT_SONATYPE_PASSWORD }} GPG_DECRYPT_PASSPHRASE: ${{ secrets.GPG_DECRYPT_PASSPHRASE }} IOT_WECOM_CID_ROBOT_KEY: ${{ secrets.IOT_WECOM_CID_ROBOT_KEY }} @@ -40,8 +40,25 @@ jobs: run: | cmake --version + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/secring.gpg.asc') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + - name: install fir - run: sudo gem install fir-cli + run: | + sudo gem uninstall fastlane + sudo gem install fir-cli + + - name: Setup NDK + run: echo "y" | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "ndk;22.1.7171670" - name: Fix App Version run: | rb=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') @@ -69,7 +86,6 @@ jobs: uses: actions/upload-artifact@v2 with: path: app/build/outputs/apk/opensource/release/*.apk - - name: Build demo apk run: | ./gradlew :sdkdemo:assembleRelease diff --git a/.github/workflows/tencent_official.yml b/.github/workflows/tencent_official.yml index d980b92c3..805c30b18 100644 --- a/.github/workflows/tencent_official.yml +++ b/.github/workflows/tencent_official.yml @@ -38,8 +38,24 @@ jobs: - name: Use cmake run: | cmake --version + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/secring.gpg.asc') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- - name: Common-->install fir - run: sudo gem install fir-cli + run: | + sudo gem uninstall fastlane + sudo gem install fir-cli + + - name: Common-->Setup NDK + run: echo "y" | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager --install "ndk;22.1.7171670" - name: Common-->Fix App Version run: | diff --git a/README.md b/README.md index 369899982..8a8e7a249 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## 接入的第三方组件 -腾讯连连是一个完整的应用项目,集成了业内主流的推送、定位、日志系统、性能统计和微信授权登录等功能。推送集成了信鸽推送,定位使用了腾讯地图,日志系统和性能统计依赖 Firebase,微信授权登录则需要微信的支持。 +腾讯连连是一个完整的应用项目,集成了业内主流的推送、定位、日志系统、性能统计和微信授权登录等功能。推送集成了信鸽推送,定位使用了腾讯地图,微信授权登录则需要微信的支持。 ## 快速开始 @@ -52,38 +52,7 @@ app-config.json 需要配置的内容,如下: } ``` -**3、Firebase(可选)** - -连连开源体验版集成了 **Firebase** 插件,用于记录应用的异常日志和性能状况。 - -* 若用户确认使用 Firebase 插件,需通过 [Firebase 官网](https://firebase.google.cn/?hl=zh-cn) 创建应用并获取 **google-services.json** 文件;将 google-services.json 文件放在 app 目录下。 -* 若不依赖 Firebase 插件,需要在以下文件中注释掉对应依赖 - - 在项目级 build.gradle(<iot-link-android>/build.gradle)中注释掉dependencies中以下三个依赖项 - - ``` - dependencies { - // classpath 'com.google.gms:google-services:4.3.3' - // classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1' - // classpath 'com.google.firebase:perf-plugin:1.3.1' - } - ``` - - 在应用级 build.gradle(<iot-link-android>/<app>/build.gradle)中注释掉以下三个应用插件和三个依赖项 - - ``` - //apply plugin: 'com.google.gms.google-services' - //apply plugin: 'com.google.firebase.crashlytics' - //apply plugin: 'com.google.firebase.firebase-perf' - - dependencies { - // implementation 'com.google.firebase:firebase-analytics-ktx:17.4.3' - // implementation 'com.google.firebase:firebase-crashlytics:17.0.1' - // implementation 'com.google.firebase:firebase-perf:19.0.7' - } - ``` - -**4、腾讯地图(可选)** +**3、腾讯地图(可选)** 连连开源体验版集成了**腾讯地图**,用于实现定位。 @@ -91,7 +60,7 @@ app-config.json 需要配置的内容,如下: * 若确认使用默认定位功能,无需修改 **TencentMapSDKValue** 配置项内容 。 * 若不使用定位功能,**TencentMapSDKValue** 设置为**长度为0的字符串**即可。 -**5、微信授权登录(可选)** +**4、微信授权登录(可选)** 连连开源体验版集成了微信授权登录。 @@ -108,7 +77,7 @@ app-config.json 需要配置的内容,如下: * 若不使用微信授权登录功能,**WXAccessAppId** 设置为**长度为0的字符串**即可。 -**6、和风天气(可选)** +**5、和风天气(可选)** 连连开源体验版集成了**和风天气**,用于实现实时天气功能。 @@ -118,7 +87,9 @@ app-config.json 需要配置的内容,如下: 完成上述配置后,依赖 Android studio 的构建,即可在手机上运行。 -## 第三方服务接入指引 +## SDK接入指引 +* [腾讯连连App-SDK](sdk/explorer-link-android/README.md) * [实时音视频接入指南](sdk/explorer-link-rtc/README.md) +* [腾讯云物联网智能视频服务接入指南](sdk/video-link-android/README.md) diff --git a/app/build.gradle b/app/build.gradle index e91d9e7aa..6cb9f4a47 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,12 +4,6 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'com.google.gms.google-services' - -apply plugin: 'com.google.firebase.crashlytics' - -apply plugin: 'com.google.firebase.firebase-perf' - rootProject.ext.configPath = "../app-config.json" apply from: "../parse_json.gradle" @@ -24,12 +18,12 @@ android { } } ndkVersion "22.1.7171670" - compileSdkVersion 29 + compileSdkVersion 31 buildToolsVersion "29.0.2" defaultConfig { applicationId "com.tencent.iot.explorer.link.opensource" minSdkVersion 21 - targetSdkVersion 29 + targetSdkVersion 31 versionCode rootProject.ext.android.versionCode versionName rootProject.ext.android.versionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -60,8 +54,8 @@ android { productFlavors { opensource { applicationId "com.tencent.iot.explorer.link.opensource" - resValue("string" , "app_name_zh", "腾讯连连开源版") - resValue("string" , "app_name_en", "Tencent LLink (OSS)") + resValue("string", "app_name_zh", "腾讯连连开源版") + resValue("string", "app_name_en", "Tencent LLink (OSS)") } //-tencentOfficial { //- applicationId "com.tencent.iot.explorer.link" @@ -70,7 +64,7 @@ android { //-} } - kotlinOptions { jvmTarget = 1.8} + kotlinOptions { jvmTarget = 1.8 } buildTypes { @@ -78,13 +72,6 @@ android { signingConfig signingConfigs.config minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - - FirebasePerformance { - // Set this flag to 'false' to disable @AddTrace annotation processing and - // automatic monitoring of HTTP/S network requests - // for a specific build variant at compile time. - instrumentationEnabled false - } } release { signingConfig signingConfigs.config @@ -102,15 +89,20 @@ android { } applicationVariants.all { variant -> variant.outputs.all { - def date = new Date().format("yyyyMMddHH" , TimeZone.getTimeZone("GMT+08")) - if(variant.buildType.name == 'debug') { + def date = new Date().format("yyyyMMddHH", TimeZone.getTimeZone("GMT+08")) + if (variant.buildType.name == 'debug') { outputFileName = "iot-link-android-debug-${date}.apk" } - if(variant.buildType.name == 'release') { + if (variant.buildType.name == 'release') { outputFileName = "iot-link-android-release-${date}.apk" } } } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { @@ -144,10 +136,8 @@ dependencies { //banner implementation 'com.youth.banner:banner:1.4.10' //左滑删除 - implementation 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.3.0' - //信鸽推送 - implementation 'com.tencent.jg:jg:1.1' - implementation 'com.tencent.tpns:tpns:1.2.0.2-release' + implementation 'com.github.mcxtzhang:SwipeDelMenuLayout:V1.2.1' + implementation 'androidx.cardview:cardview:1.0.0' api 'com.android.support:recyclerview-v7:29.0.0' //kotlin协程 @@ -158,24 +148,12 @@ dependencies { implementation 'io.github.shichuanenhui:refresh-header-classics:1.0.0' implementation 'io.github.shichuanenhui:refresh-footer-classics:1.0.0' - - implementation 'com.tencent.map:tencent-map-vector-sdk:4.4.1' - // 地图组件库,包括小车平移、点聚合等组件功能,详见开发指南。 - implementation 'com.tencent.map:sdk-utilities:1.0.6' - if (findProject(':sdk:explorer-link-android') != null) { implementation project(path: ':sdk:explorer-link-android') } else { implementation 'com.tencent.iot.explorer:explorer-link-android:+' } - implementation 'com.google.firebase:firebase-analytics-ktx:17.5.0' - - // Add the Firebase Crashlytics SDK. - implementation 'com.google.firebase:firebase-crashlytics:17.2.1' - - implementation 'com.google.firebase:firebase-perf:19.0.8' - implementation 'com.squareup.picasso:picasso:2.71828' implementation 'com.github.chrisbanes:PhotoView:2.3.0' @@ -186,8 +164,7 @@ dependencies { implementation 'com.alibaba:fastjson:1.2.73' implementation "com.tencent.iot.thirdparty.android:esptouch:0.3.7.2" implementation "com.github.skydoves:progressview:1.1.2" - implementation 'com.belerweb:pinyin4j:2.5.0' - implementation 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.2.6' + implementation 'com.belerweb:pinyin4j:2.5.1' implementation 'cn.aigestudio.wheelpicker:WheelPicker:1.1.3' implementation 'com.chauthai.swipereveallayout:swipe-reveal-layout:1.4.1' implementation 'com.github.Jay-Goo:RangeSeekBar:v3.0.0' @@ -196,4 +173,12 @@ dependencies { } else { implementation 'com.tencent.iot.explorer:explorer-link-rtc:+' } + + if (findProject(':sdk:video-link-android') != null) { + api project(path: ':sdk:video-link-android') + } + implementation 'com.tencent.iot.thirdparty.android:ijkplayer-java:2.0.8' + implementation 'com.tencent.iot.thirdparty.android:ijkplayer-armv7a:2.0.8' + implementation 'com.tencent.iot.thirdparty.android:ijkplayer-arm64:2.0.8' + } diff --git a/app/google-services.json b/app/google-services.json deleted file mode 100644 index f2ba70bb6..000000000 --- a/app/google-services.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "project_info": { - "project_number": "369055548986", - "firebase_url": "https://tencent-iot.firebaseio.com", - "project_id": "tencent-iot", - "storage_bucket": "tencent-iot.appspot.com" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:369055548986:android:5a67a4fbd33a90a6441703", - "android_client_info": { - "package_name": "com.tencent.iot.explorer.link.opensource" - } - }, - "oauth_client": [ - { - "client_id": "369055548986-rutdld43edvcm2s9m6i0imeb8mdh8f3o.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyANM497q4v3BSAr3mfImfXePn94HPDruCo" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "369055548986-rutdld43edvcm2s9m6i0imeb8mdh8f3o.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "369055548986-p9pd51l4km1eb9h1d8d178chu34ttsc1.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.tencent.iot.explorer.link" - } - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:369055548986:android:103245fe964378c2441703", - "android_client_info": { - "package_name": "com.tencent.streamasrtts" - } - }, - "oauth_client": [ - { - "client_id": "369055548986-rutdld43edvcm2s9m6i0imeb8mdh8f3o.apps.googleusercontent.com", - "client_type": 3 - } - ], - "api_key": [ - { - "current_key": "AIzaSyANM497q4v3BSAr3mfImfXePn94HPDruCo" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "369055548986-rutdld43edvcm2s9m6i0imeb8mdh8f3o.apps.googleusercontent.com", - "client_type": 3 - }, - { - "client_id": "369055548986-p9pd51l4km1eb9h1d8d178chu34ttsc1.apps.googleusercontent.com", - "client_type": 2, - "ios_info": { - "bundle_id": "com.tencent.iot.explorer.link" - } - } - ] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 37c6dd446..eff034ee7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,210 +1,197 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/App.kt b/app/src/main/java/com/tencent/iot/explorer/link/App.kt index 81d37c8c6..31c23071f 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/App.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/App.kt @@ -3,13 +3,13 @@ package com.tencent.iot.explorer.link import android.app.Activity import android.app.Application import android.content.Intent +import android.os.Build import android.os.Bundle import android.text.TextUtils import android.widget.Toast import androidx.multidex.MultiDex import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONObject -import com.tencent.android.tpush.XGPushConfig import com.tencent.iot.explorer.link.core.auth.IoTAuth import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.entity.DeviceEntity @@ -31,8 +31,10 @@ import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.WeatherUtils import com.tencent.iot.explorer.link.rtc.model.* -import com.tencent.iot.explorer.link.rtc.ui.audiocall.TRTCAudioCallActivity -import com.tencent.iot.explorer.link.rtc.ui.videocall.TRTCVideoCallActivity +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.TRTCAudioCallActivity +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.TRTCVideoCallActivity +import com.tencent.iot.explorer.link.kitlink.activity.videoui.RecordVideoActivity +import com.tencent.iot.explorer.link.kitlink.util.LogcatHelper import java.util.* import kotlin.collections.ArrayList @@ -50,6 +52,7 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag const val CONFIG = "config" const val MUST_UPGRADE_TAG = "master" var language: String? = "" + var uuid: String = "" // 根据编译使用的 buildType 类型确定是否是 debug 版本 // 编译依赖的 buildType 包含 debug 字串即认为是 debug 版本 @@ -111,14 +114,29 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag L.e("App isForeground ${data.isForeground}") L.e("App isCalling ${TRTCUIManager.getInstance().isCalling}") if (data.isForeground && !TRTCUIManager.getInstance().isCalling) { //在前台,没有正在通话时,唤起通话页面 - TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) TRTCUIManager.getInstance().deviceId = deviceId if (callingType == TRTCCalling.TYPE_VIDEO_CALL) { TRTCUIManager.getInstance().isCalling = true + for (devEls in data.deviceList) { + if (devEls.DeviceId == deviceId && devEls.CategoryId == 567) { // 消费版视频产品-视频 被呼 + TRTCUIManager.getInstance().setSessionManager(P2PAppSessionManager()) + RecordVideoActivity.startBeingCall(activity, deviceId, true) + return + } + } + TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) TRTCVideoCallActivity.startBeingCall(activity, RoomKey(), deviceId) } else if (callingType == TRTCCalling.TYPE_AUDIO_CALL) { TRTCUIManager.getInstance().isCalling = true + for (devEls in data.deviceList) { + if (devEls.DeviceId == deviceId && devEls.CategoryId == 567) { // 消费版视频产品-音频 被呼 + TRTCUIManager.getInstance().setSessionManager(P2PAppSessionManager()) + RecordVideoActivity.startBeingCall(activity, deviceId, false) + return + } + } + TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) TRTCAudioCallActivity.startBeingCall(activity, RoomKey(), deviceId) } } @@ -128,7 +146,12 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag override fun onCreate() { super.onCreate() MultiDex.install(this) - IoTAuth.setWebSocketTag(Utils.getAndroidID(this)) // 设置wss的uin + uuid = SharePreferenceUtil.getString(this, CONFIG, CommonField.UUID) + if (TextUtils.isEmpty(uuid)) { + SharePreferenceUtil.saveString(this, CONFIG, CommonField.UUID, UUID.randomUUID().toString()) + uuid = SharePreferenceUtil.getString(this, CONFIG, CommonField.UUID) + } + IoTAuth.setWebSocketTag(uuid) // 设置wss的uin IoTAuth.setWebSocketCallback(this) // 设置WebSocket连接状态回调 IoTAuth.init(BuildConfig.TencentIotLinkAppkey, BuildConfig.TencentIotLinkAppSecret) //初始化弹框 @@ -137,9 +160,6 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag L.isLog = DEBUG_VERSION //日志等级 L.LOG_LEVEL = L.LEVEL_DEBUG - //信鸽推送日志开关 - XGPushConfig.enableDebug(applicationContext, DEBUG_VERSION) - XGPushConfig.enablePullUpOtherApp(applicationContext, PULL_OTHER) language = SharePreferenceUtil.getString(this, CONFIG, "language") data.readLocalUser(this) data.appLifeCircleId = UUID.randomUUID().toString() @@ -150,6 +170,10 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag lang = lang.substring(0,2) WeatherUtils.defaultLang = lang BleConfigService.get().context = this + + loadLastCountryInfo(); + + LogcatHelper.getInstance(this.applicationContext).start() } /** @@ -157,6 +181,7 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag */ override fun onTerminate() { super.onTerminate() + LogcatHelper.getInstance(this.applicationContext).stop() //关闭WebSocket IoTAuth.destroy() T.setContext(null) @@ -170,7 +195,9 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag // App enters foreground data.isForeground = true L.e("App foreground") - requestDeviceList() + if (!TextUtils.isEmpty(data.getToken())) { + requestDeviceList() + } if (activity is AppLifeCircleListener) { activity.onAppGoforeground() } @@ -306,8 +333,7 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag if (audioCallStatusJson != null) { audioCallStatus = audioCallStatusJson.getInteger("Value") } - val calledUserIdJson = - dataJson.getJSONObject(MessageConst.USERID) + val calledUserIdJson = dataJson.getJSONObject(MessageConst.TRTC_CALLEDID) var calledUserId = "" if (calledUserIdJson != null) { calledUserId = calledUserIdJson.getString("Value") @@ -345,6 +371,16 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag */ fun startBeingCall(callingType: Int, deviceId: String) { if (TRTCUIManager.getInstance().callingDeviceId != "") { //App主动呼叫 + for (devEls in data.deviceList) { + if (devEls.DeviceId == deviceId && devEls.CategoryId == 567) { // 消费版视频产品-视频 被呼 + if (callingType == TRTCCalling.TYPE_VIDEO_CALL) { + TRTCUIManager.getInstance().joinRoom(TRTCCalling.TYPE_VIDEO_CALL, TRTCUIManager.getInstance().callingDeviceId, RoomKey()) + } else if (callingType == TRTCCalling.TYPE_AUDIO_CALL) { + TRTCUIManager.getInstance().joinRoom(TRTCCalling.TYPE_AUDIO_CALL, TRTCUIManager.getInstance().callingDeviceId, RoomKey()) + } + return + } + } trtcCallDevice(callingType) } else { //App被动 appStartBeingCall(callingType, deviceId) @@ -460,7 +496,20 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag L.d("上报数据:id=$id value=$value") var userId = SharePreferenceUtil.getString(activity, CONFIG, CommonField.USER_ID) - var data = "{\"$id\":$value, \"${MessageConst.USERID}\":\"$userId\"}" + var callerId = "" + var calledId = "" + if (TRTCUIManager.getInstance().callingDeviceId.equals("")) { //被叫 + callerId = deviceId + calledId = userId + } else { //主叫 + callerId = userId + calledId = deviceId + if (!TRTCUIManager.getInstance().callingDeviceId.equals(deviceId)) { + callerId = deviceId + calledId = userId + } + } + var data = "{\"$id\":$value, \"${MessageConst.TRTC_CALLEDID}\":\"$calledId\", \"${MessageConst.TRTC_CALLERID}\":\"$callerId\"}" HttpRequest.instance.controlDevice(productId, deviceName, data, object: MyCallback { override fun fail(msg: String?, reqCode: Int) { if (msg != null) L.e(msg) @@ -482,6 +531,17 @@ class App : Application(), Application.ActivityLifecycleCallbacks, PayloadMessag IoTAuth.registerActivePush(data.rtcDeviceIdList!!, null) } } + + private fun loadLastCountryInfo() { + var countryInfo = Utils.getStringValueFromXml(T.getContext(), CommonField.COUNTRY_INFO, CommonField.COUNTRY_INFO) + if (TextUtils.isEmpty(countryInfo)) return + + countryInfo!!.split("+").let { + val regionId = it[1] + App.data.regionId = regionId + App.data.region = it[3] + } + } } interface AppLifeCircleListener { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/AppData.kt b/app/src/main/java/com/tencent/iot/explorer/link/AppData.kt index 61a210815..1c2469fdc 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/AppData.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/AppData.kt @@ -1,6 +1,7 @@ package com.tencent.iot.explorer.link import android.content.Context +import android.os.Build import android.text.TextUtils import com.tencent.iot.explorer.link.core.auth.IoTAuth import com.tencent.iot.explorer.link.core.auth.entity.DeviceEntity @@ -81,6 +82,36 @@ class AppData private constructor() { // 设备的类目数 var numOfCategories = 0 + // 手机品牌 + private var mobileBrand = "" + // 手机型号 + private var mobileModel = "" + // 分辨率宽 320 + var resolutionWidth = 320 + // 分辨率高 320 + var resolutionHeight = 240 + // 帧率 15 + var frameRate = 15 + // 播放器参数frameDrop -1 + var frameDrop = -1 + // 播放器追帧参数frameSpeed 1.5 + var frameSpeed : Float = 1.5F + + + fun getMobileBrand(): String { + if (TextUtils.isEmpty(mobileBrand)) { + mobileBrand = Build.BRAND + } + return mobileBrand + } + + fun getMobileModel(): String { + if (TextUtils.isEmpty(mobileModel)) { + mobileModel = Build.MODEL + } + return mobileModel + } + /** * 重置刷新级别到设备级别 */ diff --git a/app/src/main/java/com/tencent/iot/explorer/link/MessageReceiver.java b/app/src/main/java/com/tencent/iot/explorer/link/MessageReceiver.java deleted file mode 100644 index c4c29c7f1..000000000 --- a/app/src/main/java/com/tencent/iot/explorer/link/MessageReceiver.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.tencent.iot.explorer.link; - -import android.content.Context; -import android.content.Intent; -import android.text.TextUtils; -import android.util.Log; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.tencent.android.tpush.NotificationAction; -import com.tencent.android.tpush.XGPushBaseReceiver; -import com.tencent.android.tpush.XGPushClickedResult; -import com.tencent.android.tpush.XGPushRegisterResult; -import com.tencent.android.tpush.XGPushShowedResult; -import com.tencent.android.tpush.XGPushTextMessage; -import com.tencent.iot.explorer.link.core.auth.message.MessageConst; -import com.tencent.iot.explorer.link.core.log.L; -import com.tencent.iot.explorer.link.kitlink.activity.HelpWebViewActivity; -import com.tencent.iot.explorer.link.kitlink.consts.CommonField; - -public class MessageReceiver extends XGPushBaseReceiver { - private static final String TAG = MessageReceiver.class.getSimpleName(); - - public static final String UPDATE_LISTVIEW_ACTION = "com.qq.xgdemo.activity.UPDATE_LISTVIEW"; - - private void show(Context context, String text) { -// Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); - } - - // 通知展示 - @Override - public void onNotificationShowedResult(Context context, - XGPushShowedResult notifiShowedRlt) { - if (context == null || notifiShowedRlt == null) { - return; - } - App.Companion.getData().setNotificationId(notifiShowedRlt.getNotifactionId()); - Intent viewIntent = new Intent(UPDATE_LISTVIEW_ACTION); - context.sendBroadcast(viewIntent); - show(context, context.getString(R.string.new_msg_be_showed) + notifiShowedRlt.toString()); - L.INSTANCE.d(TAG, context.getString(R.string.new_msg_be_showed) + notifiShowedRlt.toString() + context.getString(R.string.push_channel) + notifiShowedRlt.getPushChannel()); - } - - @Override - public void onUnregisterResult(Context context, int errorCode) { - if (context == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - text = context.getString(R.string.unregister_success);//"反注册成功"; - } else { - text = context.getString(R.string.unregister_failed) + errorCode; //"反注册失败" - } - L.INSTANCE.d(TAG, text); - show(context, text); - - } - - @Override - public void onSetTagResult(Context context, int errorCode, String tagName) { - if (context == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - text = "\"" + tagName + "\"" + context.getResources().getString(R.string.success_set); - } else { - text = "\"" + tagName + "\"" + context.getResources().getString(R.string.failed_set_with_error_code) + errorCode; - } - L.INSTANCE.d(TAG, text); - show(context, text); - - } - - @Override - public void onDeleteTagResult(Context context, int errorCode, String tagName) { - if (context == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - text = "\"" + tagName + "\"" + context.getResources().getString(R.string.success_delete); - } else { - text = "\"" + tagName + "\"" + context.getResources().getString(R.string.failed_delete_with_error_code) + errorCode; - } - L.INSTANCE.d(TAG, text); - show(context, text); - - } - - @Override - public void onSetAccountResult(Context context, int i, String s) { - - } - - @Override - public void onDeleteAccountResult(Context context, int i, String s) { - - } - - // 通知点击回调 actionType=1为该消息被清除,actionType=0为该消息被点击 - @Override - public void onNotificationClickedResult(Context context, XGPushClickedResult message) { - if (context == null || message == null) { - return; - } - String text = ""; - if (message.getActionType() == NotificationAction.clicked.getType()) { - // 通知在通知栏被点击啦。。。。。 - // APP自己处理点击的相关动作 - // 这个动作可以在activity的onResume也能监听,请看第3点相关内容 - text = context.getString(R.string.notification_opened) + message; // "通知被打开 :" - String customContent = message.getCustomContent(); - if (!TextUtils.isEmpty(customContent)) { - if (customContent.contains("call_status")) { // RTC信鸽推送消息 - App.Companion.getData().setRtcNotificationClicked(true); - } - } - } else if (message.getActionType() == NotificationAction.delete.getType()) { - // 通知被清除啦。。。。 - // APP自己处理通知被清除后的相关动作 - text = context.getString(R.string.notification_removed) + message; // "通知被清除 :" - } - - // APP自主处理的过程。。。 - L.INSTANCE.d(TAG, text); - show(context, text); - - checkMsgWithAction(context, message.getCustomContent()); - } - - @Override - public void onRegisterResult(Context context, int errorCode, XGPushRegisterResult message) { - - if (context == null || message == null) { - return; - } - String text = ""; - if (errorCode == XGPushBaseReceiver.SUCCESS) { - // 在这里拿token - String token = message.getToken(); - text = context.getString(R.string.register_success) + token; // "注册成功1. token:" - } else { - text = message + context.getString(R.string.register_failed) + errorCode; // "注册失败,错误码:" - } - L.INSTANCE.d(TAG, text); - show(context, text); - } - - // 消息透传 - @Override - public void onTextMessage(Context context, XGPushTextMessage message) { - String text = context.getString(R.string.recv_msg) + message.toString();//"收到消息:" - // APP自主处理消息的过程... - L.INSTANCE.d(TAG, text); - show(context, text); - // checkMsgWithAction(context, message.getCustomContent()); - } - - private void checkMsgWithAction(Context context, String msg) { - if (TextUtils.isEmpty(msg)) { - return; - } - JSONObject msgJson = JSON.parseObject(msg); - if (msgJson.containsKey(CommonField.MSG_TYPE) && - msgJson.getString(CommonField.MSG_TYPE).equals( - PushedMessageType.FEEDBACK.getValueStr())) { - Intent intent = new Intent(App.Companion.getActivity(), HelpWebViewActivity.class); - App.Companion.getActivity().startActivity(intent); - } - } -} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/P2PAppSessionManager.kt b/app/src/main/java/com/tencent/iot/explorer/link/P2PAppSessionManager.kt new file mode 100644 index 000000000..377c79797 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/P2PAppSessionManager.kt @@ -0,0 +1,97 @@ +package com.tencent.iot.explorer.link + +import com.tencent.iot.explorer.link.core.auth.callback.MyCallback +import com.tencent.iot.explorer.link.core.auth.message.MessageConst +import com.tencent.iot.explorer.link.core.auth.response.BaseResponse +import com.tencent.iot.explorer.link.core.log.L +import com.tencent.iot.explorer.link.core.utils.SharePreferenceUtil +import com.tencent.iot.explorer.link.kitlink.consts.CommonField +import com.tencent.iot.explorer.link.kitlink.util.HttpRequest +import com.tencent.iot.explorer.link.rtc.model.RoomKey +import com.tencent.iot.explorer.link.rtc.model.TRTCCalling +import com.tencent.iot.explorer.link.rtc.model.TRTCSessionManager +import com.tencent.iot.explorer.link.rtc.model.TRTCUIManager + +class P2PAppSessionManager : TRTCSessionManager() { + + override fun joinRoom(callingType: Int, deviceId: String) { + super.joinRoom(callingType, deviceId) + + if (TRTCUIManager.getInstance().callingDeviceId.equals("")) { //被叫 + if (callingType == TRTCCalling.TYPE_VIDEO_CALL) { + controlDevice(MessageConst.TRTC_VIDEO_CALL_STATUS, "1", deviceId) + } else if (callingType == TRTCCalling.TYPE_AUDIO_CALL) { + controlDevice(MessageConst.TRTC_AUDIO_CALL_STATUS, "1", deviceId) + } + } else { + startBeingCall(callingType, deviceId) + } + } + + override fun exitRoom(callingType: Int, deviceId: String) { + super.exitRoom(callingType, deviceId) + if (callingType == TRTCCalling.TYPE_VIDEO_CALL) { + controlDevice(MessageConst.TRTC_VIDEO_CALL_STATUS, "0", deviceId) + } else if (callingType == TRTCCalling.TYPE_AUDIO_CALL) { + controlDevice(MessageConst.TRTC_AUDIO_CALL_STATUS, "0", deviceId) + } + } + + override fun resetTRTCStatus() { + super.resetTRTCStatus() + TRTCUIManager.getInstance().callingDeviceId = "" + } + + /** + * 呼叫设备获取trtc参数信息 + */ + fun startBeingCall(callingType: Int, deviceId: String) { + App.activity?.runOnUiThread { + if (callingType == TRTCCalling.TYPE_VIDEO_CALL) { + TRTCUIManager.getInstance().joinRoom(callingType, deviceId, RoomKey()) + } else if (callingType == TRTCCalling.TYPE_AUDIO_CALL) { + TRTCUIManager.getInstance().joinRoom(callingType, deviceId, RoomKey()) + } + } + } + + /** + * 用户控制设备(上报数据) + */ + fun controlDevice(id: String, value: String, deviceId: String) { + + val list = deviceId.split("/") + + var productId = "" + var deviceName = "" + if (list.size == 2) { + productId = list[0] + deviceName = list[1] + } else { //deviceId格式有问题 + return + } + + L.d("上报数据:id=$id value=$value") + val userId = SharePreferenceUtil.getString(App.activity, App.CONFIG, CommonField.USER_ID) + var callerId = "" + var calledId = "" + if (TRTCUIManager.getInstance().callingDeviceId.equals("")) { //被叫 + callerId = deviceId + calledId = userId + } else { //主叫 + callerId = userId + calledId = deviceId + } + var data = "{\"$id\":$value, \"${MessageConst.TRTC_CALLEDID}\":\"$calledId\", \"${MessageConst.TRTC_CALLERID}\":\"$callerId\"}" + HttpRequest.instance.controlDevice(productId, deviceName, data, object: MyCallback { + override fun fail(msg: String?, reqCode: Int) { + if (msg != null) L.e(msg) + } + + override fun success(response: BaseResponse, reqCode: Int) { + + } + + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/TRTCAppSessionManager.kt b/app/src/main/java/com/tencent/iot/explorer/link/TRTCAppSessionManager.kt index 6bc8b36da..ac3398636 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/TRTCAppSessionManager.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/TRTCAppSessionManager.kt @@ -103,7 +103,16 @@ class TRTCAppSessionManager : TRTCSessionManager() { L.d("上报数据:id=$id value=$value") val userId = SharePreferenceUtil.getString(App.activity, App.CONFIG, CommonField.USER_ID) - var data = "{\"$id\":$value, \"${MessageConst.USERID}\":\"$userId\"}" + var callerId = "" + var calledId = "" + if (TRTCUIManager.getInstance().callingDeviceId.equals("")) { //被叫 + callerId = deviceId + calledId = userId + } else { //主叫 + callerId = userId + calledId = deviceId + } + var data = "{\"$id\":$value, \"${MessageConst.TRTC_CALLEDID}\":\"$calledId\", \"${MessageConst.TRTC_CALLERID}\":\"$callerId\"}" HttpRequest.instance.controlDevice(productId, deviceName, data, object: MyCallback { override fun fail(msg: String?, reqCode: Int) { if (msg != null) L.e(msg) diff --git a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/InputBirthdayDialog.java b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/InputBirthdayDialog.java index f550e97e1..ec62bffc0 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/InputBirthdayDialog.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/InputBirthdayDialog.java @@ -22,6 +22,7 @@ public class InputBirthdayDialog extends IosCenterStyleDialog implements View.OnClickListener { private TextView okBtn; private TextView cancelBtn; + private TextView detailTv; private WheelPicker yearPicker; private WheelPicker monthPicker; private WheelPicker dayPicker; @@ -34,9 +35,15 @@ public class InputBirthdayDialog extends IosCenterStyleDialog implements View.On private int MAX_YEAR_NUM; private final int MIN_YEAR = 1900; private final int MAX_MONTH_NUM = 12; + private String detailString; - public InputBirthdayDialog(Context context) { + public InputBirthdayDialog(Context context, String countryCode) { super(context, R.layout.popup_birthday_layout); + if (countryCode.equals("1")) { + detailString = context.getString(R.string.birthday_detail_usa); + } else { + detailString = context.getString(R.string.birthday_detail_china); + } Date currentDate = new Date(); final int year = currentDate.getYear() + MIN_YEAR; @@ -54,6 +61,7 @@ public InputBirthdayDialog(Context context) { public void initView() { okBtn = view.findViewById(R.id.tv_ok); cancelBtn = view.findViewById(R.id.tv_cancel); + detailTv = view.findViewById(R.id.tv_detail); yearPicker = view.findViewById(R.id.wheel_timer_year_picker); monthPicker = view.findViewById(R.id.wheel_timer_month_picker); dayPicker = view.findViewById(R.id.wheel_timer_day_picker); @@ -68,6 +76,8 @@ public void initView() { dialogLayout.setOnClickListener(this); monthPicker.setOnItemSelectedListener(yearMonthSelectedListener); yearPicker.setOnItemSelectedListener(yearMonthSelectedListener); + + detailTv.setText(detailString); initDateView(); } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/PermissionDialog.java b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/PermissionDialog.java index 36dd98f02..d75a4ccc3 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/PermissionDialog.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/PermissionDialog.java @@ -2,6 +2,7 @@ import android.content.Context; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; import androidx.constraintlayout.widget.ConstraintLayout; @@ -9,66 +10,33 @@ import com.tencent.iot.explorer.link.R; import com.tencent.iot.explorer.link.customview.dialog.entity.UpgradeInfo; -public class PermissionDialog extends IosCenterStyleDialog implements View.OnClickListener { +public class PermissionDialog extends IosCenterStyleDialog { + private ImageView logo; private TextView detail; - private TextView detailLips; - private TextView btnRefuse; - private TextView btnOK; + private TextView title; private ConstraintLayout outLayout; private String detailString; - private String detailLipsString; + private String titleString; + private int logoResId; - public PermissionDialog(Context context, String detail, String detailLips) { - super(context, R.layout.popup_permission_layout); + public PermissionDialog(Context context, int logoResId, String detail, String title) { + super(context, R.layout.popup_permission_layout, false); + this.logoResId = logoResId; this.detailString = detail; - this.detailLipsString = detailLips; + this.titleString = title; } @Override public void initView() { outLayout = view.findViewById(R.id.permission_dialog_layout); detail = view.findViewById(R.id.tv_detail); - detailLips = view.findViewById(R.id.tv_detail_lips); - btnRefuse = view.findViewById(R.id.tv_refuse); - btnOK = view.findViewById(R.id.tv_ok); - - btnRefuse.setOnClickListener(this); - btnOK.setOnClickListener(this); + title = view.findViewById(R.id.tv_title); + logo = view.findViewById(R.id.iv_logo); detail.setText(detailString); - detailLips.setText(detailLipsString); + title.setText(titleString); + logo.setImageResource(logoResId); outLayout.setBackgroundColor(getContext().getResources().getColor(R.color.dialog_background)); } - - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.tv_refuse: - dismiss(); - if (onDismisListener != null) { - onDismisListener.OnClickRefuse(); - } - break; - case R.id.tv_ok: - dismiss(); - if (onDismisListener != null) { - onDismisListener.OnClickOK(); - } - break; - default: - break; - } - } - - private volatile OnDismisListener onDismisListener; - - public interface OnDismisListener { - void OnClickRefuse(); - void OnClickOK(); - } - - public void setOnDismisListener(OnDismisListener onDismisListener) { - this.onDismisListener = onDismisListener; - } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/TipShareDevDialog.java b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/TipShareDevDialog.java index 3d5d5d9d5..aba4059cb 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/TipShareDevDialog.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/TipShareDevDialog.java @@ -12,6 +12,7 @@ import androidx.constraintlayout.widget.ConstraintLayout; +import com.tencent.iot.explorer.link.App; import com.tencent.iot.explorer.link.R; import com.tencent.iot.explorer.link.T; import com.tencent.iot.explorer.link.core.utils.Utils; @@ -24,7 +25,7 @@ public class TipShareDevDialog extends IosCenterStyleDialog implements View.OnCl private ConstraintLayout outsideLayout; private ConstraintLayout dialogLayout; private TextView moreInfo; - private String ANDROID_ID = Utils.INSTANCE.getAndroidID(T.getContext()); + private String ANDROID_ID = App.Companion.getUuid(); public TipShareDevDialog(Context context) { super(context, R.layout.popup_tip_share_dev_layout); @@ -87,9 +88,7 @@ private class TextClick extends ClickableSpan { public void onClick(View widget) { Intent intent = new Intent(getContext(), WebActivity.class); intent.putExtra(CommonField.EXTRA_TITLE, getContext().getString(R.string.register_agree_4)); - String url = CommonField.POLICY_PREFIX; - url += "?uin=" + ANDROID_ID; - url += CommonField.PRIVACY_POLICY_SUFFIX; + String url = CommonField.PRIVACY_POLICY_URL_CN_ZH; intent.putExtra(CommonField.EXTRA_TEXT, url); getContext().startActivity(intent); } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/UserAgreeDialog.kt b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/UserAgreeDialog.kt index dea54306d..c14dd7e4b 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/UserAgreeDialog.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/customview/dialog/UserAgreeDialog.kt @@ -52,20 +52,38 @@ class UserAgreeDialog(context: Context) : IosCenterStyleDialog(context, R.layout val agreeContentStrPrefix = context.getString(R.string.rule_content_prefix) val partStr1 = "《${context.getString(R.string.register_agree_2)}》" val partStr2 = context.getString(R.string.register_agree_3) - val partStr3 = "《${context.getString(R.string.register_agree_4)}》" + + val partStr3 = "《${context.getString(R.string.summary_of_privacy_policy)}》" + val partStr4 = context.getString(R.string.pause_mark) + + val partStr5 = "《${context.getString(R.string.register_agree_4)}》" val agreeContentStrMiddle = context.getString(R.string.rule_content_middle) - val partStr4 = "《${context.getString(R.string.rule_content_list)}》" + val partStr6 = "《${context.getString(R.string.app_logo_name)}App${context.getString(R.string.personal_information_list)}》" + val agreeContentStrMiddle2 = context.getString(R.string.rule_content_middle2) + val partStr7 = "《${context.getString(R.string.rule_content_list)}》" val agreeContentStrSuffix = context.getString(R.string.rule_content_suffix) - var agreeContentStr = agreeContentStrPrefix + partStr1 + partStr2 + partStr3 + agreeContentStrMiddle + partStr4 + agreeContentStrSuffix + var agreeContentStr = agreeContentStrPrefix + partStr1 + partStr2 + partStr3 + partStr4 + partStr5 + agreeContentStrMiddle + partStr6 + agreeContentStrMiddle2 + partStr7 + agreeContentStrSuffix var agreeContentSpannable = SpannableStringBuilder(agreeContentStr) + agreeContentSpannable.setSpan(IndexClickableSpan(context, 1), agreeContentStrPrefix.length, agreeContentStrPrefix.length + partStr1.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + val start1 = agreeContentStrPrefix.length + partStr1.length + partStr2.length agreeContentSpannable.setSpan(IndexClickableSpan(context, 2), start1, start1 + partStr3.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - val start2 = agreeContentStrPrefix.length + partStr1.length + partStr2.length + partStr3.length + agreeContentStrMiddle.length + + val start2 = agreeContentStrPrefix.length + partStr1.length + partStr2.length + partStr3.length + partStr4.length agreeContentSpannable.setSpan(IndexClickableSpan(context, 3), - start2, start2 + partStr4.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + start2, start2 + partStr5.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + + val start3 = agreeContentStrPrefix.length + partStr1.length + partStr2.length + partStr3.length + partStr4.length + partStr5.length + agreeContentStrMiddle.length + agreeContentSpannable.setSpan(IndexClickableSpan(context, 4), + start3, start3 + partStr6.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + + val start4 = agreeContentStrPrefix.length + partStr1.length + partStr2.length + partStr3.length + partStr4.length + partStr5.length + agreeContentStrMiddle.length + partStr6.length + agreeContentStrMiddle2.length + agreeContentSpannable.setSpan(IndexClickableSpan(context, 5), + start4, start4 + partStr7.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + tv_tip_content?.movementMethod = LinkMovementMethod.getInstance() tv_tip_content?.text = agreeContentSpannable @@ -102,7 +120,9 @@ class UserAgreeDialog(context: Context) : IosCenterStyleDialog(context, R.layout fun onOkClicked() fun onOkClickedUserAgreement() fun onOkClickedPrivacyPolicy() + fun onOkClickedPersonalInfoList() fun onOkClickedThirdSDKList() + fun onClickedPrivacyPolicySummary() } fun setOnDismisListener(onDismisListener: OnDismisListener?) { @@ -127,8 +147,12 @@ class UserAgreeDialog(context: Context) : IosCenterStyleDialog(context, R.layout if (index == 1) { onDismisListener?.onOkClickedUserAgreement() } else if (index == 2) { - onDismisListener?.onOkClickedPrivacyPolicy() + onDismisListener?.onClickedPrivacyPolicySummary() } else if (index == 3) { + onDismisListener?.onOkClickedPrivacyPolicy() + } else if (index == 4) { + onDismisListener?.onOkClickedPersonalInfoList() + } else if (index == 5) { onDismisListener?.onOkClickedThirdSDKList() } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/customview/image/RoundImageView.java b/app/src/main/java/com/tencent/iot/explorer/link/customview/image/RoundImageView.java index 04e42d379..3706b374c 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/customview/image/RoundImageView.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/customview/image/RoundImageView.java @@ -72,6 +72,9 @@ protected void onDraw(Canvas canvas) { if (drawable.getClass() == NinePatchDrawable.class) return; Bitmap b = drawableToBitmap(drawable, getMeasuredWidth(), getMeasuredHeight()); + if (b == null) { + return; + } Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true); if (defaultWidth == 0) { defaultWidth = getWidth(); diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AboutUsActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AboutUsActivity.kt index c7f7c8666..689c5b9f6 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AboutUsActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AboutUsActivity.kt @@ -27,7 +27,7 @@ import kotlinx.android.synthetic.main.menu_back_layout.* */ class AboutUsActivity : BaseActivity() { - private val ANDROID_ID = Utils.getAndroidID(T.getContext()) + private val ANDROID_ID = App.uuid override fun getContentView(): Int { return R.layout.activity_about_us @@ -59,6 +59,8 @@ class AboutUsActivity : BaseActivity() { iv_back.setOnClickListener { finish() } tv_title_privacy_policy.setOnClickListener(listener) tv_title_user_agreement.setOnClickListener(listener) + tv_title_third_party_information.setOnClickListener(listener) + tv_title_collected_personal_information.setOnClickListener(listener) tv_about_app_version.setOnClickListener(listener) tv_title_opensource.setOnClickListener(listener) } @@ -99,9 +101,7 @@ class AboutUsActivity : BaseActivity() { if (Utils.getLang().contains(CommonField.ZH_TAG)) { val intent = Intent(this@AboutUsActivity, WebActivity::class.java) intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_4)) - var url = CommonField.POLICY_PREFIX - url += "?uin=$ANDROID_ID" - url += CommonField.PRIVACY_POLICY_SUFFIX + var url = CommonField.PRIVACY_POLICY_URL_CN_ZH intent.putExtra(CommonField.EXTRA_TEXT, url) startActivity(intent) } else { @@ -118,6 +118,30 @@ class AboutUsActivity : BaseActivity() { } } + tv_title_third_party_information -> { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@AboutUsActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.rule_content_list)) + var url = CommonField.THIRD_SDK_URL_US_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@AboutUsActivity, getString(R.string.rule_content_list), CommonField.THIRD_SDK_URL_US_EN) + } + } + + tv_title_collected_personal_information -> { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@AboutUsActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.personal_information_list)) + var url = CommonField.PERSONAL_INFO_URL_US_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@AboutUsActivity, getString(R.string.personal_information_list), CommonField.PERSONAL_INFO_URL_US_EN) + } + } + tv_title_opensource -> { var url = "" if (Utils.getLang().contains(CommonField.ZH_TAG)) { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AccountAndSafetyActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AccountAndSafetyActivity.kt index 7db03a01a..8587da376 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AccountAndSafetyActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AccountAndSafetyActivity.kt @@ -15,7 +15,6 @@ import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.response.BaseResponse import com.tencent.iot.explorer.link.core.utils.Utils -import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog import kotlinx.android.synthetic.main.activity_account_and_safety.* import kotlinx.android.synthetic.main.menu_back_layout.* @@ -73,18 +72,7 @@ class AccountAndSafetyActivity : PActivity(), AccountAndSafetyView, View.OnClick if (App.data.userInfo.HasWxOpenID == "1") { T.show(getString(R.string.wechat_bind_already)) //微信已经绑定过了, 请勿重复绑定 } else { - var dlg = PermissionDialog(this@AccountAndSafetyActivity, getString(R.string.permission_of_wechat), getString(R.string.permission_of_wechat_lips)) - dlg.show() - dlg.setOnDismisListener(object : PermissionDialog.OnDismisListener { - override fun OnClickRefuse() { - - } - - override fun OnClickOK() { - WeChatLogin.getInstance().login(this@AccountAndSafetyActivity, this@AccountAndSafetyActivity) - } - - }) + WeChatLogin.getInstance().login(this, this) } } tv_modify_passwd -> {// 修改密码 diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AddFamilyActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AddFamilyActivity.kt index f0dd0dc37..14687a2b1 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AddFamilyActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/AddFamilyActivity.kt @@ -34,12 +34,8 @@ class AddFamilyActivity : BaseActivity(), MyCallback { override fun setListener() { iv_back.setOnClickListener { finish() } iv_set_location.setOnClickListener { - val intent = Intent(this, MarkerPaddingActivity::class.java) - startActivityForResult(intent, CommonField.MAP_LOCATION_REQ_CODE) } et_family_address.setOnClickListener { - val intent = Intent(this, MarkerPaddingActivity::class.java) - startActivityForResult(intent, CommonField.MAP_LOCATION_REQ_CODE) } btn_add_family.setOnClickListener { addFamily() } iv_set_family_name.setOnClickListener { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BaseActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BaseActivity.kt index e9de78b22..dbf4f3524 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BaseActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BaseActivity.kt @@ -9,17 +9,23 @@ import android.os.Bundle import android.text.TextUtils import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat +import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONObject import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.DataHolder import com.tencent.iot.explorer.link.T +import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.entity.User +import com.tencent.iot.explorer.link.core.auth.response.BaseResponse import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.core.utils.SharePreferenceUtil import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.dialog.TipShareDevDialog import com.tencent.iot.explorer.link.customview.status.StatusBarUtil import com.tencent.iot.explorer.link.kitlink.consts.CommonField +import com.tencent.iot.explorer.link.kitlink.entity.ProductEntity +import com.tencent.iot.explorer.link.kitlink.entity.ProductsEntity +import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import java.util.* /** @@ -247,8 +253,11 @@ abstract class BaseActivity : AppCompatActivity() { return true } - protected fun requestPermission(permissions: Array) { - ActivityCompat.requestPermissions(this, permissions, 102) + protected fun requestPermission( + permissions: Array, + requestCode: Int = DEFAULT_REQUEST_PERMISSIONS_CODE + ) { + ActivityCompat.requestPermissions(this, permissions, requestCode) } override fun onRequestPermissionsResult( @@ -257,7 +266,7 @@ abstract class BaseActivity : AppCompatActivity() { grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) - if (requestCode == 102) { + if (requestCode == DEFAULT_REQUEST_PERMISSIONS_CODE || requestCode == REQUEST_BLUETOOTH_PERMISSIONS_CODE) { for (i in permissions.indices) { if (grantResults[i] == PackageManager.PERMISSION_DENIED) { permissionDenied(permissions[i]) @@ -326,4 +335,34 @@ abstract class BaseActivity : AppCompatActivity() { jumpActivity(DeviceCategoryActivity::class.java) } + + interface OnTypeGeted { + fun onType(type: String) + } + + fun getDeviceType(productId: String, onTypeGeted: OnTypeGeted) { + var productsList = arrayListOf(productId) + HttpRequest.instance.deviceProducts(productsList, object: MyCallback { + override fun fail(msg: String?, reqCode: Int) {} + + override fun success(response: BaseResponse, reqCode: Int) { + if (response.isSuccess()) { + if (TextUtils.isEmpty(response.data.toString())) return + + var products = JSON.parseObject(response.data.toString(), ProductsEntity::class.java) + products?.Products?.let { + var product = JSON.parseObject(it.getString(0), ProductEntity::class.java) + product?.let { + onTypeGeted?.onType(it.NetType) + } + } + } + } + }) + } + + companion object{ + const val DEFAULT_REQUEST_PERMISSIONS_CODE = 102 //默认权限申请code码 + const val REQUEST_BLUETOOTH_PERMISSIONS_CODE = 104 //蓝牙权限申请code码 + } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleConfigHardwareActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleConfigHardwareActivity.kt index 57b9148c8..e99fdc9c9 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleConfigHardwareActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleConfigHardwareActivity.kt @@ -1,6 +1,5 @@ package com.tencent.iot.explorer.link.kitlink.activity -import android.app.Activity import android.content.Context import android.content.Intent import android.text.TextUtils @@ -17,10 +16,12 @@ import com.tencent.iot.explorer.link.customview.dialog.TipDialog import com.tencent.iot.explorer.link.customview.dialog.entity.TipInfo import com.tencent.iot.explorer.link.customview.progress.bean.StepBean import com.tencent.iot.explorer.link.kitlink.consts.CommonField +import com.tencent.iot.explorer.link.kitlink.consts.LoadViewTxtType import com.tencent.iot.explorer.link.kitlink.entity.ConfigType import com.tencent.iot.explorer.link.kitlink.entity.ProdConfigDetailEntity import com.tencent.iot.explorer.link.kitlink.response.ProductsConfigResponse import com.tencent.iot.explorer.link.kitlink.util.HttpRequest +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import kotlinx.android.synthetic.main.activity_ble_config_hardware.* import java.util.* @@ -28,23 +29,29 @@ import java.util.* class BleConfigHardwareActivity : PActivity() { companion object { - fun startWithProductid(context: Context, productId: String) { + fun startWithProductid(context: Context, productId: String, type: Int) { if (TextUtils.isEmpty(productId)) { T.show(context.getString(R.string.no_product_info)) return } var intent = Intent(context, BleConfigHardwareActivity::class.java) intent.putExtra(CommonField.PRODUCT_ID, productId) + intent.putExtra(CommonField.TYPE, type) var dev = BleDevice() dev.productId = productId dev.indexWithDevname = false App.data.bleDevice = dev context.startActivity(intent) } + + fun startWithProductid(context: Context, productId: String) { + startWithProductid(context, productId, 0) + } } private var productId = "" private var tipInfo = TipInfo() + private var type = 0 override fun getContentView(): Int { return R.layout.activity_ble_config_hardware @@ -53,8 +60,12 @@ class BleConfigHardwareActivity : PActivity() { private fun initStepView() { val stepsBeanList = ArrayList() stepsBeanList.add(StepBean(getString(R.string.config_hardware))) - stepsBeanList.add(StepBean(getString(R.string.set_target_wifi))) - stepsBeanList.add(StepBean(getString(R.string.start_config_network))) + if (type == 0) { + stepsBeanList.add(StepBean(getString(R.string.set_target_wifi))) + stepsBeanList.add(StepBean(getString(R.string.start_config_network))) + } else { + stepsBeanList.add(StepBean(getString(R.string.start_bind))) + } ble_step_progress.currentStep = 1 ble_step_progress.setStepViewTexts(stepsBeanList) ble_step_progress.setTextSize(12) @@ -65,10 +76,13 @@ class BleConfigHardwareActivity : PActivity() { tipInfo.title = getString(R.string.reset_ble_dev_way) tipInfo.content = getString(R.string.reset_ble_dev_content) sv_content.visibility = View.GONE - initStepView() if (intent.hasExtra(CommonField.PRODUCT_ID)) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() } + if (intent.hasExtra(CommonField.TYPE)) { + type = intent.getIntExtra(CommonField.TYPE, 0) + } + initStepView() if (!TextUtils.isEmpty(productId)) { // 尝试网络加载 loadViewInfo(productId) @@ -80,10 +94,22 @@ class BleConfigHardwareActivity : PActivity() { override fun setListener() { tv_ble_cancel.setOnClickListener { finish() } tv_ble_next.setOnClickListener { - val intent = Intent(this, WifiActivity::class.java) - intent.putExtra(CommonField.PRODUCT_ID, productId) - intent.putExtra(CommonField.CONFIG_TYPE, ConfigType.BleConfig.id) - startActivity(intent) + if (type == 0) { + val intent = Intent(this, WifiActivity::class.java) + intent.putExtra(CommonField.PRODUCT_ID, productId) + intent.putExtra(CommonField.CONFIG_TYPE, ConfigType.BleConfig.id) + startActivity(intent) + } else { + val intent = Intent(this, ConnectProgressActivity::class.java) + intent.putExtra(CommonField.LOAD_VIEW_TXT_TYPE, LoadViewTxtType.LoadRemoteViewTxt.ordinal) + intent.putExtra(CommonField.PRODUCT_ID, productId) + intent.putExtra(CommonField.CONFIG_TYPE, ConfigType.BleBindConfig.id) + intent.putExtra(CommonField.SSID, "") + intent.putExtra(CommonField.BSSID, "") + intent.putExtra(CommonField.PWD, "") + startActivity(intent) + } + } tv_more_guide.setOnClickListener { var dlg = TipDialog(this@BleConfigHardwareActivity, tipInfo) diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleOTADownloadActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleOTADownloadActivity.kt new file mode 100644 index 000000000..256166ad7 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BleOTADownloadActivity.kt @@ -0,0 +1,326 @@ +package com.tencent.iot.explorer.link.kitlink.activity + +import android.os.Handler +import android.os.Message +import android.text.TextUtils +import android.view.View +import com.alibaba.fastjson.JSONObject +import com.tencent.iot.explorer.link.R +import com.tencent.iot.explorer.link.core.auth.IoTAuth +import com.tencent.iot.explorer.link.core.auth.callback.MyCallback +import com.tencent.iot.explorer.link.core.auth.entity.DeviceEntity +import com.tencent.iot.explorer.link.core.auth.response.BaseResponse +import com.tencent.iot.explorer.link.core.link.entity.* +import com.tencent.iot.explorer.link.core.link.exception.TCLinkException +import com.tencent.iot.explorer.link.core.link.listener.BleDeviceConnectionListener +import com.tencent.iot.explorer.link.core.link.service.BleConfigService +import com.tencent.iot.explorer.link.core.log.L +import com.tencent.iot.explorer.link.mvp.IPresenter +import com.tencent.iot.explorer.link.retrofit.DownloadRequest +import com.tencent.iot.explorer.link.retrofit.DownloadRequest.OnDownloadListener +import kotlinx.android.synthetic.main.activity_ble_otadownload.* +import kotlinx.android.synthetic.main.menu_back_layout.* +import kotlinx.coroutines.* +import java.io.* + + +class BleOTADownloadActivity : PActivity(), CoroutineScope by MainScope() { + val MSG_DOWNLOAD_REFRESH = 0 + val MSG_DOWNLOAD_SUCCESS = 1 + val MSG_DOWNLOAD_FAILED = 2 + val MSG_UPLOAD_REFRESH = 3 + val MSG_UPLOAD_SUCCESS = 4 + val MSG_UPLOAD_FAILED = 5 + + private var TAG = BleOTADownloadActivity::class.java.simpleName + private var deviceEntity: DeviceEntity? = null + private var connectBleJob: Job? = null + + private var targetVersion: String? = null // 要升级到的ota版本 + @Volatile + private var downloadprogress: Int = 0// 下载进度值 + private var otaFilePath: String? = null // 下载固件时本地的存储路径 + + private var mtusize = 0 + @Volatile + private var uploadprogress: Int = 0// 写入进度值 + + override fun getPresenter(): IPresenter? { + return null + } + + override fun onResume() { + super.onResume() + BleConfigService.get().connetionListener = bleDeviceConnectionListener + } + + override fun getContentView(): Int { + return R.layout.activity_ble_otadownload + } + + override fun initView() { + tv_title.text = getString(R.string.firmware_update) + stopScanBleDev(true) + deviceEntity = get("device") + mtusize = get("mtusize") ?: 0 + downloadDeviceOTAInfo() + } + + override fun setListener() { + iv_back.setOnClickListener { finish() } + tv_finish.setOnClickListener { finish() } + } + + + private fun startScanBleDev() { + ble_connect_layout.visibility = View.VISIBLE + BleConfigService.get().startScanBluetoothDevices() + search_ble_dev_layout.visibility = View.VISIBLE + search_reault_layout.visibility = View.GONE + } + + private fun stopScanBleDev(connected: Boolean) { + launch(Dispatchers.Main) { + ble_connect_layout.visibility = View.VISIBLE + BleConfigService.get().stopScanBluetoothDevices() + search_ble_dev_layout.visibility = View.GONE + search_reault_layout.visibility = View.VISIBLE + if (connected) { + search_reault_layout.setBackgroundResource(R.color.blue_006EFF) + retry_connect.setTextColor(this@BleOTADownloadActivity.resources.getColor(R.color.white)) + retry_connect.setText(R.string.break_ble_connect) + retry_connect.setOnClickListener { + BleConfigService.get().bluetoothGatt?.let { + it?.close() + stopScanBleDev(false) + } + } + } else { + search_reault_layout.setBackgroundResource(R.color.red_E65A59) + retry_connect.setTextColor(this@BleOTADownloadActivity.resources.getColor(R.color.white)) + retry_connect.setText(R.string.scanning_retry) + retry_connect.setOnClickListener { startScanBleDev() } + } + } + } + + private var bleDeviceConnectionListener = object: BleDeviceConnectionListener { + override fun onBleDeviceFounded(bleDevice: BleDevice) { + if (bleDevice.productId == deviceEntity?.ProductId && !TextUtils.isEmpty(bleDevice.productId)) { + //&& bleDevice.devName == deviceEntity?.DeviceName) { + BleConfigService.get().bluetoothGatt = BleConfigService.get().connectBleDeviceAndGetLocalPsk(bleDevice, deviceEntity?.ProductId, deviceEntity?.DeviceName) + } else if (!TextUtils.isEmpty(bleDevice.bindTag)) { + deviceEntity?.let { + if (bleDevice.bindTag == BleConfigService.bytesToHex(BleConfigService.getBindTag(it.ProductId, it.DeviceName))) { + BleConfigService.get().bluetoothGatt = BleConfigService.get().connectBleDeviceAndGetLocalPsk(bleDevice, deviceEntity?.ProductId, deviceEntity?.DeviceName) + } + } + } + } + + override fun onBleDeviceConnected() { + launch { + BleConfigService.get().bluetoothGatt?.let { + delay(3000) +// if (BleConfigService.get().setMtuSize(it, 512)) return@launch + launch (Dispatchers.Main) { + delay(1000) + BleConfigService.get().bluetoothGatt?.let { + BleConfigService.get().stopScanBluetoothDevices() + if (!BleConfigService.get().connectSubDevice(it)) { + stopScanBleDev(false) + return@launch + } else { + + } + } + } + } + } + } + override fun onBleDeviceDisconnected(exception: TCLinkException) { + stopScanBleDev(false) + launch (Dispatchers.Main) { + startScanBleDev() + } + } + override fun onBleDeviceInfo(bleDeviceInfo: BleDeviceInfo) {} + override fun onBleSetWifiModeResult(success: Boolean) {} + override fun onBleSendWifiInfoResult(success: Boolean) {} + override fun onBleWifiConnectedInfo(wifiConnectInfo: BleWifiConnectInfo) {} + override fun onBlePushTokenResult(success: Boolean) {} + override fun onMtuChanged(mtu: Int, status: Int) { + L.d(TAG, "onMtuChanged mtu $mtu status $status") + } + override fun onBleBindSignInfo(bleDevBindCondition: BleDevBindCondition) {} + override fun onBleSendSignInfo(bleDevSignResult: BleDevSignResult) { + stopScanBleDev(true) + connectBleJob?.cancel() + } + override fun onBleUnbindSignInfo(signInfo: String) {} + override fun onBlePropertyValue(bleDeviceProperty: BleDeviceProperty) {} + override fun onBleControlPropertyResult(result: Int) {} + override fun onBleRequestCurrentProperty() {} + override fun onBleNeedPushProperty(eventId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleReportActionResult(reason: Int, actionId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleDeviceFirmwareVersion(firmwareVersion: BleDeviceFirmwareVersion) { + if (firmwareVersion.mtuFlag == 1) { // 是否设置 mtu 当 mtu flag为 1 时,进行 MTU 设置;当 mtu flag 为 0 时,不设置 MTU + BleConfigService.get().setMtuSize(BleConfigService.get().bluetoothGatt, firmwareVersion.mtuSize) + } + if (targetVersion.equals(firmwareVersion.version)) { //ble设备重启后,firmware版本和目标版本一致,判定ota升级成功 + launch(Dispatchers.Main) { + L.d(TAG, "ota升级成功~") + rl_ota_progress.visibility = View.GONE + rl_ota_success.visibility = View.VISIBLE + tv_finish.visibility = View.VISIBLE + tv_upgrade_success_version.text = getString(R.string.firmware_update_success_version, targetVersion) + } + } + } + + override fun onBleDevOtaUpdateResponse(otaUpdateResponse: BleDevOtaUpdateResponse) { + L.d(TAG, "onBleDevOtaUpdateResponse") + if (otaUpdateResponse.allowUpdate) { + otaFilePath?.let { + BleConfigService.get().sendOtaFirmwareData(BleConfigService.get().bluetoothGatt, + it, otaUpdateResponse) + } + } + } + + override fun onBleDevOtaUpdateResult(success: Boolean, errorCode: Int) { + if (success) { + handler.sendEmptyMessage(MSG_UPLOAD_SUCCESS) + } else { + handler.sendEmptyMessage(MSG_UPLOAD_FAILED) + } + } + + override fun onBleDevOtaReceivedProgressResponse(progress: Int) { + L.d(TAG,"progress:${progress}") + uploadprogress = progress + refreshProgress(false) + reportProgress2Cloud(progress, "updating") + } + + override fun onBleDeviceMtuSize(size: Int) {} + override fun onBleDeviceTimeOut(timeLong: Int) {} + } + + // 上报设备OTA状态进度 (下载、更新升级、烧录) State: downloading updating burning + private fun reportProgress2Cloud(progress: Int, state: String) { + targetVersion?.let { + IoTAuth.deviceImpl.reportOTAStatus("${deviceEntity?.ProductId}/${deviceEntity?.DeviceName}", state, + it, progress, object: MyCallback { + override fun fail(msg: String?, reqCode: Int) {} + + override fun success(response: BaseResponse, reqCode: Int) {} + }) + } + } + + private fun downloadDeviceOTAInfo() { + IoTAuth.deviceImpl.getDeviceOTAInfo("${deviceEntity?.ProductId}/${deviceEntity?.DeviceName}", object: MyCallback { + override fun fail(msg: String?, reqCode: Int) { + + } + + override fun success(response: BaseResponse, reqCode: Int) { + val json = response.data as JSONObject + val firmwareURL = json.getString("FirmwareURL") + if (firmwareURL != null) { + targetVersion = json.getString("TargetVersion") +// val uploadVersion = json.getString("UploadVersion") + if (TextUtils.isEmpty(otaFilePath)) { + otaFilePath = cacheDir.absolutePath + "/${deviceEntity?.ProductId}_${deviceEntity?.DeviceName}_${targetVersion}" + } + downloadOtaFirmware(firmwareURL) + } + } + }) + } + + private fun downloadOtaFirmware(url: String) { + DownloadRequest.get().download(url, otaFilePath, downloadlistener) + } + + + var downloadlistener: OnDownloadListener = object : OnDownloadListener { + override fun onDownloadSuccess(requestId: String) { + handler.sendEmptyMessage(MSG_DOWNLOAD_SUCCESS) + } + + override fun onDownloading(requestId: String, progress: Int) { + downloadprogress = progress + refreshProgress(true) + reportProgress2Cloud(progress, "downloading") + } + + override fun onDownloadFailed(requestId: String) { + handler.sendEmptyMessage(MSG_DOWNLOAD_FAILED) + } + } + + + var handler: Handler = object : Handler() { + override fun handleMessage(msg: Message) { + super.handleMessage(msg) + when(msg.what) { + MSG_DOWNLOAD_REFRESH -> { + if (progress_download != null) { + progress_download.progress = downloadprogress.toFloat() + tv_upgradeing.text = getString(R.string.firmware_upgrading_tip, downloadprogress) + "%" + } + if (downloadprogress < 100) { + L.d(TAG, "downloadprogress:${downloadprogress}, progressBar.max:${progress_download?.max}") +// onDismisListener.onDownloadProgress(count, progressBar.max as Int) + } + } + MSG_DOWNLOAD_SUCCESS -> { + progress_download?.progress = 100f + L.d(TAG, "onDownloadSuccess:${otaFilePath}") + reportProgress2Cloud(100, "downloading") + otaFilePath?.let { + targetVersion?.let { it1 -> + BleConfigService.get().requestOTAInfo(BleConfigService.get().bluetoothGatt, + it, it1 + ) + } + } + } + MSG_DOWNLOAD_FAILED -> { + L.d(TAG, "onDownloadFailed:${otaFilePath}") + } + MSG_UPLOAD_REFRESH -> { + if (progress_download != null) { + progress_download.progress = uploadprogress.toFloat() + tv_upgradeing.text = getString(R.string.firmware_writing, uploadprogress) + "%" + } + if (downloadprogress < 100) { + L.d(TAG, "uploadprogress:${uploadprogress}, progressBar.max:${progress_download?.max}") + } + } + MSG_UPLOAD_SUCCESS -> { + progress_download?.progress = 100f + L.d(TAG, "onUploadSuccess:${otaFilePath}") + reportProgress2Cloud(100, "updating") + } + MSG_UPLOAD_FAILED -> { + L.d(TAG, "onUpFailed:${otaFilePath}") + } + } + } + } + + private fun refreshProgress(download: Boolean) { //通过url下载固件文件,还是写入固件给ble设备 + // 进度在 100 以内允许刷新 + if (downloadprogress <= 100) { + if (download) { + handler.sendEmptyMessage(MSG_DOWNLOAD_REFRESH) + } else { + handler.sendEmptyMessage(MSG_UPLOAD_REFRESH) + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BuleToothActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BuleToothActivity.kt index 6a3b02d9c..79f8854fb 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BuleToothActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/BuleToothActivity.kt @@ -65,9 +65,9 @@ class BuleToothActivity: BaseActivity(), View.OnClickListener, MyCallback { val ticketResponse = JSON.parse(response.data.toString()) as JSONObject var url = H5_PANEL_BASE_URL + "?productId=${productId}" + - "&uin=${Utils.getAndroidID(this)}" + + "&uin=${App.uuid}" + "&lid=${App.data.appLifeCircleId}" + - "&quid=${Utils.getAndroidID(this)}" + + "&quid=${App.uuid}" + "&ticket=${ticketResponse[CommonField.TOKEN_TICKET]}" + "&appID=${T.getContext().applicationInfo.packageName}" + "&platform=${HttpRequest.PLATFORM_TAG}" + diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ChooseCountryActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ChooseCountryActivity.kt index d1ad5e5a7..1c25c3c14 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ChooseCountryActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ChooseCountryActivity.kt @@ -34,8 +34,7 @@ class ChooseCountryActivity : PActivity(), ChooseCountryView, View.OnClickListen override fun initView() { tv_title.text = getString(R.string.country_or_place) presenter = ChooseCountryPresenter(this) - loadLastCountryInfo() - showBirthDayDlg() +// loadLastCountryInfo() } override fun setListener() { @@ -50,6 +49,51 @@ class ChooseCountryActivity : PActivity(), ChooseCountryView, View.OnClickListen startActivityForResult(Intent(this, RegionActivity::class.java), 100) } btn_bind_get_code -> { + + if (tv_register_to_country.text == resources.getString(R.string.please_choose)) { + T.show(resources.getString(R.string.please_choose) + resources.getString(R.string.country_or_place)) + return + } + val countryCode = presenter.getCountryCode() + var lastTimeJson: String? + if (countryCode == "1") { + lastTimeJson = Utils.getStringValueFromXml(this@ChooseCountryActivity, CommonField.USA_USER_REG_TIME_INFO, CommonField.USA_USER_REG_TIME_INFO) + } else if (countryCode == "86") { + lastTimeJson = Utils.getStringValueFromXml(this@ChooseCountryActivity, CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO, CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO) + } else { + return + } + + // 不存在上一次的注册信息 + if (TextUtils.isEmpty(lastTimeJson) || lastTimeJson == "{}") { + T.show(resources.getString(R.string.please_choose) + resources.getString(R.string.country_or_place)) + return + } + var json = JSONObject.parseObject(lastTimeJson) + + var tagYear = 0 + var tagMonth = 0 + var tagDay = 0 + if (countryCode == "1") { + tagYear = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_YEAR) + tagMonth = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_MONTH) + tagDay = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_DAY) + } else if (countryCode == "86") { + tagYear = json.getIntValue(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_YEAR) + tagMonth = json.getIntValue(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_MONTH) + tagDay = json.getIntValue(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_DAY) + } + + // 是否满age周岁 + if (!ifOverAge(countryCode, tagYear, tagMonth, tagDay)) { + if (countryCode == "1") { + T.show(resources.getString(R.string.usa_too_young_to_use)) + } else if (countryCode == "86") { + T.show(resources.getString(R.string.mainland_too_young_to_use)) + } + return + } + Intent(this, RegisterActivity::class.java).run { startActivity(this) } @@ -61,23 +105,40 @@ class ChooseCountryActivity : PActivity(), ChooseCountryView, View.OnClickListen tv_register_to_country.text = countryName + getString(R.string.conutry_code_num, countryCode) } - private fun shouldShowBirthdayDlg(): Boolean { - var lastTimeJson = Utils.getStringValueFromXml(this@ChooseCountryActivity, CommonField.USA_USER_REG_TIME_INFO, CommonField.USA_USER_REG_TIME_INFO) + private fun shouldShowBirthdayDlg(countryCode: String): Boolean { + var lastTimeJson: String? + if (countryCode == "1") { + lastTimeJson = Utils.getStringValueFromXml(this@ChooseCountryActivity, CommonField.USA_USER_REG_TIME_INFO, CommonField.USA_USER_REG_TIME_INFO) + } else if (countryCode == "86") { + lastTimeJson = Utils.getStringValueFromXml(this@ChooseCountryActivity, CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO, CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO) + } else { + return false + } + // 不存在上一次的注册信息 if (TextUtils.isEmpty(lastTimeJson) || lastTimeJson == "{}") return true var json = JSONObject.parseObject(lastTimeJson) - var currentDate = Date() - var currentYear = currentDate.year + 1900 - var currentMonth = currentDate.month + 1 - var currentDay = currentDate.day - var tagYear = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_YEAR) - var tagMonth = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_MONTH) - var tagDay = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_DAY) - if (currentYear - tagYear > 0 && currentMonth - tagMonth == 0 && currentDay - tagDay == 0) { // 满周年 - return true + var tagYear = 0 + var tagMonth = 0 + var tagDay = 0 + if (countryCode == "1") { + tagYear = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_YEAR) + tagMonth = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_MONTH) + tagDay = json.getIntValue(CommonField.USA_USER_REG_TIME_INFO_DAY) + } else if (countryCode == "86") { + tagYear = json.getIntValue(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_YEAR) + tagMonth = json.getIntValue(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_MONTH) + tagDay = json.getIntValue(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_DAY) } + if (!ifOverAge(countryCode, tagYear, tagMonth, tagDay)) { + if (countryCode == "1") { + T.show(resources.getString(R.string.usa_too_young_to_use)) + } else if (countryCode == "86") { + T.show(resources.getString(R.string.mainland_too_young_to_use)) + } + } return false } @@ -95,30 +156,39 @@ class ChooseCountryActivity : PActivity(), ChooseCountryView, View.OnClickListen } private fun showBirthDayDlg() { - if (presenter.getCountryCode() == "1" && shouldShowBirthdayDlg()) { - var dlg = InputBirthdayDialog(this@ChooseCountryActivity) + val countryCode = presenter.getCountryCode() + if (shouldShowBirthdayDlg(countryCode)) { + var dlg = InputBirthdayDialog(this@ChooseCountryActivity, countryCode) dlg.show() dlg.setOnDismissListener(object: InputBirthdayDialog.OnDismisListener { override fun onOkClicked(year: Int, month: Int, day: Int) { - // 是否满13周岁 - if (!ifOver13YearsOld(year, month, day)) { - T.show(resources.getString(R.string.too_young_to_use)) - finish() - return + // 是否满age周岁 + if (!ifOverAge(countryCode, year, month, day)) { + if (countryCode == "1") { + T.show(resources.getString(R.string.usa_too_young_to_use)) + } else if (countryCode == "86") { + T.show(resources.getString(R.string.mainland_too_young_to_use)) + } } var timeJson = JSONObject() - var currentDate = Date() - var currentYear = currentDate.year + 1900 - var currentMonth = currentDate.month + 1 - var currentDay = currentDate.day - // 记录本次使用的日期 - timeJson.put(CommonField.USA_USER_REG_TIME_INFO_YEAR, currentYear) - timeJson.put(CommonField.USA_USER_REG_TIME_INFO_MONTH, currentMonth) - timeJson.put(CommonField.USA_USER_REG_TIME_INFO_DAY, currentDay) - Utils.setXmlStringValue(T.getContext(), CommonField.USA_USER_REG_TIME_INFO, - CommonField.USA_USER_REG_TIME_INFO, timeJson.toJSONString()) + + if (countryCode == "1") { + // 记录本次使用的日期 + timeJson.put(CommonField.USA_USER_REG_TIME_INFO_YEAR, year) + timeJson.put(CommonField.USA_USER_REG_TIME_INFO_MONTH, month) + timeJson.put(CommonField.USA_USER_REG_TIME_INFO_DAY, day) + Utils.setXmlStringValue(T.getContext(), CommonField.USA_USER_REG_TIME_INFO, + CommonField.USA_USER_REG_TIME_INFO, timeJson.toJSONString()) + } else if (countryCode == "86") { + // 记录本次使用的日期 + timeJson.put(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_YEAR, year) + timeJson.put(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_MONTH, month) + timeJson.put(CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO_DAY, day) + Utils.setXmlStringValue(T.getContext(), CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO, + CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO, timeJson.toJSONString()) + } } override fun onCancelClicked() { finish() } @@ -126,13 +196,21 @@ class ChooseCountryActivity : PActivity(), ChooseCountryView, View.OnClickListen } } - private fun ifOver13YearsOld(year: Int, month: Int, day: Int): Boolean { + private fun ifOverAge(countryCode: String, year: Int, month: Int, day: Int): Boolean { + + var age = 0 + if (countryCode == "1") { + age = 13 + } else if (countryCode == "86") { + age = 18 + } + var currentDate = Date() var currentYear = currentDate.year + 1900 var currentMonth = currentDate.month + 1 var currentDay = currentDate.day - if (currentYear - year < 13 || (currentYear - year == 13 && currentMonth - month < 0) || - (currentYear - year == 13 && currentMonth - month == 0 && currentDay - day < 0)) { + if (currentYear - year < age || (currentYear - year == age && currentMonth - month < 0) || + (currentYear - year == age && currentMonth - month == 0 && currentDay - day < 0)) { return false } return true diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CommentDetailsActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CommentDetailsActivity.kt index 0cfc7a2bc..6e1436c60 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CommentDetailsActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CommentDetailsActivity.kt @@ -11,6 +11,7 @@ import android.widget.LinearLayout import android.widget.ProgressBar import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONObject +import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.auth.callback.MyCallback @@ -113,7 +114,7 @@ class CommentDetailsActivity: BaseActivity(), View.OnClickListener, MyCallback { return } var js = JSON.parse(response.data.toString()) as JSONObject - var weburl = CommonField.H5_BASE_URL + "?uin=${Utils.getAndroidID(this)}#" + + var weburl = CommonField.H5_BASE_URL + "?uin=${App.uuid}#" + pathUrl + "&ticket=${js[CommonField.TOKEN_TICKET]}" + "&lang=${Utils.getLang()}" comment_detail_web.loadUrl(weburl) } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CompleteTaskInfoActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CompleteTaskInfoActivity.kt index d5bdc766a..9fe04c145 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CompleteTaskInfoActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/CompleteTaskInfoActivity.kt @@ -22,6 +22,7 @@ import com.tencent.iot.explorer.link.kitlink.entity.RouteType import com.tencent.iot.explorer.link.kitlink.entity.SceneEntity import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.Utils +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_complete_task_info.* import kotlinx.android.synthetic.main.menu_back_layout.* @@ -233,11 +234,11 @@ class CompleteTaskInfoActivity : BaseActivity(),MyCallback { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CommonField.REQUEST_PIC_REQ_CODE && // 添加任务 resultCode == Activity.RESULT_OK && data != null) { - var picUrl = data?.getStringExtra(CommonField.EXTRA_PIC_URL) + val picUrl = data.getStringExtra(CommonField.EXTRA_PIC_URL).safe() smartPicUrl = picUrl } else if (requestCode == CommonField.REQUEST_TASK_NAME_REQ_CODE && // 添加任务 resultCode == Activity.RESULT_OK && data != null) { - var name = data?.getStringExtra(CommonField.EXYRA_TASK_NAME) + val name = data.getStringExtra(CommonField.EXYRA_TASK_NAME).safe() smartName = name.trim() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetFailedActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetFailedActivity.kt index c0c650ac9..912e01651 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetFailedActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetFailedActivity.kt @@ -3,6 +3,7 @@ package com.tencent.iot.explorer.link.kitlink.activity import android.content.Intent import android.view.View import com.tencent.iot.explorer.link.R +import com.tencent.iot.explorer.link.core.link.service.LLSyncErrorCode import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.entity.ConfigType import kotlinx.android.synthetic.main.activity_config_net_failed.* @@ -10,6 +11,7 @@ import kotlinx.android.synthetic.main.activity_config_net_failed.* class ConfigNetFailedActivity : BaseActivity() { var type = ConfigType.SmartConfig.id var productId = "" + var bleErrorCode = "" override fun getContentView(): Int { return R.layout.activity_config_net_failed @@ -18,6 +20,7 @@ class ConfigNetFailedActivity : BaseActivity() { override fun initView() { type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SmartConfig.id) productId = intent.getStringExtra(CommonField.PRODUCT_ID) ?: "" + bleErrorCode = intent.getStringExtra(CommonField.CONFIG_NET_ERROR_CODE) ?: "" when (type) { @@ -35,7 +38,7 @@ class ConfigNetFailedActivity : BaseActivity() { ConfigType.BleConfig.id -> { tv_config_net_failed_title.setText(getString(R.string.ble_config_network)) - tv_config_net_failed_reason.setText("") + tv_config_net_failed_reason.setText(getErrorTipMessage(bleErrorCode)) tv_soft_first_commit.visibility = View.GONE tv_soft_first_commit.setText("") tv_more_reason.visibility = View.GONE @@ -43,6 +46,63 @@ class ConfigNetFailedActivity : BaseActivity() { } } + private fun getErrorTipMessage(code: String): String { + when (code) { + LLSyncErrorCode.WIFI_CONFIG_TIMEOUT_ERROR_CODE -> { + return "设备配网超时" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_DISCONNECT_ERROR_CODE -> { + return "设备和App断开蓝牙连接" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_SET_MTU_ERROR_CODE -> { + return "设置MTU失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_MODE_ERROR_CODE -> { + return "设置设备WiFi模式失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_MODE_RESPONSE_ERROR_CODE -> { + return "设备反馈设置WiFi模式失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_INFO_ERROR_CODE -> { + return "向设备发送WIFI信息失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_REQUEST_WIFI_CONNECT_ERROR_CODE -> { + return "请求设备连接wifi失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_WIFI_CONNECT_RESPONSE_ERROR_CODE -> { + return "设备反馈连接wifi失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_REQUEST_BIND_TOKEN_ERROR_CODE -> { + return "请求设备绑定token失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_TOKEN_RESPONSE_ERROR_CODE -> { + return "设备反馈绑定token失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_SET_MTU_RESPONSE_ERROR_CODE -> { + return "设备反馈设置MTU失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_REQUEST_GET_DEVICE_INFO_ERROR_CODE -> { + return "获取蓝牙设备信息失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_BLE_DEVICE_NET_ERROR_CODE -> { + return "绑定蓝牙设备接口服务失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_BLE_DEVICE_NET_OTHER_ERROR_CODE -> { + return "绑定蓝牙设备接口失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_BLE_DEVICE_RESPONSE_ERROR_CODE -> { + return "向蓝牙设备发送绑定设备结果失败" + } + LLSyncErrorCode.WIFI_CONFIG_BLE_PARAMS_ERROR_CODE -> { + return "解析数据失败" + } + LLSyncErrorCode.PURE_BLE_SET_UNIX_TIMESTAMP_NONCE_ERROR_CODE -> { + return "设置unix和随机串失败" + } + } + return "" + } + override fun setListener() { tv_soft_first_commit.setOnClickListener(listener) tv_retry.setOnClickListener(listener) diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetSuccessActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetSuccessActivity.kt index ec9d54893..ad3238e1f 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetSuccessActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConfigNetSuccessActivity.kt @@ -6,6 +6,7 @@ import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.entity.ConfigType import com.tencent.iot.explorer.link.kitlink.util.Utils +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_config_net_success.* class ConfigNetSuccessActivity : BaseActivity() { @@ -18,7 +19,7 @@ class ConfigNetSuccessActivity : BaseActivity() { override fun initView() { type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SmartConfig.id) - deviceName = intent.getStringExtra(CommonField.DEVICE_NAME) + deviceName = intent.getStringExtra(CommonField.DEVICE_NAME).safe() var str2Show = resources.getString(R.string.device_name) + resources.getString(R.string.splite_from_name) + deviceName tv_config_net_sucess_reason_tip.setText(str2Show) diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConnectProgressActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConnectProgressActivity.kt index 8f0336036..342a962d4 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConnectProgressActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ConnectProgressActivity.kt @@ -1,28 +1,26 @@ package com.tencent.iot.explorer.link.kitlink.activity -import android.bluetooth.BluetoothDevice import android.content.Intent -import android.util.Log import android.view.animation.Animation import android.view.animation.LinearInterpolator import android.view.animation.RotateAnimation +import com.alibaba.fastjson.JSONObject import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.link.entity.BleConfigStep import com.tencent.iot.explorer.link.core.link.entity.SmartConfigStep import com.tencent.iot.explorer.link.core.link.entity.SoftAPStep +import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.progress.bean.StepBean import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.entity.ConfigType import com.tencent.iot.explorer.link.kitlink.popup.CommonPopupWindow +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import com.tencent.iot.explorer.link.mvp.presenter.ConnectPresenter import com.tencent.iot.explorer.link.mvp.view.ConnectView import kotlinx.android.synthetic.main.activity_connect_progress.* -import kotlinx.android.synthetic.main.activity_connect_progress.softap_step_progress -import kotlinx.android.synthetic.main.activity_connect_progress.tv_soft_ap_cancel -import kotlinx.android.synthetic.main.activity_connect_progress.tv_soft_ap_title import java.util.* class ConnectProgressActivity : PActivity(), ConnectView { @@ -62,6 +60,14 @@ class ConnectProgressActivity : PActivity(), ConnectView { softap_step_progress.currentStep = 4 softap_step_progress.setStepViewTexts(stepsBeanList) softap_step_progress.setTextSize(12) + } else if (type == ConfigType.BleBindConfig.id) { + tv_soft_ap_title.setText(R.string.ble_config_network) + val stepsBeanList = ArrayList() + stepsBeanList.add(StepBean(getString(R.string.config_hardware))) + stepsBeanList.add(StepBean(getString(R.string.start_bind))) + softap_step_progress.currentStep = 2 + softap_step_progress.setStepViewTexts(stepsBeanList) + softap_step_progress.setTextSize(12) } else { if (type == ConfigType.SmartConfig.id) { tv_soft_ap_title.setText(R.string.smart_config_config_network) @@ -105,13 +111,13 @@ class ConnectProgressActivity : PActivity(), ConnectView { productId = intent.getStringExtra(CommonField.PRODUCT_ID) ?: "" type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SmartConfig.id) if (intent.hasExtra(CommonField.SSID)) { - ssid = intent.getStringExtra(CommonField.SSID) + ssid = intent.getStringExtra(CommonField.SSID).safe() } if (intent.hasExtra(CommonField.BSSID)) { - bssid = intent.getStringExtra(CommonField.BSSID) + bssid = intent.getStringExtra(CommonField.BSSID).safe() } if (intent.hasExtra(CommonField.PWD)) { - wifiPassword = intent.getStringExtra(CommonField.PWD) + wifiPassword = intent.getStringExtra(CommonField.PWD).safe() } refreshTypeView() @@ -202,6 +208,12 @@ class ConnectProgressActivity : PActivity(), ConnectView { tv_init_success.setTextColor(resources.getColor(R.color.black_15161A)) App.data.setRefreshLevel(2) + // 记录连接成功的wifi ssid和pwd + var json = JSONObject() + json.put(CommonField.WIFI_SSID, ssid) + json.put(CommonField.WIFI_PWD, wifiPassword) + Utils.setXmlStringValue(T.getContext(), CommonField.WIFI_INFO, CommonField.WIFI_INFO, json.toJSONString()) + var successIntent = Intent(this, ConfigNetSuccessActivity::class.java) successIntent.putExtra(CommonField.CONFIG_TYPE, type) if (presenter.model?.deviceInfo?.deviceName != null) { @@ -295,7 +307,7 @@ class ConnectProgressActivity : PActivity(), ConnectView { } override fun deviceConnectToWifiFail() { - showfailedReason() + showfailedReason("") } override fun softApConnectToWifiFail(ssid: String) { @@ -305,7 +317,7 @@ class ConnectProgressActivity : PActivity(), ConnectView { } override fun connectFail(code: String, message: String) { - showfailedReason() + showfailedReason(code) } override fun onBackPressed() { @@ -336,13 +348,14 @@ class ConnectProgressActivity : PActivity(), ConnectView { } } - private fun showfailedReason() { + private fun showfailedReason(code: String) { run { runOnUiThread { if (!quit) { var failedIntent = Intent(this, ConfigNetFailedActivity::class.java) failedIntent.putExtra(CommonField.CONFIG_TYPE, type) failedIntent.putExtra(CommonField.PRODUCT_ID, productId) + failedIntent.putExtra(CommonField.CONFIG_NET_ERROR_CODE, code) startActivity(failedIntent) backToDeviceCategoryActivity() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPanelActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPanelActivity.kt index 2d3521ba0..3432c776c 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPanelActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPanelActivity.kt @@ -1,40 +1,68 @@ package com.tencent.iot.explorer.link.kitlink.activity +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.ConnectivityManager +import android.net.wifi.WifiManager +import android.os.Build +import android.text.TextUtils import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.annotation.RequiresApi +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.alibaba.fastjson.JSONObject import com.tencent.iot.explorer.link.App +import com.tencent.iot.explorer.link.P2PAppSessionManager import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.TRTCAppSessionManager +import com.tencent.iot.explorer.link.core.auth.IoTAuth +import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.entity.DeviceEntity import com.tencent.iot.explorer.link.core.auth.entity.NavBar import com.tencent.iot.explorer.link.core.auth.message.MessageConst +import com.tencent.iot.explorer.link.core.auth.response.BaseResponse +import com.tencent.iot.explorer.link.core.link.entity.* +import com.tencent.iot.explorer.link.core.link.exception.TCLinkException +import com.tencent.iot.explorer.link.core.link.listener.BleDeviceConnectionListener +import com.tencent.iot.explorer.link.core.link.service.BleConfigService +import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.customview.recyclerview.CRecyclerView +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.TRTCAudioCallActivity +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.utils.NetWorkStateReceiver +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.TRTCVideoCallActivity +import com.tencent.iot.explorer.link.kitlink.activity.videoui.ParamSettingActivity +import com.tencent.iot.explorer.link.kitlink.activity.videoui.RecordVideoActivity +import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.entity.DevicePropertyEntity +import com.tencent.iot.explorer.link.kitlink.popup.CommonPopupWindow import com.tencent.iot.explorer.link.kitlink.popup.EnumPopupWindow import com.tencent.iot.explorer.link.kitlink.popup.NumberPopupWindow import com.tencent.iot.explorer.link.kitlink.popup.OfflinePopupWindow import com.tencent.iot.explorer.link.kitlink.theme.PanelThemeManager import com.tencent.iot.explorer.link.kitlink.util.StatusBarUtil +import com.tencent.iot.explorer.link.kitlink.util.VideoUtils import com.tencent.iot.explorer.link.mvp.IPresenter import com.tencent.iot.explorer.link.mvp.presenter.ControlPanelPresenter import com.tencent.iot.explorer.link.mvp.view.ControlPanelView import com.tencent.iot.explorer.link.rtc.model.RoomKey import com.tencent.iot.explorer.link.rtc.model.TRTCUIManager -import com.tencent.iot.explorer.link.rtc.ui.audiocall.TRTCAudioCallActivity -import com.tencent.iot.explorer.link.rtc.ui.utils.NetWorkStateReceiver -import com.tencent.iot.explorer.link.rtc.ui.videocall.TRTCVideoCallActivity +import com.tencent.xnet.XP2P import kotlinx.android.synthetic.main.activity_control_panel.* import kotlinx.android.synthetic.main.menu_back_and_right.* import kotlinx.android.synthetic.main.menu_back_layout.* import kotlinx.coroutines.* +import java.util.* /** * 控制面板 */ -class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.RecyclerItemView { - +class ControlPanelActivity : PActivity(), CoroutineScope by MainScope(), ControlPanelView, CRecyclerView.RecyclerItemView, + NetWorkStateReceiver.NetworkStateReceiverListener { + private var TAG = ControlPanelActivity::class.java.simpleName private var deviceEntity: DeviceEntity? = null private lateinit var presenter: ControlPanelPresenter @@ -44,9 +72,11 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl private var enumPopup: EnumPopupWindow? = null private var offlinePopup: OfflinePopupWindow? = null private var job: Job? = null - + private var connectBleJob: Job? = null private var netWorkStateReceiver: NetWorkStateReceiver? = null + private var mtusize = 0 + override fun getContentView(): Int { return R.layout.activity_control_panel } @@ -56,16 +86,35 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl } override fun onResume() { + netWorkStateReceiver?.run { + registerReceiver( + netWorkStateReceiver, + IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) + ) + } super.onResume() tv_title.text = deviceEntity?.getAlias() presenter.requestDeviceData() presenter.getUserSetting() + BleConfigService.get().connetionListener = bleDeviceConnectionListener + } + + override fun onPause() { + if (deviceEntity?.CategoryId != 567) { //非双向 + netWorkStateReceiver?.run { + unregisterReceiver(netWorkStateReceiver) + } + } + super.onPause() } override fun initView() { // App.setEnableEnterRoomCallback(false) presenter = ControlPanelPresenter(this) netWorkStateReceiver = NetWorkStateReceiver() + val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) + registerReceiver(netWorkStateReceiver, filter) + netWorkStateReceiver!!.addListener(this) deviceEntity = get("device") deviceEntity?.run { presenter.setProductId(ProductId) @@ -77,6 +126,15 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl crv_panel.addRecyclerItemView(this@ControlPanelActivity) presenter.requestControlPanel() presenter.registerActivePush() + getDeviceType(ProductId, object: OnTypeGeted { + override fun onType(type: String) { + if (type == "ble") { + launch (Dispatchers.Main) { + startScanBleDev() + } + } + } + }) if (online != 1) {//延时显示 job = CoroutineScope(Dispatchers.IO).launch { @@ -87,6 +145,170 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl } } } + registVideoOverBrodcast() + } + + /** + * xxxxxxxx 转成 xxx.xxx.xxx.xxx + * int转化为ip地址 + */ + private fun intToIp(paramInt: Int): String { + return ((paramInt and 0xFF).toString() + "." + (0xFF and (paramInt shr 8)) + "." + + (0xFF and (paramInt shr 16)) + "." + (0xFF and (paramInt shr 24))) + } + + private fun startScanBleDev() { + ble_connect_layout.visibility = View.VISIBLE + BleConfigService.get().startScanBluetoothDevices() + search_ble_dev_layout.visibility = View.VISIBLE + search_reault_layout.visibility = View.GONE + } + + private fun stopScanBleDev(connected: Boolean) { + launch(Dispatchers.Main) { + ble_connect_layout.visibility = View.VISIBLE + BleConfigService.get().stopScanBluetoothDevices() + search_ble_dev_layout.visibility = View.GONE + search_reault_layout.visibility = View.VISIBLE + if (connected) { + search_reault_layout.setBackgroundResource(R.color.blue_006EFF) + retry_connect.setTextColor(this@ControlPanelActivity.resources.getColor(R.color.white)) + retry_connect.setText(R.string.break_ble_connect) + retry_connect.setOnClickListener { + BleConfigService.get().bluetoothGatt?.let { + it?.close() + stopScanBleDev(false) + } + } + } else { + search_reault_layout.setBackgroundResource(R.color.red_E65A59) + retry_connect.setTextColor(this@ControlPanelActivity.resources.getColor(R.color.white)) + retry_connect.setText(R.string.scanning_retry) + retry_connect.setOnClickListener { startScanBleDev() } + } + } + } + + private var bleDeviceConnectionListener = object: BleDeviceConnectionListener { + override fun onBleDeviceFounded(bleDevice: BleDevice) { + if (bleDevice.productId == deviceEntity?.ProductId && !TextUtils.isEmpty(bleDevice.productId)) { + //&& bleDevice.devName == deviceEntity?.DeviceName) { + BleConfigService.get().bluetoothGatt = BleConfigService.get().connectBleDeviceAndGetLocalPsk(bleDevice, presenter.getProductId(), presenter.getDeviceName()) + } else if (!TextUtils.isEmpty(bleDevice.bindTag)) { + deviceEntity?.let { + if (bleDevice.bindTag == BleConfigService.bytesToHex(BleConfigService.getBindTag(it.ProductId, it.DeviceName))) { + BleConfigService.get().bluetoothGatt = BleConfigService.get().connectBleDeviceAndGetLocalPsk(bleDevice, presenter.getProductId(), presenter.getDeviceName()) + } + } + } + } + + override fun onBleDeviceConnected() { + launch { + BleConfigService.get().bluetoothGatt?.let { + delay(3000) +// if (BleConfigService.get().setMtuSize(it, 512)) return@launch + launch (Dispatchers.Main) { + delay(1000) + BleConfigService.get().bluetoothGatt?.let { + BleConfigService.get().stopScanBluetoothDevices() + if (!BleConfigService.get().connectSubDevice(it)) { + stopScanBleDev(false) + return@launch + } else { + connectBleJob = launch (Dispatchers.Main) { + delay(10000) + stopScanBleDev(false) + } + } + } + } + } + } + } + override fun onBleDeviceDisconnected(exception: TCLinkException) { + stopScanBleDev(false) + } + override fun onBleDeviceInfo(bleDeviceInfo: BleDeviceInfo) {} + override fun onBleSetWifiModeResult(success: Boolean) {} + override fun onBleSendWifiInfoResult(success: Boolean) {} + override fun onBleWifiConnectedInfo(wifiConnectInfo: BleWifiConnectInfo) {} + override fun onBlePushTokenResult(success: Boolean) {} + override fun onMtuChanged(mtu: Int, status: Int) { + L.d(TAG, "onMtuChanged mtu $mtu status $status") + } + override fun onBleBindSignInfo(bleDevBindCondition: BleDevBindCondition) {} + override fun onBleSendSignInfo(bleDevSignResult: BleDevSignResult) { + stopScanBleDev(true) + connectBleJob?.cancel() + } + override fun onBleUnbindSignInfo(signInfo: String) {} + override fun onBlePropertyValue(bleDeviceProperty: BleDeviceProperty) { + L.d(TAG, "onBlePropertyValue $bleDeviceProperty ") + } + override fun onBleControlPropertyResult(result: Int) {} + override fun onBleRequestCurrentProperty() { + presenter.model?.getBleDeviceStatus()?.let { + BleConfigService.get().sendCurrentBleDeviceProperty(BleConfigService.get().bluetoothGatt, + it + ) + } + } + override fun onBleNeedPushProperty(eventId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleReportActionResult(reason: Int, actionId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleDeviceFirmwareVersion(firmwareVersion: BleDeviceFirmwareVersion) { + if (firmwareVersion.mtuFlag == 1) { // 是否设置 mtu 当 mtu flag为 1 时,进行 MTU 设置;当 mtu flag 为 0 时,不设置 MTU + BleConfigService.get().setMtuSize(BleConfigService.get().bluetoothGatt, firmwareVersion.mtuSize) + } + mtusize = firmwareVersion.mtuSize + + deviceEntity?.run { + IoTAuth.deviceImpl.checkFirmwareUpdate(ProductId, DeviceName, object: MyCallback{ + override fun fail(msg: String?, reqCode: Int) { + + } + + override fun success(response: BaseResponse, reqCode: Int) { + val json = response.data as JSONObject + val dstVersion = json.getString("DstVersion") +// val currentVersion = json.getString("CurrentVersion") + if (!dstVersion.equals(firmwareVersion.version)) { + showCommonPopup(dstVersion) + } + } + }) + } + } + + override fun onBleDevOtaUpdateResponse(otaUpdateResponse: BleDevOtaUpdateResponse) {} + override fun onBleDevOtaUpdateResult(success: Boolean, errorCode: Int) {} + + override fun onBleDevOtaReceivedProgressResponse(progress: Int) {} + + override fun onBleDeviceMtuSize(size: Int) {} + override fun onBleDeviceTimeOut(timeLong: Int) {} + } + + private fun showCommonPopup(newVersion: String) { + var commonPopupWindow = CommonPopupWindow(this) + commonPopupWindow?.setBg(control_panel_bg) + commonPopupWindow?.setCommonParams( + getString(R.string.ble_firmware_found), + getString(R.string.ble_firmware_found_detail, newVersion) + ) + commonPopupWindow?.setMenuText("", getString(R.string.upgrade_now)) + commonPopupWindow?.onKeyListener = object : CommonPopupWindow.OnKeyListener { + override fun cancel(popupWindow: CommonPopupWindow) { + popupWindow.dismiss() + } + + override fun confirm(popupWindow: CommonPopupWindow) { + put("mtusize", mtusize) + jumpActivity(BleOTADownloadActivity::class.java) + popupWindow.dismiss() + } + } + commonPopupWindow?.show(control_panel) } override fun setListener() { @@ -111,7 +333,10 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl } override fun toFeedback(popupWindow: OfflinePopupWindow) { - jumpActivity(FeedbackActivity::class.java, true) + var intent = Intent(this@ControlPanelActivity, HelpWebViewActivity::class.java) + intent.putExtra(CommonField.FEEDBACK_DEVICE, true) + intent.putExtra(CommonField.FEEDBACK_CATEGORY, deviceEntity?.AliasName) + startActivity(intent) } } offlinePopup?.setBg(control_panel_bg) @@ -183,13 +408,38 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl } override fun refreshDeviceStatus(isOnline: Boolean) { - if (!isOnline) { + if (!isOnline) { //离线显示离线弹窗 job = CoroutineScope(Dispatchers.IO).launch { delay(200) CoroutineScope(Dispatchers.Main).launch { showOfflinePopup() } } + } else { //上线如果有离线弹窗去掉刷新状态 + job = CoroutineScope(Dispatchers.IO).launch { + delay(200) + CoroutineScope(Dispatchers.Main).launch { + offlinePopup?.run { + if (isShowing) { + dismiss() + if (presenter?.getCategoryId() == 567) { // 消费版视频平台产品 + delay(1000) + } + presenter.requestDeviceData() + presenter.getUserSetting() + } + } + } + } + } + } + + override fun refreshCategrayId(categoryId: Int) { + if (categoryId == 567) { // 消费版视频平台产品 + tv_param_setting.visibility = View.VISIBLE + tv_param_setting.setOnClickListener { + jumpActivity(ParamSettingActivity::class.java) + } } } @@ -280,7 +530,7 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl */ fun checkTRTCCallStatusIsBusy() : Boolean { if (!netWorkStateReceiver!!.isConnected(getApplicationContext())) { - Toast.makeText(this, "网络异常请重试", Toast.LENGTH_LONG).show() +// Toast.makeText(this, "网络异常请重试", Toast.LENGTH_LONG).show() return true; } var audioCallStatus = "0"; @@ -307,23 +557,39 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl //特殊处理,当设备为trtc设备时。虽然call_status是枚举类型,但产品要求不弹弹窗,点击即拨打语音或视频通话。 if (entity.id == MessageConst.TRTC_AUDIO_CALL_STATUS) { if (checkTRTCCallStatusIsBusy()) { + //如果是websocket未收到设备的消息,那么主动通过http刷新一下。 + presenter.requestDeviceData() return } controlDevice(entity.id, "1") - TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) - TRTCUIManager.getInstance().isCalling = true - TRTCUIManager.getInstance().deviceId = TRTCUIManager.getInstance().callingDeviceId - TRTCAudioCallActivity.startCallSomeone(this, RoomKey(), TRTCUIManager.getInstance().callingDeviceId) + if (presenter?.getCategoryId() == 567) { // 消费版视频平台产品 call + TRTCUIManager.getInstance().setSessionManager(P2PAppSessionManager()) + TRTCUIManager.getInstance().isCalling = true + TRTCUIManager.getInstance().deviceId = TRTCUIManager.getInstance().callingDeviceId + RecordVideoActivity.startCallSomeone(this, TRTCUIManager.getInstance().callingDeviceId, false) + } else { // TRTC产品 call + TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) + TRTCUIManager.getInstance().isCalling = true + TRTCUIManager.getInstance().deviceId = TRTCUIManager.getInstance().callingDeviceId + TRTCAudioCallActivity.startCallSomeone(this, RoomKey(), TRTCUIManager.getInstance().callingDeviceId) + } return } else if (entity.id == MessageConst.TRTC_VIDEO_CALL_STATUS) { if (checkTRTCCallStatusIsBusy()) { return } controlDevice(entity.id, "1") - TRTCUIManager.getInstance().isCalling = true - TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) - TRTCUIManager.getInstance().deviceId = TRTCUIManager.getInstance().callingDeviceId - TRTCVideoCallActivity.startCallSomeone(this, RoomKey(), TRTCUIManager.getInstance().callingDeviceId) + if (presenter?.getCategoryId() == 567) { // 消费版视频平台产品 call + TRTCUIManager.getInstance().isCalling = true + TRTCUIManager.getInstance().setSessionManager(P2PAppSessionManager()) + TRTCUIManager.getInstance().deviceId = TRTCUIManager.getInstance().callingDeviceId + RecordVideoActivity.startCallSomeone(this, TRTCUIManager.getInstance().callingDeviceId, true) + } else { // TRTC产品 call + TRTCUIManager.getInstance().isCalling = true + TRTCUIManager.getInstance().setSessionManager(TRTCAppSessionManager()) + TRTCUIManager.getInstance().deviceId = TRTCUIManager.getInstance().callingDeviceId + TRTCVideoCallActivity.startCallSomeone(this, RoomKey(), TRTCUIManager.getInstance().callingDeviceId) + } return } if (enumPopup == null) { @@ -365,9 +631,71 @@ class ControlPanelActivity : PActivity(), ControlPanelView, CRecyclerView.Recycl } override fun onDestroy() { + if (deviceEntity?.CategoryId == 567) { //非双向 + netWorkStateReceiver?.run { + unregisterReceiver(netWorkStateReceiver) + } + } PanelThemeManager.instance.destroy() job?.cancel() + cancel() + BleConfigService.get().bluetoothGatt?.close() + BleConfigService.get().stopScanBluetoothDevices() + BleConfigService.get().bluetoothGatt = null // App.setEnableEnterRoomCallback(true) + if (deviceEntity?.CategoryId == 567) { + XP2P.stopService(deviceEntity?.DeviceId) + presenter.removeReconnectCycleTasktask() + } + unregistVideoOverBrodcast() super.onDestroy() } + + override fun networkAvailable() { + + (applicationContext?.getSystemService(Context.WIFI_SERVICE) as? WifiManager)?.let { + val hostIp = intToIp(it.dhcpInfo.gateway) + if (presenter?.getCategoryId() == 567) { // 消费版视频平台产品 +// Toast.makeText(this, hostIp, Toast.LENGTH_SHORT).show() + } + L.e("hostIp=${hostIp}") + if (App.activity is RecordVideoActivity) { + presenter.requestDeviceData() + } else { + //网络可达 + presenter.requestDeviceData() + presenter.getUserSetting() + } + } + } + + override fun networkUnavailable() { + //网络不可达 +// Toast.makeText(this, "网络异常", Toast.LENGTH_LONG).show() + } + + fun registVideoOverBrodcast() { + L.e(TAG, "registVideoOverBrodcast ControlPanelActivity") + val broadcastManager = LocalBroadcastManager.getInstance(this@ControlPanelActivity) + val intentFilter = IntentFilter() + intentFilter.addAction("android.intent.action.CART_BROADCAST") + broadcastManager.registerReceiver(recevier, intentFilter) + } + + fun unregistVideoOverBrodcast() { + L.e(TAG, "unregistVideoOverBrodcast ControlPanelActivity") + val broadcastManager = LocalBroadcastManager.getInstance(this@ControlPanelActivity) + broadcastManager.unregisterReceiver(recevier) + } + + var recevier: BroadcastReceiver = object : BroadcastReceiver() { + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + override fun onReceive(context: Context, intent: Intent) { + val refreshTag = intent.getIntExtra(VideoUtils.VIDEO_RESET, 0) + L.d(TAG, "ControlPanelActivity refreshTag: $refreshTag") + if (refreshTag == 100 && App.activity is RecordVideoActivity) { //拉p2p_info,重启p2p + presenter.requestDeviceDataByP2P() + } + } + } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPermissionActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPermissionActivity.kt index a2dcd5c65..dc2cfdba3 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPermissionActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ControlPermissionActivity.kt @@ -7,6 +7,7 @@ import android.net.Uri import android.provider.Settings import android.util.Log import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationManagerCompat import androidx.recyclerview.widget.LinearLayoutManager import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.kitlink.adapter.PermissionsAdapter @@ -20,17 +21,12 @@ class ControlPermissionActivity : BaseActivity() { private var permissionsList = arrayOf( Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.RECEIVE_SMS, - Manifest.permission.READ_SMS, - Manifest.permission.SEND_SMS, - Manifest.permission.WRITE_SETTINGS) + Manifest.permission.ACCESS_COARSE_LOCATION) private var permissionsData: MutableList = ArrayList() private var adapter: PermissionsAdapter? = null @@ -43,7 +39,9 @@ class ControlPermissionActivity : BaseActivity() { super.onResume() // 刷新当前的权限列表,避免用户手动进入到后台,调整权限的情况 if (permissionsData.isEmpty()) return - for (i in permissionsData.indices) { + val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(this@ControlPermissionActivity); + permissionsData[0].permissionAccessed = notificationManager.areNotificationsEnabled() + for (i in 1 until permissionsData.size) { permissionsData[i].permissionAccessed = ActivityCompat.checkSelfPermission(this, permissionsData[i].permission) == PackageManager.PERMISSION_GRANTED } @@ -68,10 +66,37 @@ class ControlPermissionActivity : BaseActivity() { private var onItemClicked = object: PermissionsAdapter.OnItemActionListener { override fun onItemSwitched(pos: Int, permissionAccessInfo: PermissionAccessInfo) { - if (!permissionAccessInfo.permissionAccessed) { // 没有对应的权限,尝试开启权限 - ActivityCompat.requestPermissions(this@ControlPermissionActivity, arrayOf(permissionAccessInfo.permission), REQUEST_CODE) + if (pos == 0) { //通知 + val intent: Intent = Intent() + try { + intent.action = Settings.ACTION_APP_NOTIFICATION_SETTINGS + + //8.0及以后版本使用这两个extra. >=API 26 + intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName) + intent.putExtra(Settings.EXTRA_CHANNEL_ID, applicationInfo.uid) + + //5.0-7.1 使用这两个extra. <= API 25, >=API 21 + intent.putExtra("app_package", packageName) + intent.putExtra("app_uid", applicationInfo.uid) + + startActivity(intent) + } catch (e: Exception) { + e.printStackTrace() + + //其他低版本或者异常情况,走该节点。进入APP设置界面 + intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS + intent.putExtra("package", packageName) + + //val uri = Uri.fromParts("package", packageName, null) + //intent.data = uri + startActivity(intent) + } return } +// if (!permissionAccessInfo.permissionAccessed) { // 没有对应的权限,尝试开启权限 +// ActivityCompat.requestPermissions(this@ControlPermissionActivity, arrayOf(permissionAccessInfo.permission), REQUEST_CODE) +// return +// } val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) intent.data = Uri.parse("package:$packageName") startActivityForResult(intent, REQUEST_CODE) // 申请权限返回执行 @@ -79,6 +104,7 @@ class ControlPermissionActivity : BaseActivity() { } private fun checkPermissionsInfo(permissions: Array) { + permissionsData.add(checkNotificationPermissions()) val pm = this@ControlPermissionActivity.packageManager for (permission in permissions) { var accessInfo = PermissionAccessInfo() @@ -88,7 +114,12 @@ class ControlPermissionActivity : BaseActivity() { try { var permissionInfo = pm.getPermissionInfo(permission, 0) - accessInfo.permissionName = permissionInfo.loadLabel(pm).toString() + if (permission.equals("android.permission.READ_EXTERNAL_STORAGE")) { + var storageName = getString(R.string.permission_album_lips) + accessInfo.permissionName = storageName.substring(1) + } else { + accessInfo.permissionName = permissionInfo.loadLabel(pm).toString() + } accessInfo.permission = permission } catch (e: Exception) { e.printStackTrace() @@ -97,4 +128,12 @@ class ControlPermissionActivity : BaseActivity() { } } + private fun checkNotificationPermissions() : PermissionAccessInfo { + var accessInfo = PermissionAccessInfo() + val notificationManager: NotificationManagerCompat = NotificationManagerCompat.from(this@ControlPermissionActivity); + accessInfo.permissionAccessed = notificationManager.areNotificationsEnabled() + accessInfo.permissionName = "通知管理" + return accessInfo + } + } \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceCategoryActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceCategoryActivity.kt index eee1432f2..82a75237e 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceCategoryActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceCategoryActivity.kt @@ -5,12 +5,12 @@ import android.app.Activity import android.bluetooth.BluetoothAdapter import android.content.Context import android.content.Intent -import android.net.Uri +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.os.Handler import android.text.TextUtils import android.util.DisplayMetrics -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -21,17 +21,24 @@ import android.view.animation.LinearInterpolator import androidx.fragment.app.Fragment import androidx.recyclerview.widget.GridLayoutManager import com.alibaba.fastjson.JSON +import com.alibaba.fastjson.JSONObject import com.example.qrcode.Constant -import com.example.qrcode.ScannerActivity import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.auth.callback.MyCallback -import com.tencent.iot.explorer.link.core.auth.entity.DevModeInfo import com.tencent.iot.explorer.link.core.auth.response.BaseResponse import com.tencent.iot.explorer.link.core.auth.util.JsonManager -import com.tencent.iot.explorer.link.core.link.entity.* +import com.tencent.iot.explorer.link.core.link.entity.BleDevBindCondition +import com.tencent.iot.explorer.link.core.link.entity.BleDevOtaUpdateResponse +import com.tencent.iot.explorer.link.core.link.entity.BleDevSignResult +import com.tencent.iot.explorer.link.core.link.entity.BleDevice +import com.tencent.iot.explorer.link.core.link.entity.BleDeviceFirmwareVersion +import com.tencent.iot.explorer.link.core.link.entity.BleDeviceInfo +import com.tencent.iot.explorer.link.core.link.entity.BleDeviceProperty +import com.tencent.iot.explorer.link.core.link.entity.BleWifiConnectInfo import com.tencent.iot.explorer.link.core.link.entity.DeviceInfo +import com.tencent.iot.explorer.link.core.link.entity.TrtcDeviceInfo import com.tencent.iot.explorer.link.core.link.exception.TCLinkException import com.tencent.iot.explorer.link.core.link.listener.BleDeviceConnectionListener import com.tencent.iot.explorer.link.core.link.service.BleConfigService @@ -40,24 +47,45 @@ import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.MyScrollView import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog import com.tencent.iot.explorer.link.customview.recyclerview.CRecyclerView -import com.tencent.iot.explorer.link.customview.verticaltab.* +import com.tencent.iot.explorer.link.customview.verticaltab.ITabView +import com.tencent.iot.explorer.link.customview.verticaltab.TabAdapter +import com.tencent.iot.explorer.link.customview.verticaltab.TabView +import com.tencent.iot.explorer.link.customview.verticaltab.VerticalTabLayout import com.tencent.iot.explorer.link.kitlink.adapter.BleDeviceAdapter -import com.tencent.iot.explorer.link.kitlink.adapter.DeviceAdapter import com.tencent.iot.explorer.link.kitlink.consts.CommonField -import com.tencent.iot.explorer.link.kitlink.entity.* +import com.tencent.iot.explorer.link.kitlink.entity.BindDevResponse +import com.tencent.iot.explorer.link.kitlink.entity.GatewaySubDevsResp +import com.tencent.iot.explorer.link.kitlink.entity.ProdConfigDetailEntity +import com.tencent.iot.explorer.link.kitlink.entity.ProductEntity +import com.tencent.iot.explorer.link.kitlink.entity.ProductGlobal +import com.tencent.iot.explorer.link.kitlink.entity.ProductsEntity import com.tencent.iot.explorer.link.kitlink.fragment.DeviceFragment import com.tencent.iot.explorer.link.kitlink.holder.DeviceListViewHolder import com.tencent.iot.explorer.link.kitlink.response.DeviceCategoryListResponse import com.tencent.iot.explorer.link.kitlink.response.ProductsConfigResponse -import com.tencent.iot.explorer.link.kitlink.util.* +import com.tencent.iot.explorer.link.kitlink.util.HttpRequest +import com.tencent.iot.explorer.link.kitlink.util.RequestCode import com.tencent.iot.explorer.link.mvp.IPresenter -import kotlinx.android.synthetic.main.activity_device_category.* -import kotlinx.android.synthetic.main.bluetooth_adapter_invalid.* -import kotlinx.android.synthetic.main.menu_back_layout.* +import kotlinx.android.synthetic.main.activity_device_category.container_normal +import kotlinx.android.synthetic.main.activity_device_category.container_top +import kotlinx.android.synthetic.main.activity_device_category.gray_line_0 +import kotlinx.android.synthetic.main.activity_device_category.gray_line_1 +import kotlinx.android.synthetic.main.activity_device_category.iv_scann +import kotlinx.android.synthetic.main.activity_device_category.linearlayout_scann +import kotlinx.android.synthetic.main.activity_device_category.my_scroll_view +import kotlinx.android.synthetic.main.activity_device_category.not_found_dev +import kotlinx.android.synthetic.main.activity_device_category.scann_fail +import kotlinx.android.synthetic.main.activity_device_category.scanning +import kotlinx.android.synthetic.main.activity_device_category.vtab_device_category +import kotlinx.android.synthetic.main.bluetooth_adapter_invalid.retry_to_scann01 +import kotlinx.android.synthetic.main.menu_back_layout.iv_back import kotlinx.android.synthetic.main.menu_cancel_layout.tv_title -import kotlinx.android.synthetic.main.not_found_device.* -import kotlinx.android.synthetic.main.scanned_devices.* -import kotlinx.android.synthetic.main.scanning.* +import kotlinx.android.synthetic.main.not_found_device.retry_to_scann02 +import kotlinx.android.synthetic.main.not_found_device.tv_tag +import kotlinx.android.synthetic.main.scanned_devices.scanned_device_list +import kotlinx.android.synthetic.main.scanned_devices.tv_devs_tip +import kotlinx.android.synthetic.main.scanning.iv_loading_cirecle +import kotlinx.android.synthetic.main.scanning.tv_scanning_ble_devs class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerItemView, View.OnClickListener, VerticalTabLayout.OnTabSelectedListener{ @@ -70,15 +98,24 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI Manifest.permission.CAMERA, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.CHANGE_WIFI_STATE, - Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, - Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.CHANGE_WIFI_MULTICAST_STATE ) + private var permissionDialog: PermissionDialog? = null private var blueToothPermissions = arrayOf( Manifest.permission.ACCESS_COARSE_LOCATION, - Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.BLUETOOTH ) + private val android12BluetoothPermissions = arrayOf( + Manifest.permission.BLUETOOTH_SCAN, + Manifest.permission.BLUETOOTH_CONNECT, + Manifest.permission.ACCESS_FINE_LOCATION, + ) + + private var hasPermission = false + override fun getPresenter(): IPresenter? { return null } @@ -127,7 +164,7 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI private var bleDeviceConnectionListener = object: BleDeviceConnectionListener { override fun onBleDeviceFounded(bleDevice: BleDevice) { var index = bleDevs.indexOf(bleDevice) - if (index < 0) { + if (index < 0 && bleDevice.boundState != 2) {//非连接状态的蓝牙设备在此处可以展示出来进行扫描绑定 bleDevs.add(bleDevice) refreshDevInfo(bleDevice) } @@ -142,6 +179,22 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI override fun onBleWifiConnectedInfo(wifiConnectInfo: BleWifiConnectInfo) {} override fun onBlePushTokenResult(success: Boolean) {} override fun onMtuChanged(mtu: Int, status: Int) {} + override fun onBleBindSignInfo(bleDevBindCondition: BleDevBindCondition) {} + override fun onBleSendSignInfo(bleDevSignResult: BleDevSignResult) {} + override fun onBleUnbindSignInfo(signInfo: String) {} + override fun onBlePropertyValue(bleDeviceProperty: BleDeviceProperty) {} + override fun onBleControlPropertyResult(result: Int) {} + override fun onBleRequestCurrentProperty() {} + override fun onBleNeedPushProperty(eventId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleReportActionResult(reason: Int, actionId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleDeviceFirmwareVersion(firmwareVersion: BleDeviceFirmwareVersion) {} + override fun onBleDevOtaUpdateResponse(otaUpdateResponse: BleDevOtaUpdateResponse) {} + override fun onBleDevOtaUpdateResult(success: Boolean, errorCode: Int) {} + + override fun onBleDevOtaReceivedProgressResponse(progress: Int) {} + + override fun onBleDeviceMtuSize(size: Int) {} + override fun onBleDeviceTimeOut(timeLong: Int) {} } private val runnable = Runnable { @@ -153,7 +206,9 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI } else { tv_tag.setText(R.string.not_found_device) } - BleConfigService.get().stopScanBluetoothDevices() + if (hasPermission) { + BleConfigService.get().stopScanBluetoothDevices() + } } override fun onResume() { @@ -164,7 +219,9 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI override fun onDestroy() { super.onDestroy() iv_loading_cirecle.clearAnimation() - BleConfigService.get().stopScanBluetoothDevices() + if (hasPermission){ + BleConfigService.get().stopScanBluetoothDevices() + } } override fun setListener() { @@ -198,10 +255,7 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI T.show(getString(R.string.no_product_info)) return } - var intent = Intent(this@DeviceCategoryActivity, BleConfigHardwareActivity::class.java) - intent.putExtra(CommonField.PRODUCT_ID, dev.productId) - App.data.bleDevice = dev - this@DeviceCategoryActivity.startActivity(intent) + BleConfigHardwareActivity.startWithProductid(this@DeviceCategoryActivity, dev.productId, dev.type) BleConfigService.get().stopScanBluetoothDevices() } } @@ -300,14 +354,38 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI return 0 } - override fun permissionAllGranted() { - var intent = Intent(Intent(this, ScannerActivity::class.java)) - intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC,true) - startActivityForResult(intent, CommonField.QR_CODE_REQUEST_CODE) + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (requestCode == DEFAULT_REQUEST_PERMISSIONS_CODE) { + permissionDialog?.dismiss() + permissionDialog = null + for (i in permissions.indices) { + if (permissions.contains(Manifest.permission.ACCESS_COARSE_LOCATION) && grantResults[i] == PackageManager.PERMISSION_GRANTED) { //同意了蓝牙的定位权限 + beginScanning() + return + } + } + //同意了相机权限 + var intent = Intent(Intent(this, ScannerActivity::class.java)) + intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC,true) + startActivityForResult(intent, CommonField.QR_CODE_REQUEST_CODE) + permissionDialog?.dismiss() + permissionDialog = null + } } override fun permissionDenied(permission: String) { -// requestPermission(arrayOf(permission)) + permissionDialog?.dismiss() + permissionDialog = null + } + + override fun permissionAllGranted() { + super.permissionAllGranted() + hasPermission = true } override fun onClick(v: View?) { @@ -318,18 +396,23 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC,true) startActivityForResult(intent, CommonField.QR_CODE_REQUEST_CODE) } else { - var dlg = PermissionDialog(this@DeviceCategoryActivity, getString(R.string.permission_of_wifi), getString(R.string.permission_of_wifi_lips)) - dlg.show() - dlg.setOnDismisListener(object : PermissionDialog.OnDismisListener { - override fun OnClickRefuse() { - - } + // 查看请求camera权限的时间是否大于48小时 + var cameraJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA) + var cameraJson: JSONObject? = JSONObject.parse(cameraJsonString) as JSONObject? + val lasttime = cameraJson?.getLong(CommonField.PERMISSION_CAMERA) + if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { + T.show(getString(R.string.permission_of_camera_refuse)) + return + } + requestPermission(permissions) + permissionDialog = PermissionDialog(App.activity, R.mipmap.permission_camera ,getString(R.string.permission_camera_lips), getString(R.string.permission_camera)) + permissionDialog!!.show() - override fun OnClickOK() { - requestPermission(permissions) - } + // 记录请求camera权限的时间 + var json = JSONObject() + json.put(CommonField.PERMISSION_CAMERA, System.currentTimeMillis() / 1000) + Utils.setXmlStringValue(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA, json.toJSONString()) - }) } } // iv_question -> { @@ -484,19 +567,29 @@ class DeviceCategoryActivity : PActivity(), MyCallback, CRecyclerView.RecyclerI } private fun beginScanning() { - if (!checkPermissions(blueToothPermissions)) { - var dlg = PermissionDialog(this@DeviceCategoryActivity, getString(R.string.permission_of_wifi), getString(R.string.permission_of_wifi_lips)) - dlg.show() - dlg.setOnDismisListener(object : PermissionDialog.OnDismisListener { - override fun OnClickRefuse() { - - } - - override fun OnClickOK() { - requestPermission(blueToothPermissions) - } - - }) + val permissions = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) android12BluetoothPermissions else blueToothPermissions + hasPermission = checkPermissions(permissions) + if (!hasPermission) { + // 查看请求ble location权限的时间是否大于48小时 + var locationJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_LOCATION, CommonField.PERMISSION_LOCATION) + var locationJson: JSONObject? = JSONObject.parse(locationJsonString) as JSONObject? + val lasttime = locationJson?.getLong(CommonField.PERMISSION_LOCATION) + if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { + T.show(getString(R.string.permission_of_location_add_device_refuse)) + scann_fail.visibility = View.VISIBLE + scanning.visibility = View.GONE + not_found_dev.visibility = View.GONE + return + } + requestPermission(permissions, REQUEST_BLUETOOTH_PERMISSIONS_CODE) + permissionDialog = PermissionDialog(this@DeviceCategoryActivity, R.mipmap.permission_location ,getString(R.string.permission_location_lips), getString(R.string.permission_location_ssid_ble)) + permissionDialog!!.show() + + // 记录请求camera权限的时间 + var json = JSONObject() + json.put(CommonField.PERMISSION_LOCATION, System.currentTimeMillis() / 1000) + Utils.setXmlStringValue(T.getContext(), CommonField.PERMISSION_LOCATION, CommonField.PERMISSION_LOCATION, json.toJSONString()) return } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceDetailsActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceDetailsActivity.kt index 55511ea11..c7da823b1 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceDetailsActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceDetailsActivity.kt @@ -1,8 +1,10 @@ package com.tencent.iot.explorer.link.kitlink.activity import android.app.Activity +import android.bluetooth.BluetoothGatt import android.content.Intent import android.text.TextUtils +import android.util.Log import com.alibaba.fastjson.JSON import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.R @@ -11,20 +13,29 @@ import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.entity.DeviceEntity import com.tencent.iot.explorer.link.core.auth.entity.RoomEntity import com.tencent.iot.explorer.link.core.auth.response.BaseResponse +import com.tencent.iot.explorer.link.core.link.entity.* +import com.tencent.iot.explorer.link.core.link.exception.TCLinkException +import com.tencent.iot.explorer.link.core.link.listener.BleDeviceConnectionListener +import com.tencent.iot.explorer.link.core.link.service.BleConfigService import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.kitlink.consts.CommonField +import com.tencent.iot.explorer.link.kitlink.entity.BleConfig import com.tencent.iot.explorer.link.kitlink.entity.EditNameValue +import com.tencent.iot.explorer.link.kitlink.entity.ProductEntity +import com.tencent.iot.explorer.link.kitlink.entity.ProductsEntity import com.tencent.iot.explorer.link.kitlink.popup.CommonPopupWindow import com.tencent.iot.explorer.link.kitlink.popup.EditPopupWindow import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.Utils +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import com.tencent.iot.explorer.link.mvp.presenter.DeviceDetailPresenter import com.tencent.iot.explorer.link.mvp.view.DeviceDetailView import kotlinx.android.synthetic.main.activity_device_details.* import kotlinx.android.synthetic.main.menu_back_layout.* +import kotlinx.coroutines.* -class DeviceDetailsActivity : PActivity(), DeviceDetailView { +class DeviceDetailsActivity : PActivity(), CoroutineScope by MainScope(), DeviceDetailView { private var deviceEntity: DeviceEntity? = null @@ -71,6 +82,9 @@ class DeviceDetailsActivity : PActivity(), DeviceDetailView { } override fun deleteSuccess() { + BleConfigService.get().bluetoothGatt?.let { + BleConfigService.get().sendUnbindResult(it, true) + } App.data.refresh = true App.data.setRefreshLevel(2) Utils.sendRefreshBroadcast(this@DeviceDetailsActivity) @@ -78,6 +92,9 @@ class DeviceDetailsActivity : PActivity(), DeviceDetailView { } override fun fail(message: String) { + BleConfigService.get().bluetoothGatt?.let { + BleConfigService.get().sendUnbindResult(it, false) + } T.show(message) } @@ -96,9 +113,21 @@ class DeviceDetailsActivity : PActivity(), DeviceDetailView { override fun confirm(popupWindow: CommonPopupWindow) { popupWindow.dismiss() deviceEntity?.run { - presenter.deleteDevice(ProductId, DeviceName) + getDeviceType(ProductId, object: OnTypeGeted { + override fun onType(type: String) { + if (type == "ble") { + BleConfigService.get()?.let { + BleConfigService.get().connetionListener = bleDeviceConnectionListener + if (BleConfigService.get().unbind(it.bluetoothGatt)) { + T.show(getString(R.string.delete_success)) + } + } + } else { + deleteDevice() + } + } + }) } - Utils.sendRefreshBroadcast(this@DeviceDetailsActivity) } override fun cancel(popupWindow: CommonPopupWindow) { @@ -107,20 +136,17 @@ class DeviceDetailsActivity : PActivity(), DeviceDetailView { } } + private fun deleteDevice() { + launch (Dispatchers.Main) { + deviceEntity?.run { + presenter.deleteDevice(ProductId, DeviceName) + } + Utils.sendRefreshBroadcast(this@DeviceDetailsActivity) + } + } + private fun showEditPopup() { -// if (editPopupWindow == null) { - editPopupWindow = EditPopupWindow(this) -// } -// deviceEntity?.run { -// editPopupWindow?.setShowData(getString(R.string.device_name), AliasName) -// } -// editPopupWindow?.setBg(device_detail_bg) -// editPopupWindow?.show(device_detail) -// editPopupWindow?.onVerifyListener = object : EditPopupWindow.OnVerifyListener { -// override fun onVerify(text: String) { -// commitAlias(text) -// } -// } + editPopupWindow = EditPopupWindow(this) var intent = Intent(this@DeviceDetailsActivity, EditNameActivity::class.java) var editNameValue = EditNameValue() editNameValue.name = deviceEntity!!.getAlias() @@ -132,11 +158,41 @@ class DeviceDetailsActivity : PActivity(), DeviceDetailView { startActivityForResult(intent, CommonField.EDIT_NAME_REQ_CODE) } + private var bleDeviceConnectionListener = object: BleDeviceConnectionListener { + override fun onBleDeviceFounded(bleDevice: BleDevice) {} + override fun onBleDeviceConnected() {} + override fun onBleDeviceDisconnected(exception: TCLinkException) {} + override fun onBleDeviceInfo(bleDeviceInfo: BleDeviceInfo) {} + override fun onBleSetWifiModeResult(success: Boolean) {} + override fun onBleSendWifiInfoResult(success: Boolean) {} + override fun onBleWifiConnectedInfo(wifiConnectInfo: BleWifiConnectInfo) {} + override fun onBlePushTokenResult(success: Boolean) {} + override fun onMtuChanged(mtu: Int, status: Int) {} + override fun onBleBindSignInfo(bleDevBindCondition: BleDevBindCondition) {} + override fun onBleSendSignInfo(bleDevSignResult: BleDevSignResult) {} + override fun onBleUnbindSignInfo(signInfo: String) { + deleteDevice() + } + override fun onBlePropertyValue(bleDeviceProperty: BleDeviceProperty) {} + override fun onBleControlPropertyResult(result: Int) {} + override fun onBleRequestCurrentProperty() {} + override fun onBleNeedPushProperty(eventId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleReportActionResult(reason: Int, actionId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleDeviceFirmwareVersion(firmwareVersion: BleDeviceFirmwareVersion) {} + override fun onBleDevOtaUpdateResponse(otaUpdateResponse: BleDevOtaUpdateResponse) {} + override fun onBleDevOtaUpdateResult(success: Boolean, errorCode: Int) {} + + override fun onBleDevOtaReceivedProgressResponse(progress: Int) {} + + override fun onBleDeviceMtuSize(size: Int) {} + override fun onBleDeviceTimeOut(timeLong: Int) {} + } + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CommonField.EDIT_NAME_REQ_CODE && resultCode == Activity.RESULT_OK && data != null) { - var extraInfo = data?.getStringExtra(CommonField.EXTRA_TEXT) + val extraInfo = data.getStringExtra(CommonField.EXTRA_TEXT).safe() commitAlias(extraInfo) } } @@ -196,6 +252,7 @@ class DeviceDetailsActivity : PActivity(), DeviceDetailView { dismiss() } } + cancel() super.onDestroy() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DevicePanelActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DevicePanelActivity.kt index 05851234f..d888e5e63 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DevicePanelActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DevicePanelActivity.kt @@ -72,11 +72,11 @@ class DevicePanelActivity: BaseActivity(), View.OnClickListener, MyCallback, App var url = H5_PANEL_BASE_URL + "?deviceId=${deviceEntity?.DeviceId}" + "&familyId=${deviceEntity?.FamilyId}" + - "&uin=${Utils.getAndroidID(this)}" + + "&uin=${App.uuid}" + "&roomId=${deviceEntity?.RoomId}" + "&familyType=${App.data.getCurrentFamily().FamilyType}" + "&lid=${App.data.appLifeCircleId}" + - "&quid=${Utils.getAndroidID(this)}" + + "&quid=${App.uuid}" + "&ticket=${ticketResponse[CommonField.TOKEN_TICKET]}" + "&appID=${T.getContext().applicationInfo.packageName}" + "&platform=android" + diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceWifiActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceWifiActivity.kt index 40079475e..e0006cf5c 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceWifiActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/DeviceWifiActivity.kt @@ -1,5 +1,6 @@ package com.tencent.iot.explorer.link.kitlink.activity +import android.Manifest import android.content.Context import android.content.Intent import android.net.wifi.WifiInfo @@ -13,12 +14,12 @@ import androidx.appcompat.content.res.AppCompatResources import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.utils.KeyBoardUtils -import com.tencent.iot.explorer.link.core.utils.LocationUtil import com.tencent.iot.explorer.link.customview.dialog.WifiHelperDialog import com.tencent.iot.explorer.link.customview.progress.bean.StepBean import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.consts.LoadViewTxtType import com.tencent.iot.explorer.link.kitlink.entity.ConfigType +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import kotlinx.android.synthetic.main.activity_device_wifi.* import kotlinx.android.synthetic.main.smart_config_second.* @@ -67,17 +68,17 @@ class DeviceWifiActivity : PActivity() { override fun initView() { loadViewTextType = intent.getIntExtra(CommonField.LOAD_VIEW_TXT_TYPE, LoadViewTxtType.LoadLocalViewTxt.ordinal) if (loadViewTextType != LoadViewTxtType.LoadLocalViewTxt.ordinal) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() } type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SmartConfig.id) if (intent.hasExtra(CommonField.SSID)) { - extraSsid = intent.getStringExtra(CommonField.SSID) + extraSsid = intent.getStringExtra(CommonField.SSID).safe() } if (intent.hasExtra(CommonField.BSSID)) { - extraBssid = intent.getStringExtra(CommonField.BSSID) + extraBssid = intent.getStringExtra(CommonField.BSSID).safe() } if (intent.hasExtra(CommonField.PWD)) { - extraPwd = intent.getStringExtra(CommonField.PWD) + extraPwd = intent.getStringExtra(CommonField.PWD).safe() } refreshTypeView() @@ -95,6 +96,8 @@ class DeviceWifiActivity : PActivity() { showWifiInfo() } + private val permissions = arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION) + /** * 展示wifi */ @@ -110,7 +113,7 @@ class DeviceWifiActivity : PActivity() { openWifiDialog?.show() } else { var ssid2Set = wifiManager.connectionInfo.ssid.replace("\"", "") - if (!LocationUtil.isLocationServiceEnable(this)) { + if (!checkPermissions(permissions)) { tv_select_wifi.hint = getString(R.string.open_location_tip) ssid2Set = "" openLocationServiceDialog?.show() diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditAutoicTaskActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditAutoicTaskActivity.kt index a96339802..248a9f7e4 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditAutoicTaskActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditAutoicTaskActivity.kt @@ -30,6 +30,7 @@ import com.tencent.iot.explorer.link.kitlink.adapter.touch.OnItemTouchListener import com.tencent.iot.explorer.link.kitlink.adapter.touch.TaskItemTouchHelper import com.tencent.iot.explorer.link.kitlink.util.RequestCode import com.tencent.iot.explorer.link.kitlink.util.Utils +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_add_autoic_task.* import kotlinx.android.synthetic.main.activity_complete_task_info.iv_smart_background import kotlinx.android.synthetic.main.activity_complete_task_info.layout_smart_name @@ -535,11 +536,11 @@ class EditAutoicTaskActivity : BaseActivity(), MyCallback { if (requestCode == CommonField.REQUEST_PIC_REQ_CODE && // 添加任务 resultCode == Activity.RESULT_OK && data != null) { - var picUrl = data?.getStringExtra(CommonField.EXTRA_PIC_URL) + var picUrl = data.getStringExtra(CommonField.EXTRA_PIC_URL).safe() smartPicUrl = picUrl } else if (requestCode == CommonField.REQUEST_TASK_NAME_REQ_CODE && // 添加任务 resultCode == Activity.RESULT_OK && data != null) { - var name = data?.getStringExtra(CommonField.EXYRA_TASK_NAME) + var name = data.getStringExtra(CommonField.EXYRA_TASK_NAME).safe() smartName = name.trim() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditManualTaskActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditManualTaskActivity.kt index e6c735393..fd8455d61 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditManualTaskActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/EditManualTaskActivity.kt @@ -27,6 +27,7 @@ import com.tencent.iot.explorer.link.kitlink.adapter.touch.OnItemTouchListener import com.tencent.iot.explorer.link.kitlink.adapter.touch.TaskItemTouchHelper import com.tencent.iot.explorer.link.kitlink.util.RequestCode import com.tencent.iot.explorer.link.kitlink.util.Utils +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_add_manual_task.* import kotlinx.android.synthetic.main.activity_complete_task_info.iv_smart_background import kotlinx.android.synthetic.main.activity_complete_task_info.layout_smart_name @@ -271,11 +272,11 @@ class EditManualTaskActivity : BaseActivity(), MyCallback { if (requestCode == CommonField.REQUEST_PIC_REQ_CODE && // 添加任务 resultCode == Activity.RESULT_OK && data != null) { - var picUrl = data?.getStringExtra(CommonField.EXTRA_PIC_URL) + var picUrl = data?.getStringExtra(CommonField.EXTRA_PIC_URL).safe() smartPicUrl = picUrl } else if (requestCode == CommonField.REQUEST_TASK_NAME_REQ_CODE && // 添加任务 resultCode == Activity.RESULT_OK && data != null) { - var name = data?.getStringExtra(CommonField.EXYRA_TASK_NAME) + var name = data?.getStringExtra(CommonField.EXYRA_TASK_NAME).safe() smartName = name.trim() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FamilyActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FamilyActivity.kt index e92b4d0c2..83ae55b0e 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FamilyActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FamilyActivity.kt @@ -1,5 +1,6 @@ package com.tencent.iot.explorer.link.kitlink.activity +import android.Manifest.permission import android.app.Activity import android.content.Intent import android.os.Bundle @@ -19,6 +20,7 @@ import com.tencent.iot.explorer.link.kitlink.holder.FamilyInfoHeaderHolder import com.tencent.iot.explorer.link.kitlink.holder.MemberListViewHolder import com.tencent.iot.explorer.link.kitlink.popup.CommonPopupWindow import com.tencent.iot.explorer.link.kitlink.popup.EditPopupWindow +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IModel import com.tencent.iot.explorer.link.mvp.model.FamilyModel import com.tencent.iot.explorer.link.mvp.view.FamilyView @@ -45,6 +47,8 @@ class FamilyActivity : MActivity(), FamilyView, CRecyclerView.RecyclerItemView { private var deleteFamilyPopup: CommonPopupWindow? = null private var exitFamilyPopup: CommonPopupWindow? = null + private val permissions = arrayOf(permission.ACCESS_COARSE_LOCATION) + override fun getModel(): IModel? { return model } @@ -79,6 +83,7 @@ class FamilyActivity : MActivity(), FamilyView, CRecyclerView.RecyclerItemView { */ private fun addHeader() { headerHolder = FamilyInfoHeaderHolder(this, crv_member_list, R.layout.head_family) + familyEntity?.showAddress = checkPermissions(permissions) headerHolder.data = familyEntity crv_member_list.addHeader(headerHolder) headerHolder.headListener = object : CRecyclerView.HeadListener { @@ -97,13 +102,6 @@ class FamilyActivity : MActivity(), FamilyView, CRecyclerView.RecyclerItemView { jumpActivity(RoomListActivity::class.java) } 2 -> { - val intent = Intent(this@FamilyActivity, MarkerPaddingActivity::class.java) - var bundle = Bundle() - bundle.putString(CommonField.ADDRESS, headerHolder.data?.Address) - bundle.putString(CommonField.FAMILY_ID, familyEntity?.FamilyId) - bundle.putString(CommonField.FAMILY_NAME, familyEntity?.FamilyName) - intent.putExtra(CommonField.ADDRESS, bundle) - startActivityForResult(intent, CommonField.MAP_LOCATION_REQ_CODE) } 3 -> { jumpActivity(InviteMemberActivity::class.java) @@ -220,7 +218,7 @@ class FamilyActivity : MActivity(), FamilyView, CRecyclerView.RecyclerItemView { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CommonField.EDIT_NAME_REQ_CODE && resultCode == Activity.RESULT_OK && data != null) { - var extraInfo = data?.getStringExtra(CommonField.EXTRA_TEXT) + var extraInfo = data.getStringExtra(CommonField.EXTRA_TEXT).safe() model.modifyFamilyName(extraInfo) } else if (requestCode == CommonField.MAP_LOCATION_REQ_CODE && resultCode == RESULT_OK) { var ret = data?.getStringExtra(CommonField.ADDRESS) ?: "" diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FeedbackActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FeedbackActivity.kt deleted file mode 100644 index 1a68b79f5..000000000 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/FeedbackActivity.kt +++ /dev/null @@ -1,235 +0,0 @@ -package com.tencent.iot.explorer.link.kitlink.activity - -import android.Manifest -import android.content.Context -import android.content.Intent -import android.text.Editable -import android.text.TextUtils -import android.text.TextWatcher -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import androidx.recyclerview.widget.LinearLayoutManager -import com.tencent.iot.explorer.link.R -import com.tencent.iot.explorer.link.kitlink.holder.FeedbackViewHolder -import com.tencent.iot.explorer.link.kitlink.popup.CameraPopupWindow -import com.tencent.iot.explorer.link.kitlink.util.HttpRequest -import com.tencent.iot.explorer.link.core.auth.callback.MyCallback -import com.tencent.iot.explorer.link.mvp.model.UploadModel -import com.tencent.iot.explorer.link.mvp.view.UploadView -import com.tencent.cos.xml.exception.CosXmlClientException -import com.tencent.iot.explorer.link.core.log.L -import com.tencent.iot.explorer.link.T -import com.tencent.iot.explorer.link.core.auth.response.BaseResponse -import com.tencent.iot.explorer.link.customview.recyclerview.CRecyclerView -import com.tencent.iot.explorer.link.kitlink.util.picture.imp.ImageSelectorUtils -import kotlinx.android.synthetic.main.activity_feedback.* -import kotlinx.android.synthetic.main.menu_back_layout.* -import java.lang.StringBuilder - -class FeedbackActivity : BaseActivity(), UploadView, CRecyclerView.RecyclerItemView, MyCallback { - - private var isCommit = false - private var popupWindow: CameraPopupWindow? = null - private lateinit var uploadModel: UploadModel - private val successList = arrayListOf() - private val maxSize = 4 - private val emptyEntity = PathUrlEntity("", "") - private var mPosition = 0 - - private var permissions = arrayOf( - Manifest.permission.CAMERA, - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - - override fun getContentView(): Int { - return R.layout.activity_feedback - } - - - override fun initView() { - successList.add(emptyEntity) - uploadModel = UploadModel(this) - tv_title.text = getString(R.string.feedback) - crv_feedback.layoutManager = - LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) - crv_feedback.setList(successList) - crv_feedback.addRecyclerItemView(this) - } - - override fun setListener() { - iv_back.setOnClickListener { finish() } - tv_feedback_commit.setOnClickListener { - commit() - } - et_feedback_problem.addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable?) { - s?.let { - if (it.length <= 200) { - tv_feedback_count.text = "${it.length}/200" - return - } - } - } - - override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { - } - - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { - } - - }) - } - - override fun getViewType(position: Int): Int { - return 0 - } - - override fun getViewHolder(parent: ViewGroup, viewType: Int): CRecyclerView.CViewHolder<*> { - return FeedbackViewHolder( - LayoutInflater.from(this).inflate(R.layout.item_feekback, parent, false) - ) - } - - override fun doAction( - viewHolder: CRecyclerView.CViewHolder<*>, - clickView: View, - position: Int - ) { - if (clickView is ImageView) { - deleteFile(position) - return - } - if (successList.size > 4) return - mPosition = position - if (checkPermissions(permissions)) - showCameraPopup(mPosition) - else requestPermission(permissions) - - } - - private fun showCameraPopup(position: Int) { - if (position < successList.size - 1) return - if (getCount() <= 0) return - if (popupWindow == null) { - popupWindow = CameraPopupWindow(this) - } - popupWindow?.setBg(feedback_popup_bg) - popupWindow?.show(feedback, getCount()) - } - - override fun permissionAllGranted() { - showCameraPopup(mPosition) - } - - private fun commit() { - if (isCommit) return - val problem = et_feedback_problem.text.trim().toString() - var phone = et_feedback_phone.text.trim().toString() - if (TextUtils.isEmpty(phone)) - phone = "13800138000" - if (TextUtils.isEmpty(problem)) { - T.show(getString(R.string.add_question)) //请填写问题描述 - return - } - if (problem.trim().length < 10) { - T.show(getString(R.string.question_desc_less_20_char)) //请填写不少于10个字的问题描述 - return - } - - HttpRequest.instance.feedback(problem, phone, getLogUrl(), this) - isCommit = true - } - - override fun fail(msg: String?, reqCode: Int) { - L.e(msg ?: "") - } - - override fun success(response: BaseResponse, reqCode: Int) { - isCommit = false - if (response.isSuccess()) { - T.show(getString(R.string.commit_success)) //提交成功 - et_feedback_problem.setText("") - et_feedback_phone.setText("") - successList.clear() - successList.add(emptyEntity) - successAll() - } else { - T.show(response.msg) - } - } - - /** - * 上传图片第一步:获取签名 - */ - private fun upload(context: Context, srcPath: List) { - uploadModel.uploadMultiFile(context, srcPath) - } - - override fun successAll() { - runOnUiThread { - crv_feedback.notifyDataChanged() - } - } - - override fun uploadSuccess(loadPath: String, fileUrl: String, all: Boolean) { - successList.add(successList.size - 1, PathUrlEntity(loadPath, fileUrl)) - if (successList.size > maxSize) { - for (i in maxSize until successList.size) { - successList.removeAt(i) - } - } - runOnUiThread { - crv_feedback.notifyDataChanged() - } - } - - - override fun uploadFail(loadPath: String, exception: CosXmlClientException?) { - L.e("上传失败:$loadPath") - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - if (data != null) { - val list = ImageSelectorUtils.getImageSelectorList(requestCode, resultCode, data) - if (list != null && list.size > 0) { - upload(this, list) - list.forEach { - L.e("图片地址:$it") - } - } - } - } - - private fun getLogUrl(): String { - val sb = StringBuilder() - successList.forEachIndexed { index, pathUrlEntity -> - sb.append(pathUrlEntity.url) - if (index < successList.size - 1) - sb.append(",") - } - return sb.toString() - } - - private fun deleteFile(position: Int) { - successList.removeAt(position) - if (!successList.contains(emptyEntity)) { - successList.add(emptyEntity) - } - crv_feedback.notifyDataChanged() - } - - private fun getCount(): Int { - val c = maxSize + 1 - successList.size - return if (c < 0) 0 else c - } - - inner class PathUrlEntity(path: String, url: String) { - var path = path - var url = url - } - -} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ForgotPasswordActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ForgotPasswordActivity.kt index 9429e37db..86e28d0e8 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ForgotPasswordActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ForgotPasswordActivity.kt @@ -8,6 +8,7 @@ import android.text.method.LinkMovementMethod import android.text.style.ClickableSpan import android.view.LayoutInflater import android.view.View +import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.kitlink.consts.SocketConstants @@ -31,7 +32,7 @@ import kotlinx.android.synthetic.main.menu_back_layout.* */ class ForgotPasswordActivity : PActivity(), ForgotPasswordView, View.OnClickListener { - private val ANDROID_ID = Utils.getAndroidID(T.getContext()) + private val ANDROID_ID = App.uuid private lateinit var presenter: ForgotPasswordPresenter private var forgotType = true @@ -61,8 +62,7 @@ class ForgotPasswordActivity : PActivity(), ForgotPasswordView, View.OnClickList false -> showEmailForgot() } } - - formatTipText() + iv_register_agreement.visibility = View.INVISIBLE } private fun initViewPager() { @@ -89,84 +89,6 @@ class ForgotPasswordActivity : PActivity(), ForgotPasswordView, View.OnClickList emailView.tv_forgot_to_phone.setOnClickListener(this) } - private fun formatTipText() { - val str = resources.getString(R.string.register_agree_1) - val partStr1 = resources.getString(R.string.register_agree_2) - val partStr2 = resources.getString(R.string.register_agree_3) - val partStr3 = resources.getString(R.string.register_agree_4) - var showStr = str + partStr1 + partStr2 + partStr3 - val spannable = SpannableStringBuilder(showStr) - spannable.setSpan(object : ClickableSpan() { - override fun onClick(widget: View) { - if (presenter.getCountryCode() == "86") { - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - val intent = Intent(this@ForgotPasswordActivity, WebActivity::class.java) - intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_2)) - var url = CommonField.POLICY_PREFIX - url += "?uin=$ANDROID_ID" - url += CommonField.SERVICE_POLICY_SUFFIX - intent.putExtra(CommonField.EXTRA_TEXT, url) - startActivity(intent) - } else { - OpensourceLicenseActivity.startWebWithExtra(this@ForgotPasswordActivity, getString(R.string.register_agree_2), CommonField.SERVICE_AGREEMENT_URL_CN_EN) - } - } else { - var url = "" - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - url = CommonField.SERVICE_AGREEMENT_URL_US_ZH - } else { - url = CommonField.SERVICE_AGREEMENT_URL_US_EN - } - OpensourceLicenseActivity.startWebWithExtra(this@ForgotPasswordActivity, getString(R.string.register_agree_2), url) - } - } - - override fun updateDrawState(ds: TextPaint) { - super.updateDrawState(ds) - ds.color = resources.getColor(R.color.blue_0066FF) - ds.setUnderlineText(false); - } - }, - str.length, str.length + partStr1.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - - spannable.setSpan(object : ClickableSpan() { - override fun onClick(widget: View) { - if (presenter.getCountryCode() == "86") { - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - val intent = Intent(this@ForgotPasswordActivity, WebActivity::class.java) - intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_4)) - var url = CommonField.POLICY_PREFIX - url += "?uin=$ANDROID_ID" - url += CommonField.PRIVACY_POLICY_SUFFIX - intent.putExtra(CommonField.EXTRA_TEXT, url) - startActivity(intent) - } else { - OpensourceLicenseActivity.startWebWithExtra(this@ForgotPasswordActivity, getString(R.string.register_agree_4), CommonField.PRIVACY_POLICY_URL_CN_EN) - } - } else { - var url = "" - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - url = CommonField.PRIVACY_POLICY_URL_US_ZH - } else { - url = CommonField.PRIVACY_POLICY_URL_US_EN - } - OpensourceLicenseActivity.startWebWithExtra(this@ForgotPasswordActivity, getString(R.string.register_agree_4), url) - } - } - - override fun updateDrawState(ds: TextPaint) { - super.updateDrawState(ds) - ds.color = resources.getColor(R.color.blue_0066FF) - ds.setUnderlineText(false); - } - - }, - showStr.length - partStr1.length, showStr.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - - tv_register_tip_click.setMovementMethod(LinkMovementMethod.getInstance()) - tv_register_tip_click.setText(spannable) - } - override fun onClick(v: View?) { when (v) { phoneView.tv_forgot_to_country, phoneView.iv_forgot_to_country -> { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GetCodeActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GetCodeActivity.kt index 2c18de276..d88090340 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GetCodeActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GetCodeActivity.kt @@ -1,13 +1,9 @@ package com.tencent.iot.explorer.link.kitlink.activity -import android.Manifest -import android.content.ClipboardManager -import android.content.Context import android.content.Intent import android.os.Handler import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.core.auth.util.Weak -import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.mvp.IPresenter @@ -15,16 +11,16 @@ import com.tencent.iot.explorer.link.mvp.presenter.GetCodePresenter import com.tencent.iot.explorer.link.mvp.view.GetCodeView import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.utils.KeyBoardUtils +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_get_code.* import kotlinx.android.synthetic.main.menu_back_layout.* /** * 验证码验证界面 */ -class GetCodeActivity : PActivity(), GetCodeView, ClipboardManager.OnPrimaryClipChangedListener { +class GetCodeActivity : PActivity(), GetCodeView { private lateinit var presenter: GetCodePresenter - private lateinit var clipboardManager: ClipboardManager companion object { const val TYPE = "type" @@ -33,14 +29,6 @@ class GetCodeActivity : PActivity(), GetCodeView, ClipboardManager.OnPrimaryClip const val COUNTRY_CODE = "country_code" } - private val permissions = arrayOf( - Manifest.permission.RECEIVE_SMS, - Manifest.permission.READ_SMS, - Manifest.permission.SEND_SMS - ) - - private var handler by Weak() - override fun getContentView(): Int { return R.layout.activity_get_code } @@ -50,12 +38,7 @@ class GetCodeActivity : PActivity(), GetCodeView, ClipboardManager.OnPrimaryClip } override fun initView() { - if (checkPermissions(permissions)) { - permissionAllGranted() - } presenter = GetCodePresenter(this) - //剪切板 - clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager tv_title.text = getString(R.string.verification_code) presenter.lockResend() getInitData() @@ -76,19 +59,6 @@ class GetCodeActivity : PActivity(), GetCodeView, ClipboardManager.OnPrimaryClip presenter.next() } } - clipboardManager.addPrimaryClipChangedListener(this) - } - - override fun onPrimaryClipChanged() { - if (clipboardManager.hasPrimaryClip()) { - clipboardManager.primaryClip?.let { - if (it.itemCount > 0) { - val code = it.getItemAt(0).text.toString() - L.e("验证码:$code") -// vcv_get_code.text = code - } - } - } } /** @@ -98,7 +68,7 @@ class GetCodeActivity : PActivity(), GetCodeView, ClipboardManager.OnPrimaryClip intent?.let { val action = it.getIntExtra(SetPasswordActivity.ACTION, -1) presenter.setCommonData( - it.getStringExtra(TYPE), + it.getStringExtra(TYPE).safe(), action ) when (action) { @@ -176,9 +146,6 @@ class GetCodeActivity : PActivity(), GetCodeView, ClipboardManager.OnPrimaryClip } override fun onDestroy() { - clipboardManager.removePrimaryClipChangedListener(this) - handler?.removeCallbacks(null) - handler = null super.onDestroy() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GuideActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GuideActivity.kt index 8376cc80d..b799ea76b 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GuideActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/GuideActivity.kt @@ -78,7 +78,7 @@ class GuideActivity : PActivity(), View.OnClickListener{ if (hits[0] >= SystemClock.uptimeMillis() - duration) { if (hits.size == 5) { // 获取AndroidID,并保存至剪切板 - Utils.copy(this, Utils.getAndroidID(T.getContext())) + Utils.copy(this, App.uuid) } } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/HelpWebViewActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/HelpWebViewActivity.kt index 338ca756e..eb75b5486 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/HelpWebViewActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/HelpWebViewActivity.kt @@ -26,8 +26,10 @@ import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.core.utils.FileUtils import com.tencent.iot.explorer.link.core.utils.PhotoUtils import com.tencent.iot.explorer.link.core.utils.Utils +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.util.HttpRequest +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.kitlink.webview.BridgeImpl import com.tencent.iot.explorer.link.kitlink.webview.JSBridgeKt import com.tencent.iot.explorer.link.kitlink.webview.WebCallBack @@ -47,8 +49,12 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { private var uploadMessageAboveL: ValueCallback>? = null private val FILE_CHOOSER_RESULT_CODE = 10000 private val FILE_CAMERA_RESULT_CODE = 9999 + private val FILE_CHOOSER_CAMERA_RESULT_CODE = 9998 private var configQuestionList = false + private var feedbackDevice = false + private var feedbackCategory = "" + private var permissionDialog: PermissionDialog? = null override fun getContentView(): Int { return R.layout.activity_help_feedback @@ -59,6 +65,12 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { if (intent.hasExtra(CommonField.CONFIG_QUESTION_LIST)) { configQuestionList = intent.getBooleanExtra(CommonField.CONFIG_QUESTION_LIST, false) } + if (intent.hasExtra(CommonField.FEEDBACK_DEVICE)) { + feedbackDevice = intent.getBooleanExtra(CommonField.FEEDBACK_DEVICE, false) + } + if (intent.hasExtra(CommonField.FEEDBACK_CATEGORY)) { + feedbackCategory = intent.getStringExtra(CommonField.FEEDBACK_CATEGORY).safe() + } initWebView() getAppGetTokenTicket() } @@ -102,10 +114,26 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { getAppGetTokenTicket() } else if (requestCode == PhotoUtils.RESULT_CODE_CAMERA && resultCode == Activity.RESULT_OK) { - //拍照并确定 - var bitmap = BitmapFactory.decodeFile(PhotoUtils.PATH_PHOTO) - var file = FileUtils.compressImage(bitmap) - uploadMessageAboveL?.onReceiveValue(arrayOf(Uri.fromFile(file))) +// if (checkCameraPermission(false)) { + //拍照并确定 + val bitmap = BitmapFactory.decodeFile(PhotoUtils.PATH_PHOTO) + val file = FileUtils.compressImage(this, bitmap) + uploadMessageAboveL?.onReceiveValue(arrayOf(Uri.fromFile(file))) +// } else { +// // 查看请求album权限的时间是否大于48小时 +// if (requestPermissionIsIn48Hours(CommonField.PERMISSION_ALBUM)) { +// T.show(resources.getString(R.string.permission_of_album_refuse)) +// } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { +// requestPermissions(permissions, FILE_CHOOSER_RESULT_CODE) +// } +// permissionDialog = PermissionDialog(this@HelpWebViewActivity, R.mipmap.permission_album ,getString(R.string.permission_album_lips), getString(R.string.permission_storage_help_center)) +// permissionDialog!!.show() +// +// // 记录请求album权限的时间 +// savePermission(CommonField.PERMISSION_ALBUM) +// } + } else if (requestCode == PhotoUtils.RESULT_CODE_PHOTO && resultCode == Activity.RESULT_OK) { onActivityResultAboveL(requestCode, resultCode, data); @@ -141,7 +169,7 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { var js = JSON.parse(response.data.toString()) as JSONObject var url ="https://iot.cloud.tencent.com/explorer-h5/help-center/?" + "&ticket=" + js[CommonField.TOKEN_TICKET] - url += "&uin=${Utils.getAndroidID(this)}" + url += "&uin=${App.uuid}" if (!App.isOEMApp()) { url += "&appID=" + T.getContext().applicationInfo.packageName } @@ -149,6 +177,9 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { if (configQuestionList) { url += "/#/pages/Functional/HelpCenter/QnAList/QnAList?genCateID=config7" } + if (feedbackDevice) { + url += "#/pages/User/Feedback/Feedback?cate=" + feedbackCategory + } help_web.loadUrl(url) } @@ -204,23 +235,23 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { } // 检查相机权限是否开启 - private fun checkCameraPermission(): Boolean { - - // android M(6.0) 以上检查存储权限以及相机权限 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && - (ContextCompat.checkSelfPermission(this@HelpWebViewActivity, - Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || - ContextCompat.checkSelfPermission(this@HelpWebViewActivity, - Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED)) { - - requestPermissions(arrayOf(Manifest.permission.CAMERA, - Manifest.permission.WRITE_SETTINGS, - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE), - FILE_CHOOSER_RESULT_CODE) - return false + private fun checkCameraPermission(isCamera: Boolean): Boolean { + + if (isCamera) {//相机 + // android M(6.0) 以上检查存储权限以及相机权限 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + (ContextCompat.checkSelfPermission(this@HelpWebViewActivity, + Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED)) { + return false + } + return true + } else {//相册 + // android M(6.0) 以上检查存储权限 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && ContextCompat.checkSelfPermission(this@HelpWebViewActivity, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + return false + } + return true } - return true } override fun onClick(v: View?) { @@ -235,6 +266,56 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { } } + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + permissionDialog?.dismiss() + permissionDialog = null + if (requestCode == FILE_CHOOSER_RESULT_CODE) { + if (grantResults.size > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + //拍照并确定 + val bitmap = BitmapFactory.decodeFile(PhotoUtils.PATH_PHOTO) + val file = FileUtils.compressImage(this, bitmap) + uploadMessageAboveL?.onReceiveValue(arrayOf(Uri.fromFile(file))) + } + } + for (i in permissions.indices) { + if (grantResults[i] == PackageManager.PERMISSION_DENIED) { + when (requestCode) { + FILE_CHOOSER_RESULT_CODE -> { + T.show(resources.getString(R.string.permission_of_album_refuse)) + } + FILE_CAMERA_RESULT_CODE -> { + T.show(resources.getString(R.string.permission_of_camera_refuse)) + } + FILE_CHOOSER_CAMERA_RESULT_CODE -> { + T.show(resources.getString(R.string.permission_of_camera_refuse)) + } + } + return + } + } + } + + //查看请求 permissionName 权限的时间是否大于48小时 + private fun requestPermissionIsIn48Hours(permissionName: String) :Boolean { + // 查看请求camera权限的时间是否大于48小时 + var cameraJsonString = Utils.getStringValueFromXml(T.getContext(), permissionName, permissionName) + var cameraJson: JSONObject? = JSONObject.parse(cameraJsonString) as JSONObject? + val lasttime = cameraJson?.getLong(permissionName) + return (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) + } + //记录请求 permissionName 权限的时间 + private fun savePermission(permissionName: String) { + // + var json = JSONObject() + json.put(permissionName, System.currentTimeMillis() / 1000) + Utils.setXmlStringValue(T.getContext(), permissionName, permissionName, json.toJSONString()) + } + val webChromeClient = object: WebChromeClient() { override fun onShowFileChooser (webView: WebView, filePathCallback: ValueCallback>, fileChooserParams: FileChooserParams?): Boolean { @@ -244,19 +325,50 @@ class HelpWebViewActivity: BaseActivity(), MyCallback, View.OnClickListener { if (fileChooserParams.isCaptureEnabled) { isCapture = true; - if (checkCameraPermission()) { + if (checkCameraPermission(true)) { PhotoUtils.startCamera(this@HelpWebViewActivity) } else { + + // 检查相机权限 + if (ContextCompat.checkSelfPermission(this@HelpWebViewActivity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { + // 查看请求camera权限的时间是否大于48小时 + if (requestPermissionIsIn48Hours(CommonField.PERMISSION_CAMERA)) { + T.show(resources.getString(R.string.permission_of_camera_refuse)) + return false + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(arrayOf(Manifest.permission.CAMERA), FILE_CAMERA_RESULT_CODE) + } + permissionDialog = PermissionDialog(this@HelpWebViewActivity, R.mipmap.permission_camera ,getString(R.string.permission_camera_lips), getString(R.string.permission_camera_help_center)) + permissionDialog!!.show() + + // 记录请求camera权限的时间 + savePermission(CommonField.PERMISSION_CAMERA) + } return false } } else { isCapture = false; - if (checkCameraPermission()) { - PhotoUtils.startAlbum(this@HelpWebViewActivity) - } else { - return false - } + PhotoUtils.startAlbum(this@HelpWebViewActivity) +// if (checkCameraPermission(false)) { +// PhotoUtils.startAlbum(this@HelpWebViewActivity) +// } else { +// // 查看请求album权限的时间是否大于48小时 +// if (requestPermissionIsIn48Hours(CommonField.PERMISSION_ALBUM)) { +// T.show(resources.getString(R.string.permission_of_album_refuse)) +// return false +// } +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { +// requestPermissions(permissions, FILE_CHOOSER_RESULT_CODE) +// } +// permissionDialog = PermissionDialog(this@HelpWebViewActivity, R.mipmap.permission_album ,getString(R.string.permission_album_lips), getString(R.string.permission_storage_help_center)) +// permissionDialog!!.show() +// +// // 记录请求album权限的时间 +// savePermission(CommonField.PERMISSION_ALBUM) +// return false +// } } return true } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LoginActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LoginActivity.kt index 830853f39..9774c2bdf 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LoginActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LoginActivity.kt @@ -1,18 +1,15 @@ package com.tencent.iot.explorer.link.kitlink.activity -import android.Manifest import android.content.Intent import android.os.Handler -import android.text.Editable -import android.text.TextUtils -import android.text.TextWatcher +import android.text.* +import android.text.method.LinkMovementMethod +import android.text.style.ClickableSpan import android.view.LayoutInflater import android.view.View import android.widget.TextView import androidx.core.text.isDigitsOnly import com.alibaba.fastjson.JSONObject -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tencent.iot.explorer.link.* import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.check.VerifyEdit @@ -39,7 +36,6 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin private lateinit var accoutPasswdLoginView: View private lateinit var verifyCodeLoginView: View - private var mFirebaseAnalytics: FirebaseAnalytics? = null private var fromTag = "" private var accountType = false //true为手机号,false为邮箱 private var accountForAutoFill = "" @@ -47,11 +43,8 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin private var canGetCode = true private var handler: Handler = Handler() - private val permissions = arrayOf( - Manifest.permission.RECEIVE_SMS, - Manifest.permission.READ_SMS, - Manifest.permission.SEND_SMS - ) + private var agreement = false + private val ANDROID_ID = App.uuid override fun onResume() { super.onResume() @@ -72,23 +65,14 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin override fun initView() { App.data.regionId = "1" App.data.region = "ap-guangzhou" - if (!checkPermissions(permissions)) { - requestPermission(permissions) - } else { - permissionAllGranted() - } intent.getStringExtra("from")?.let { fromTag = it } - mFirebaseAnalytics = FirebaseAnalytics.getInstance(this@LoginActivity) presenter = LoginPresenter(this) tv_title.text = getString(R.string.verify_code_login) initViewPager() if (!TextUtils.isEmpty(App.data.getToken())) { - val userId = SharePreferenceUtil.getString(this@LoginActivity, App.CONFIG, CommonField.USER_ID) - FirebaseCrashlytics.getInstance().setUserId(userId) - mFirebaseAnalytics!!.setUserId(userId) startActivity(Intent(this, MainActivity::class.java)) finish() return @@ -98,6 +82,89 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin verifyCodeLoginView.tv_login_to_country_byverifycode.text = getString(R.string.country_china) + getString(R.string.conutry_code_num, presenter.getCountryCode()) loadLastCountryInfo() + + formatTipText() + + iv_login_agreement.setImageResource(R.mipmap.icon_unselected) + iv_login_agreement_status.visibility = View.GONE + + } + + private fun formatTipText() { + val str = resources.getString(R.string.register_agree_1) + val partStr1 = resources.getString(R.string.register_agree_2) + val partStr2 = resources.getString(R.string.register_agree_3) + val partStr3 = resources.getString(R.string.register_agree_4) + var showStr = str + partStr1 + partStr2 + partStr3 + val spannable = SpannableStringBuilder(showStr) + spannable.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + if (presenter.getCountryCode() == "86") { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@LoginActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_2)) + var url = CommonField.POLICY_PREFIX + url += "?uin=$ANDROID_ID" + url += CommonField.SERVICE_POLICY_SUFFIX + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@LoginActivity, getString(R.string.register_agree_2), CommonField.SERVICE_AGREEMENT_URL_CN_EN) + } + } else { + var url = "" + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + url = CommonField.SERVICE_AGREEMENT_URL_US_ZH + } else { + url = CommonField.SERVICE_AGREEMENT_URL_US_EN + } + OpensourceLicenseActivity.startWebWithExtra(this@LoginActivity, getString(R.string.register_agree_2), url) + } + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.color = resources.getColor(R.color.blue_0066FF) + ds.setUnderlineText(false); + } + }, + str.length, str.length + partStr1.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + + spannable.setSpan(object : ClickableSpan() { + override fun onClick(widget: View) { + if (presenter.getCountryCode() == "86") { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@LoginActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_4)) + var url = CommonField.PRIVACY_POLICY_URL_CN_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@LoginActivity, getString(R.string.register_agree_4), CommonField.PRIVACY_POLICY_URL_CN_EN) + } + } else { + var url = "" + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + url = CommonField.PRIVACY_POLICY_URL_US_ZH + } else { + url = CommonField.PRIVACY_POLICY_URL_US_EN + } + OpensourceLicenseActivity.startWebWithExtra(this@LoginActivity, getString(R.string.register_agree_4), url) + } + + } + + override fun updateDrawState(ds: TextPaint) { + super.updateDrawState(ds) + ds.color = resources.getColor(R.color.blue_0066FF) + ds.setUnderlineText(false); + } + + }, + str.length + partStr1.length + partStr2.length, str.length + partStr1.length + partStr2.length + partStr3.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + + tv_login_tip.setMovementMethod(LinkMovementMethod.getInstance()) + tv_login_tip.setText(spannable) } private fun loadLastCountryInfo() { @@ -158,6 +225,8 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin verifyCodeLoginView.et_login_phone_or_email_byverifycode.setText("") verifyCodeLoginView.et_login_phone_or_email_verifycode.setText("") iv_wechat_login.setOnClickListener(this) + + iv_login_agreement.setOnClickListener(this) } private fun pwd2Check() { @@ -269,17 +338,29 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin showVerifyCodeLogin() } iv_wechat_login -> {// 微信登录 + if (!agreement) { + T.show(getString(R.string.toast_register_agreement)) + return + } WeChatLogin.getInstance().login(this, this) } accoutPasswdLoginView.btn_account_passwd_login -> {// 密码登录 val account = accoutPasswdLoginView.et_login_phone_or_email.text.trim().toString() val passwd = accoutPasswdLoginView.et_login_phone_or_email_passwd.text.trim().toString() if (!account.contains("@")) { // 手机登录 - presenter.setPhoneData(account, passwd) - presenter.phoneCommit() + if (agreement) { + presenter.setPhoneData(account, passwd) + presenter.phoneCommit() + } else { + T.show(getString(R.string.toast_register_agreement)) + } } else { // 邮箱登录 - presenter.setEmailData(account, passwd) - presenter.emailCommit() + if (agreement) { + presenter.setEmailData(account, passwd) + presenter.emailCommit() + } else { + T.show(getString(R.string.toast_register_agreement)) + } } KeyBoardUtils.hideKeyBoard(this, accoutPasswdLoginView.et_login_phone_or_email_passwd) } @@ -289,16 +370,24 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin if (!account.contains("@")) {// 手机登录 presenter.setPhone(account) if (!TextUtils.isEmpty(verifycode)) { - presenter.setVerifyCode(verifycode) - presenter.phoneVerifyCodeCommit() + if (agreement) { + presenter.setVerifyCode(verifycode) + presenter.phoneVerifyCodeCommit() + } else { + T.show(getString(R.string.toast_register_agreement)) + } } else { T.show(getString(R.string.phone_verifycode_empty)) } } else {// 邮箱登录 presenter.setEmail(account) if (!TextUtils.isEmpty(verifycode)) { - presenter.setVerifyCode(verifycode) - presenter.emailVerifyCodeCommit() + if (agreement) { + presenter.setVerifyCode(verifycode) + presenter.emailVerifyCodeCommit() + } else { + T.show(getString(R.string.toast_register_agreement)) + } } else { T.show(getString(R.string.email_verifycode_empty)) } @@ -318,6 +407,10 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin if (!canGetCode) { return } + if (!agreement) { + T.show(getString(R.string.toast_register_agreement)) + return + } Utils.startCountBySeconds(60, secondsCountDownCallback) canGetCode = false enableTextView(verifyCodeLoginView.tv_get_verify_code, @@ -341,6 +434,16 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin verifyCodeLoginView.tv_login_to_country_byverifycode, verifyCodeLoginView.iv_login_to_country_byverifycode -> {// 验证码登录时选则国家 startActivityForResult(Intent(this, RegionActivity::class.java), 100) } + iv_login_agreement -> { + agreement = !agreement + if (agreement) { + iv_login_agreement.setImageResource(R.mipmap.readed) + iv_login_agreement_status.visibility = View.VISIBLE + } else { + iv_login_agreement.setImageResource(R.mipmap.icon_unselected) + iv_login_agreement_status.visibility = View.GONE + } + } } } @@ -390,8 +493,6 @@ class LoginActivity : PActivity(), LoginView, View.OnClickListener, WeChatLogin response.parse(UserInfoResponse::class.java)?.Data?.run { App.data.userInfo = this SharePreferenceUtil.saveString(this@LoginActivity, App.CONFIG, CommonField.USER_ID, App.data.userInfo.UserID) - FirebaseCrashlytics.getInstance().setUserId(App.data.userInfo.UserID) - mFirebaseAnalytics?.setUserId(App.data.userInfo.UserID) saveUser(user) T.show(getString(R.string.login_success)) if (TextUtils.isEmpty(fromTag)) { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LogoutActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LogoutActivity.kt index 451da56cd..ce47b0dec 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LogoutActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/LogoutActivity.kt @@ -26,7 +26,7 @@ class LogoutActivity : PActivity(), LogoutView, View.OnClickListener{ private lateinit var presenter: LogoutPresenter - private val ANDROID_ID = Utils.getAndroidID(T.getContext()) + private val ANDROID_ID = App.uuid override fun getPresenter(): IPresenter? { return presenter diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MainActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MainActivity.kt index 297df1881..e6de61a8b 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MainActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MainActivity.kt @@ -5,8 +5,8 @@ import android.app.Activity import android.app.AlertDialog import android.content.DialogInterface import android.content.Intent -import android.net.Uri import android.text.TextUtils +import android.util.Log import android.view.View import android.view.animation.Animation import android.view.animation.AnimationSet @@ -16,12 +16,6 @@ import androidx.fragment.app.Fragment import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONObject import com.example.qrcode.Constant -import com.example.qrcode.ScannerActivity -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.tencent.android.tpush.XGIOperateCallback -import com.tencent.android.tpush.XGPushConfig -import com.tencent.android.tpush.XGPushManager import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.BuildConfig import com.tencent.iot.explorer.link.R @@ -30,6 +24,8 @@ import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.entity.FamilyEntity import com.tencent.iot.explorer.link.core.auth.response.BaseResponse import com.tencent.iot.explorer.link.core.auth.util.JsonManager +import com.tencent.iot.explorer.link.core.link.entity.BleDeviceFirmwareVersion +import com.tencent.iot.explorer.link.core.link.entity.BleDeviceProperty import com.tencent.iot.explorer.link.core.link.entity.DeviceInfo import com.tencent.iot.explorer.link.core.link.entity.TrtcDeviceInfo import com.tencent.iot.explorer.link.core.log.L @@ -55,7 +51,6 @@ import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.LogcatHelper import com.tencent.iot.explorer.link.kitlink.util.RequestCode import com.tencent.iot.explorer.link.mvp.IPresenter -import com.tencent.tpns.baseapi.XGApiConfig import kotlinx.android.synthetic.main.activity_main.* import java.util.* import kotlin.system.exitProcess @@ -70,21 +65,15 @@ class MainActivity : PActivity(), MyCallback { private var familyPopup: FamilyListPopup? = null - private var isForceUpgrade = true + private var permissionDialog: PermissionDialog? = null - private var permissions = arrayOf( - Manifest.permission.CAMERA, - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.RECORD_AUDIO - ) + private var isForceUpgrade = true private var scanPermissions = arrayOf( Manifest.permission.CAMERA, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.CHANGE_WIFI_STATE, - Manifest.permission.CHANGE_WIFI_MULTICAST_STATE, - Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.CHANGE_WIFI_MULTICAST_STATE ) override fun getContentView(): Int { @@ -147,26 +136,8 @@ class MainActivity : PActivity(), MyCallback { } override fun initView() { - val userId = SharePreferenceUtil.getString(this@MainActivity, App.CONFIG, CommonField.USER_ID) - FirebaseCrashlytics.getInstance().setUserId(userId) - FirebaseAnalytics.getInstance(this).setUserId(userId) - openXGPush() home_bottom_view.addUnclickAbleItem(2) // 限定2号位置不可选中 - if (!checkPermissions(permissions)) { - var dlg = PermissionDialog(this@MainActivity, getString(R.string.permission_of_mic_camera), getString(R.string.permission_of_mic_camera_lips)) - dlg.show() - dlg.setOnDismisListener(object : PermissionDialog.OnDismisListener { - override fun OnClickRefuse() { - - } - - override fun OnClickOK() { - requestPermission(permissions) - } - - }) - } - LogcatHelper.getInstance(this).start() +// LogcatHelper.getInstance(this).start() home_bottom_view.addMenu( BottomItemEntity( getString(R.string.main_tab_1), @@ -254,7 +225,23 @@ class MainActivity : PActivity(), MyCallback { intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC, true) startActivityForResult(intent, CommonField.QR_CODE_REQUEST_CODE) } else { + // 查看请求camera权限的时间是否大于48小时 + var cameraJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA) + var cameraJson: JSONObject? = JSONObject.parse(cameraJsonString) as JSONObject? + val lasttime = cameraJson?.getLong(CommonField.PERMISSION_CAMERA) + if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { + T.show(getString(R.string.permission_of_camera_refuse)) + return + } + permissionDialog = PermissionDialog(App.activity, R.mipmap.permission_camera ,getString(R.string.permission_camera_lips), getString(R.string.permission_camera)) + permissionDialog!!.show() requestPermission(scanPermissions) + + // 记录请求camera权限的时间 + var json = JSONObject() + json.put(CommonField.PERMISSION_CAMERA, System.currentTimeMillis() / 1000) + Utils.setXmlStringValue(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA, json.toJSONString()) + } } } @@ -306,6 +293,8 @@ class MainActivity : PActivity(), MyCallback { intent.putExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC,true) startActivityForResult(intent, CommonField.QR_CODE_REQUEST_CODE) } + permissionDialog?.dismiss() + permissionDialog = null } } @@ -317,34 +306,6 @@ class MainActivity : PActivity(), MyCallback { } } - private fun openXGPush() { - XGPushConfig.init(applicationContext) - if (App.data.regionId == "1") {// 中国大陆 - XGPushConfig.setAccessId(applicationContext, - if (BuildConfig.XgAccessId.toLongOrNull() == null) 0 else BuildConfig.XgAccessId.toLong()) - XGPushConfig.setAccessKey(applicationContext, BuildConfig.XgAccessKey) - XGApiConfig.setServerSuffix(applicationContext, CommonField.XG_ACCESS_POINT_CHINA) - } else if (App.data.regionId == "22") {// 美国 - XGPushConfig.setAccessId(applicationContext, - if (BuildConfig.XgUSAAccessId.toLongOrNull() == null) 0 else BuildConfig.XgUSAAccessId.toLong()) - XGPushConfig.setAccessKey(applicationContext, BuildConfig.XgUSAAccessKey) - XGApiConfig.setServerSuffix(applicationContext, CommonField.XG_ACCESS_POINT_USA) - } - XGPushManager.registerPush(applicationContext, object : XGIOperateCallback { - override fun onSuccess(data: Any?, p1: Int) { - L.e("注册成功,设备token为:$data") - data?.let { - App.data.xg_token = it.toString() - bindXG() - } - } - - override fun onFail(data: Any?, errCode: Int, msg: String?) { - L.e("注册失败,错误码:$errCode ,错误信息:$msg") - } - }) - } - /** * 绑定信鸽推送 */ @@ -459,7 +420,7 @@ class MainActivity : PActivity(), MyCallback { override fun onDestroy() { unbindXG() super.onDestroy() - LogcatHelper.getInstance(this).stop() +// LogcatHelper.getInstance(this).stop() } private var timestamp = 0L diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MarkerPaddingActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MarkerPaddingActivity.java deleted file mode 100644 index f210926b8..000000000 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/MarkerPaddingActivity.java +++ /dev/null @@ -1,539 +0,0 @@ -package com.tencent.iot.explorer.link.kitlink.activity; - -import android.Manifest.permission; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.graphics.Point; -import android.os.Bundle; -import android.os.Looper; -import android.text.TextUtils; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONException; -import com.alibaba.fastjson.JSONObject; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.scwang.smart.refresh.footer.ClassicsFooter; -import com.scwang.smart.refresh.layout.SmartRefreshLayout; -import com.scwang.smart.refresh.layout.api.RefreshLayout; -import com.scwang.smart.refresh.layout.listener.OnRefreshLoadMoreListener; -import com.tencent.iot.explorer.link.R; -import com.tencent.iot.explorer.link.T; -import com.tencent.iot.explorer.link.core.auth.callback.MyCallback; -import com.tencent.iot.explorer.link.core.auth.http.HttpCallBack; -import com.tencent.iot.explorer.link.core.auth.http.HttpUtil; -import com.tencent.iot.explorer.link.core.auth.response.BaseResponse; -import com.tencent.iot.explorer.link.kitlink.adapter.PostionsAdapter; -import com.tencent.iot.explorer.link.kitlink.consts.CommonField; -import com.tencent.iot.explorer.link.kitlink.entity.AdInfo; -import com.tencent.iot.explorer.link.kitlink.entity.Address; -import com.tencent.iot.explorer.link.kitlink.entity.Location; -import com.tencent.iot.explorer.link.kitlink.entity.LocationResp; -import com.tencent.iot.explorer.link.kitlink.entity.Postion; -import com.tencent.iot.explorer.link.kitlink.util.HttpRequest; -import com.tencent.iot.explorer.link.kitlink.util.StatusBarHeightView; -import com.tencent.map.geolocation.TencentLocation; -import com.tencent.map.geolocation.TencentLocationListener; -import com.tencent.map.geolocation.TencentLocationManager; -import com.tencent.map.geolocation.TencentLocationRequest; -import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory; -import com.tencent.tencentmap.mapsdk.maps.MapView; -import com.tencent.tencentmap.mapsdk.maps.TencentMap; -import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory; -import com.tencent.tencentmap.mapsdk.maps.model.CameraPosition; -import com.tencent.tencentmap.mapsdk.maps.model.LatLng; -import com.tencent.tencentmap.mapsdk.maps.model.Marker; -import com.tencent.tencentmap.mapsdk.maps.model.MarkerOptions; - -import org.jetbrains.annotations.NotNull; - -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; - -public class MarkerPaddingActivity extends BaseActivity implements TencentMap.OnCameraChangeListener, View.OnClickListener, TencentLocationListener { - - private final int PAGE_SIZE = 20; - private final String[] permissions = {permission.ACCESS_COARSE_LOCATION, permission.READ_PHONE_STATE, permission.WRITE_EXTERNAL_STORAGE}; - public MapView mMapView; - private TencentMap mTencentMap; - private RecyclerView recyclerView; - private BottomSheetBehavior behavior; - private View bottomSheet; - private int heightPixels = 0; - private int widthPixels = 0; - private int mapMinHeight = 0; - private ConstraintLayout mapcontain; - private ConstraintLayout mapLayout; - private Marker marker = null; - private LatLng startPostion = null; - private String tencentMapKey = ""; - private List postions = new CopyOnWriteArrayList(); - private PostionsAdapter adapter = null; - private TextView title; - private StatusBarHeightView menuFamilyAddress; - private volatile int pageSize = 1; - private ImageView backBtn; - private ConstraintLayout searchLayout; - private ConstraintLayout layoutSeachLayout; - private TextView okBtn; - private LinearLayoutManager linearLayoutManager; - private String defaultAddress = ""; - private String familyId = ""; - private String familyName = ""; - private ImageView reloc; - private volatile boolean requestFlag = true; - private SmartRefreshLayout smartRefreshLayout = null; - private volatile boolean firstZoom = true; - private TencentLocationManager locationManager = null; - private TencentLocationRequest locationRequest = null; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - try { - tencentMapKey = this.getPackageManager().getApplicationInfo(this.getPackageName(), PackageManager.GET_META_DATA) - .metaData.getString("TencentMapSDK"); - } catch (PackageManager.NameNotFoundException e) { - tencentMapKey = ""; - e.printStackTrace(); - } - - Bundle bundle = getIntent().getBundleExtra(CommonField.ADDRESS); - if (bundle != null) { - defaultAddress = bundle.getString(CommonField.ADDRESS, ""); - familyId = bundle.getString(CommonField.FAMILY_ID, ""); - familyName = bundle.getString(CommonField.FAMILY_NAME, ""); - } - - initView(); - setListener(); - autoLocate(defaultAddress); - } - - public void autoLocate(String address) { - if (checkPermissions(permissions)) { - if (TextUtils.isEmpty(address)) { - startLocation(); - } else { - tagPostionByAddress(address); - } - } else { - requestPermission(permissions); - } - } - - @Override - public void onRequestPermissionsResult(int requestCode, @NotNull String[] permissions, @NotNull int[] grantResults) { - for (int i = 0; i < permissions.length; i++) { - if (grantResults[i] == PackageManager.PERMISSION_DENIED) { - permissionDenied(permissions[i]); - return; - } - } - permissionAllGranted(); - } - - @Override - public void permissionAllGranted() { - autoLocate(defaultAddress); - } - - private void tagPostionByAddress(String address) { - try { - Address loc = JSONObject.parseObject(address, Address.class); - if (loc.getLatitude() == 0f && loc.getLongitude() == 0f) { - startLocation(); - return; - } - startPostion = new LatLng(loc.getLatitude(), loc.getLongitude()); - mTencentMap.moveCamera(CameraUpdateFactory.newLatLngZoom(startPostion, 16)); - if (marker != null) marker.remove(); - marker = mTencentMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)).position(startPostion)); - - } catch (JSONException e) { - e.printStackTrace(); - startLocation(); - } - } - - private void startLocation() { - //用于访问腾讯定位服务的类, 周期性向客户端提供位置更新 - locationManager = TencentLocationManager.getInstance(this); - //设置坐标系 - locationManager.setCoordinateType(TencentLocationManager.COORDINATE_TYPE_GCJ02); - //创建定位请求 - locationRequest = TencentLocationRequest.create(); - //设置定位周期(位置监听器回调周期)为3s - locationRequest.setInterval(3000); - //设置为true表示显示定位层并可触发定位,false表示隐藏定位层并不可触发定位,默认是false - mTencentMap.setMyLocationEnabled(true); - - int err = locationManager.requestLocationUpdates(locationRequest, this, Looper.myLooper()); - switch (err) { - case 1: - case 2: - case 3: - T.show(getString(R.string.location_failed)); - break; - } - } - - @Override - public void onLocationChanged(TencentLocation tencentLocation, int i, String s) { - if (tencentLocation != null && i == TencentLocation.ERROR_OK) { - android.location.Location location = new android.location.Location(tencentLocation.getProvider()); - //设置经纬度以及精度 - location.setLatitude(tencentLocation.getLatitude()); - location.setLongitude(tencentLocation.getLongitude()); - location.setAccuracy(tencentLocation.getAccuracy()); - startPostion = new LatLng(tencentLocation.getLatitude(), tencentLocation.getLongitude()); - mTencentMap.moveCamera(CameraUpdateFactory.newLatLngZoom(startPostion, 16)); - if (marker != null) marker.remove(); - marker = mTencentMap.addMarker(new MarkerOptions().icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED)).position(startPostion)); - - } else { - T.show(s); - } - locationManager.removeUpdates(this); - - } - - @Override - public void onStatusUpdate(String s, int i, String s1) {} - - public void initView() { - //获取屏幕高度 - heightPixels = getResources().getDisplayMetrics().heightPixels; - widthPixels = getResources().getDisplayMetrics().widthPixels; - mapMinHeight = dip2px(this, 180); - - title = findViewById(R.id.tv_title); - title.setText(R.string.map_select_postion); - recyclerView = findViewById(R.id.rec); - mapcontain = findViewById(R.id.mapcontain); - menuFamilyAddress = findViewById(R.id.menu_family_address); - mMapView = findViewById(R.id.map); - backBtn = findViewById(R.id.iv_back); - bottomSheet = findViewById(R.id.bottom_sheet); - searchLayout = findViewById(R.id.layout_seach); - okBtn = findViewById(R.id.btn_add_family); - behavior = BottomSheetBehavior.from(bottomSheet); - reloc = findViewById(R.id.iv_reset_loaction); - smartRefreshLayout = findViewById(R.id.smart_refreshLayout); - layoutSeachLayout = findViewById(R.id.layout_seach_layout); - mapLayout = findViewById(R.id.map_layout); - - smartRefreshLayout.setEnableRefresh(false); - smartRefreshLayout.setEnableLoadMore(true); - smartRefreshLayout.setRefreshFooter(new ClassicsFooter(this)); - linearLayoutManager = new LinearLayoutManager(this); - adapter = new PostionsAdapter(postions); - recyclerView.setAdapter(adapter); - recyclerView.setLayoutManager(linearLayoutManager); - mTencentMap = mMapView.getMap(); - - mapLayout.postDelayed(new Runnable() { - @Override - public void run() { - heightPixels = mapLayout.getHeight(); - widthPixels = mapLayout.getWidth(); - int height = mapLayout.getHeight() - mapLayout.getWidth() - menuFamilyAddress.getHeight(); - behavior.setPeekHeight(height); - } - }, 200); - } - - @Override - protected void onStart() { - mMapView.onStart(); - super.onStart(); - } - - @Override - protected void onResume() { - mMapView.onResume(); - super.onResume(); - } - - @Override - protected void onPause() { - mMapView.onPause(); - super.onPause(); - } - - @Override - protected void onStop() { - mMapView.onStop(); - super.onStop(); - } - - @Override - protected void onDestroy() { - mMapView.onDestroy(); - super.onDestroy(); - } - - public static Float px2dp(Context context, Float f) { - Float x = context.getResources().getDisplayMetrics().density; - return f / x; - } - - private static int dip2px(Context context, float dpValue) { - final float scale = context.getResources().getDisplayMetrics().density; - return (int) (dpValue * scale + 0.5f); - } - - @Override - public void onCameraChange(CameraPosition cameraPosition) { } - - @Override - public void onCameraChangeFinished(CameraPosition cameraPosition) { - if (firstZoom) { - firstZoom = false; - return; - } - Point point = mTencentMap.getProjection().toScreenLocation(cameraPosition.target); - if (marker != null) marker.setFixingPoint(point.x, point.y); - - if (!requestFlag) { //只调整指针位置不做网络请求 - requestFlag = true; - } else { //调整整指针位置,同时从第一页进行网络请求 - startPostion = cameraPosition.target; - getPostionInfo(startPostion); - } - } - - public void getPostionInfo(final LatLng latLng) { - runOnUiThread(new Runnable() { - @Override - public void run() { - postions.clear(); - pageSize = 1; - adapter.setSelectPos(0); - adapter.notifyDataSetChanged(); - linearLayoutManager.scrollToPosition(0); - getPostionInfo(latLng, pageSize); - } - }); - } - - private void moveCameraWithoutRequest(LatLng target) { - if (mTencentMap == null) return; - requestFlag = false; - mTencentMap.moveCamera(CameraUpdateFactory.newLatLng(target)); - } - - public void getPostionInfo(LatLng latLng, int page, final Address firstItem) { - StringBuilder sb = new StringBuilder("https://apis.map.qq.com/ws/geocoder/v1/?location="); - sb.append(latLng.latitude).append(",").append(latLng.longitude).append("&key=").append(tencentMapKey); - sb.append("&get_poi=1").append("&poi_options=page_size=" + PAGE_SIZE + ";page_index=" + page); - HttpUtil.INSTANCE.get(sb.toString(), new HttpCallBack() { - @Override public void onError(@NotNull String error) { } - @Override public void onSuccess(@NotNull String response) { - final LocationResp locationResp = JSONObject.parseObject(response, LocationResp.class); - - runOnUiThread(new Runnable() { - @Override - public void run() { - if (locationResp.getResult().getPois().size() > 0 && - (postions.size() + locationResp.getResult().getPois().size()) < locationResp.getResult().getPoi_count()) { - if (firstItem != null) { - initFirstItem(locationResp.getResult().getPois(), firstItem); - } - postions.addAll(locationResp.getResult().getPois()); - pageSize++; - } - adapter.notifyDataSetChanged(); - if (smartRefreshLayout != null) { - smartRefreshLayout.finishLoadMore(); - } - } - }); - } - }); - } - - public void getPostionInfo(LatLng latLng, int page) { - getPostionInfo(latLng, page, null); - } - - private void initFirstItem(List all, Address selectedPostion) { - Postion firstItem = new Postion(); - firstItem.setTitle(selectedPostion.getName()); - firstItem.setAddress(selectedPostion.getAddress()); - firstItem.setLocation(new Location()); - firstItem.getLocation().setLat(selectedPostion.getLatitude()); - firstItem.getLocation().setLng(selectedPostion.getLongitude()); - firstItem.setAd_info(new AdInfo()); - firstItem.getAd_info().setCity(selectedPostion.getCity()); - firstItem.setId(selectedPostion.getId()); - - for (int i = 0; i < all.size(); i++) { - if (all.get(i).getId().equals(firstItem.getId())) { - all.set(i, all.get(0)); - break; - } - } - all.set(0, firstItem); - } - - @Override - public int getContentView() { - return R.layout.activity_map_address; - } - - public void setListener() { - backBtn.setOnClickListener(this); - searchLayout.setOnClickListener(this); - okBtn.setOnClickListener(this); - reloc.setOnClickListener(this); - mTencentMap.setOnCameraChangeListener(this); - adapter.setOnItemClicked(new PostionsAdapter.OnItemClicked (){ - @Override - public void onItemClicked(int pos) { - adapter.setSelectPos(pos); - adapter.notifyDataSetChanged(); - LatLng target = new LatLng(adapter.getSelectPostion().getLocation().getLat(), adapter.getSelectPostion().getLocation().getLng()); - moveCameraWithoutRequest(target); - } - }); - mapcontain.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (adapter != null && adapter.getSelectPostion() != null && adapter.getSelectPostion().getLocation() != null) { - LatLng target = new LatLng(adapter.getSelectPostion().getLocation().getLat(), adapter.getSelectPostion().getLocation().getLng()); - moveCameraWithoutRequest(target); - } - } - }); - - behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { - @Override - public void onStateChanged(@NonNull View view, int newState) { - ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - int tagHeight = heightPixels - mapMinHeight - menuFamilyAddress.getHeight(); - if (bottomSheet.getHeight() > tagHeight) { - layoutParams.height = tagHeight; - bottomSheet.setLayoutParams(layoutParams); - } - - ViewGroup.LayoutParams mapLayoutParams = mapcontain.getLayoutParams(); - switch (newState) { - case 3: //处于完全展开的状态 - mapLayoutParams.height = mapMinHeight; - mapcontain.setLayoutParams(mapLayoutParams); - break; - case 4: //默认的折叠状态 - mapLayoutParams.height = widthPixels; - mapcontain.setLayoutParams(mapLayoutParams); - break; - } - } - - @Override - public void onSlide(@NonNull View view, float v) { } - }); - - smartRefreshLayout.setOnRefreshLoadMoreListener(new OnRefreshLoadMoreListener() { - @Override - public void onRefresh(@NonNull RefreshLayout refreshLayout) { - refreshLayout.finishRefresh(); - } - - @Override - public void onLoadMore(@NonNull RefreshLayout refreshLayout) { - if (startPostion == null || pageSize > 20) { - refreshLayout.finishLoadMore(); - return; - } - getPostionInfo(startPostion, pageSize); - } - }); - } - - @Override - public void onClick(View v) { - if (v.getId() == R.id.iv_back) { - finish(); - } else if (v.getId() == R.id.layout_seach) { - Intent postionIntent = new Intent(this, SelectPointActivity.class); - startActivityForResult(postionIntent, CommonField.SELECT_MAP_POSTION_REQ_CODE); - } else if (v.getId() == R.id.btn_add_family) { - if (TextUtils.isEmpty(familyId)) { // 新增房间的方式 - if (adapter != null) { - Intent data = new Intent(); - data.putExtra(CommonField.ADDRESS, JSON.toJSONString(adapter.getSelectPostion())); - setResult(RESULT_OK, data); - } - finish(); - - } else { // 修改房间地址的方式 - if (adapter != null && adapter.getSelectPostion() != null) { - Address address = new Address(); - address.setName(adapter.getSelectPostion().getTitle()); - address.setAddress(adapter.getSelectPostion().getAddress()); - if (adapter.getSelectPostion().getLocation() != null) { - address.setLatitude(adapter.getSelectPostion().getLocation().getLat()); - address.setLongitude(adapter.getSelectPostion().getLocation().getLng()); - } - if (adapter.getSelectPostion().getAd_info() != null) { - address.setCity(adapter.getSelectPostion().getAd_info().getCity()); - } - HttpRequest.Companion.getInstance().modifyFamily(familyId, familyName, - JSON.toJSONString(address), new MyCallback() { - @Override - public void fail(@org.jetbrains.annotations.Nullable String msg, int reqCode) { - T.show(msg); - } - - @Override - public void success(@NotNull BaseResponse response, int reqCode) { - if (response.isSuccess()) { - Intent data = new Intent(); - data.putExtra(CommonField.ADDRESS, JSON.toJSONString(adapter.getSelectPostion())); - setResult(RESULT_OK, data); - finish(); - } else { - T.show(response.getMsg()); - } - } - }); - } - } - - } else if (v.getId() == R.id.iv_reset_loaction) { - autoLocate(null); - } - } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (requestCode == CommonField.SELECT_MAP_POSTION_REQ_CODE && resultCode == Activity.RESULT_OK && data != null) { - String extraInfo = data.getStringExtra(CommonField.EXTRA_INFO); - Address selectedPostion = JSON.parseObject(extraInfo, Address.class); - LatLng target = new LatLng(selectedPostion.getLatitude(), selectedPostion.getLongitude()); - startPostion = new LatLng(target); - mTencentMap.moveCamera(CameraUpdateFactory.newLatLng(target)); - postions.clear(); - pageSize = 1; - adapter.setSelectPos(0); - linearLayoutManager.scrollToPosition(0); - getPostionInfo(target, pageSize, selectedPostion); - } - } -} - diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/PrivicyDialogActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/PrivicyDialogActivity.kt new file mode 100644 index 000000000..2c93f258f --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/PrivicyDialogActivity.kt @@ -0,0 +1,108 @@ +package com.tencent.iot.explorer.link.kitlink.activity + +import android.content.Intent +import android.text.TextUtils +import com.tencent.iot.explorer.link.App +import com.tencent.iot.explorer.link.R +import com.tencent.iot.explorer.link.core.utils.Utils +import com.tencent.iot.explorer.link.customview.dialog.UserAgreeDialog +import com.tencent.iot.explorer.link.kitlink.consts.CommonField + + +class PrivicyDialogActivity : BaseActivity() { + + private var dialog: UserAgreeDialog? = null + private val ANDROID_ID = App.uuid + + override fun getContentView(): Int { + return R.layout.activity_privicy_dialog + } + + override fun initView() { + if (!TextUtils.isEmpty(App.data.getToken())) { + startActivity(Intent(this, MainActivity::class.java)) + return + } + // 21.11.20 产品沟通这里展示默认的中国对应的协议。 + dialog = UserAgreeDialog(this@PrivicyDialogActivity) + dialog!!.show() + dialog!!.setOnDismisListener(object : UserAgreeDialog.OnDismisListener { + override fun onDismised() { + onBackPressed() + } + override fun onOkClicked() { + Utils.setXmlStringValue(this@PrivicyDialogActivity, CommonField.AGREED_RULE_FLAG, CommonField.AGREED_RULE_FLAG, "1") + finish() + App.toLogin() + } + + override fun onOkClickedUserAgreement() { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@PrivicyDialogActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_2)) + var url = CommonField.POLICY_PREFIX + url += "?uin=$ANDROID_ID" + url += CommonField.SERVICE_POLICY_SUFFIX + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@PrivicyDialogActivity, getString(R.string.register_agree_2), CommonField.SERVICE_AGREEMENT_URL_CN_EN) + } + } + + override fun onOkClickedPrivacyPolicy() { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@PrivicyDialogActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_4)) + var url = CommonField.PRIVACY_POLICY_URL_CN_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@PrivicyDialogActivity, getString(R.string.register_agree_4), CommonField.PRIVACY_POLICY_URL_CN_EN) + } + } + + override fun onOkClickedPersonalInfoList() { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@PrivicyDialogActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.personal_information_list)) + var url = CommonField.PERSONAL_INFO_URL_US_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@PrivicyDialogActivity, getString(R.string.personal_information_list), CommonField.PERSONAL_INFO_URL_US_EN) + } + } + + override fun onOkClickedThirdSDKList() { + if (Utils.getLang().contains(CommonField.ZH_TAG)) { + val intent = Intent(this@PrivicyDialogActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.rule_content_list)) + var url = CommonField.THIRD_SDK_URL_US_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } else { + OpensourceLicenseActivity.startWebWithExtra(this@PrivicyDialogActivity, getString(R.string.rule_content_list), CommonField.THIRD_SDK_URL_US_EN) + } + } + + override fun onClickedPrivacyPolicySummary() { + val intent = Intent(this@PrivicyDialogActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.summary_of_privacy_policy)) + var url = CommonField.PRIVACY_POLICY_SUMMARY_URL_CN_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) + } + }) + } + + override fun setListener() { + + } + + override fun onDestroy() { + dialog?.dismiss() + dialog = null + super.onDestroy() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ProductIntroduceActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ProductIntroduceActivity.kt index 25a2db8eb..05db55858 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ProductIntroduceActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ProductIntroduceActivity.kt @@ -13,6 +13,7 @@ import com.tencent.iot.explorer.link.kitlink.entity.* import com.tencent.iot.explorer.link.kitlink.response.ProductsConfigResponse import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.RequestCode +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_product_introducation.* import kotlinx.android.synthetic.main.menu_back_layout.* @@ -27,7 +28,7 @@ class ProductIntroduceActivity : BaseActivity(), MyCallback { override fun initView() { tv_title.setText(getString(R.string.bind_dev)) if (intent.hasExtra(CommonField.PRODUCT_ID)) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() HttpRequest.instance.getProductsConfig(arrayListOf(productId), this) } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RegisterActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RegisterActivity.kt index 79074b182..e93d4c94f 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RegisterActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RegisterActivity.kt @@ -17,7 +17,6 @@ import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.utils.KeyBoardUtils import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.dialog.InputBirthdayDialog -import com.tencent.iot.explorer.link.customview.dialog.UserAgreeDialog import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.consts.SocketConstants import com.tencent.iot.explorer.link.mvp.IPresenter @@ -34,12 +33,6 @@ import java.util.* */ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { - private val permissions = arrayOf( - Manifest.permission.RECEIVE_SMS, - Manifest.permission.READ_SMS, - Manifest.permission.SEND_SMS - ) - companion object { const val ACCOUNT_TYPE = "account_type" const val ACCOUNT_NUMBER = "account_number" @@ -52,7 +45,7 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { private lateinit var phoneView: View private lateinit var emailView: View - private val ANDROID_ID = Utils.getAndroidID(T.getContext()) + private val ANDROID_ID = App.uuid override fun getPresenter(): IPresenter? { return presenter @@ -85,6 +78,7 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { phoneView.et_register_phone.setText(account) } } + presenter.agreement() if (presenter.isAgreement()) { iv_register_agreement.setImageResource(R.mipmap.readed) iv_register_agreement_status.visibility = View.VISIBLE @@ -99,11 +93,11 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { phoneView.layout_phone_country.visibility = View.GONE emailView.layout_email_country.visibility = View.GONE - iv_register_agreement.visibility = View.INVISIBLE +// iv_register_agreement.visibility = View.INVISIBLE loadLastCountryInfo() // showBirthDayDlg() -// formatTipText() + formatTipText() checkIfShowAgreeDlg() } @@ -112,86 +106,9 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { agreed?.let { if (it == "1") { presenter.agreement() - if (!checkPermissions(permissions)) { - requestPermission(permissions) - } else { - permissionAllGranted() - } return@checkIfShowAgreeDlg } } - var dlg = UserAgreeDialog(this@RegisterActivity) - dlg.show() - dlg.setOnDismisListener(object : UserAgreeDialog.OnDismisListener { - override fun onDismised() { finish() } - override fun onOkClicked() { - presenter.agreement() - Utils.setXmlStringValue(this@RegisterActivity, CommonField.AGREED_RULE_FLAG, CommonField.AGREED_RULE_FLAG, "1") - if (!checkPermissions(permissions)) { - requestPermission(permissions) - } else { - permissionAllGranted() - } - } - - override fun onOkClickedUserAgreement() { - if (presenter.getCountryCode() == "86") { - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - val intent = Intent(this@RegisterActivity, WebActivity::class.java) - intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_2)) - var url = CommonField.POLICY_PREFIX - url += "?uin=$ANDROID_ID" - url += CommonField.SERVICE_POLICY_SUFFIX - intent.putExtra(CommonField.EXTRA_TEXT, url) - startActivity(intent) - } else { - OpensourceLicenseActivity.startWebWithExtra(this@RegisterActivity, getString(R.string.register_agree_2), CommonField.SERVICE_AGREEMENT_URL_CN_EN) - } - } else { - var url = "" - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - url = CommonField.SERVICE_AGREEMENT_URL_US_ZH - } else { - url = CommonField.SERVICE_AGREEMENT_URL_US_EN - } - OpensourceLicenseActivity.startWebWithExtra(this@RegisterActivity, getString(R.string.register_agree_2), url) - } - } - - override fun onOkClickedPrivacyPolicy() { - if (presenter.getCountryCode() == "86") { - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - val intent = Intent(this@RegisterActivity, WebActivity::class.java) - intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_4)) - var url = CommonField.POLICY_PREFIX - url += "?uin=$ANDROID_ID" - url += CommonField.PRIVACY_POLICY_SUFFIX - intent.putExtra(CommonField.EXTRA_TEXT, url) - startActivity(intent) - } else { - OpensourceLicenseActivity.startWebWithExtra(this@RegisterActivity, getString(R.string.register_agree_4), CommonField.PRIVACY_POLICY_URL_CN_EN) - } - } else { - var url = "" - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - url = CommonField.PRIVACY_POLICY_URL_US_ZH - } else { - url = CommonField.PRIVACY_POLICY_URL_US_EN - } - OpensourceLicenseActivity.startWebWithExtra(this@RegisterActivity, getString(R.string.register_agree_4), url) - } - } - - override fun onOkClickedThirdSDKList() { - var url = "" - if (Utils.getLang().contains(CommonField.ZH_TAG)) { - url = CommonField.THIRD_SDK_URL_US_ZH - } else { - url = CommonField.THIRD_SDK_URL_US_EN - } - OpensourceLicenseActivity.startWebWithExtra(this@RegisterActivity, getString(R.string.rule_content_list), url) - } - }) } private fun formatTipText() { @@ -240,9 +157,7 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { if (Utils.getLang().contains(CommonField.ZH_TAG)) { val intent = Intent(this@RegisterActivity, WebActivity::class.java) intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.register_agree_4)) - var url = CommonField.POLICY_PREFIX - url += "?uin=$ANDROID_ID" - url += CommonField.PRIVACY_POLICY_SUFFIX + var url = CommonField.PRIVACY_POLICY_URL_CN_ZH intent.putExtra(CommonField.EXTRA_TEXT, url) startActivity(intent) } else { @@ -267,7 +182,7 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { } }, - showStr.length - partStr1.length, showStr.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) + str.length + partStr1.length + partStr2.length, str.length + partStr1.length + partStr2.length + partStr3.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) tv_register_tip.setMovementMethod(LinkMovementMethod.getInstance()) tv_register_tip.setText(spannable) @@ -453,14 +368,14 @@ class RegisterActivity : PActivity(), RegisterView, View.OnClickListener { private fun showBirthDayDlg() { if (presenter.getCountryCode() == "1" && shouldShowBirthdayDlg()) { - var dlg = InputBirthdayDialog(this@RegisterActivity) + var dlg = InputBirthdayDialog(this@RegisterActivity, presenter.getCountryCode()) dlg.show() dlg.setOnDismissListener(object: InputBirthdayDialog.OnDismisListener { override fun onOkClicked(year: Int, month: Int, day: Int) { // 是否满13周岁 if (!ifOver13YearsOld(year, month, day)) { - T.show(resources.getString(R.string.too_young_to_use)) + T.show(resources.getString(R.string.usa_too_young_to_use)) finish() return } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RoomActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RoomActivity.kt index 1c727f240..0e2062752 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RoomActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/RoomActivity.kt @@ -17,6 +17,7 @@ import com.tencent.iot.explorer.link.kitlink.popup.CommonPopupWindow import com.tencent.iot.explorer.link.kitlink.popup.EditPopupWindow import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.RequestCode +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_room.* import kotlinx.android.synthetic.main.menu_back_layout.* @@ -145,7 +146,7 @@ class RoomActivity : BaseActivity(), MyCallback { super.onActivityResult(requestCode, resultCode, data) if (requestCode == CommonField.EDIT_NAME_REQ_CODE && resultCode == Activity.RESULT_OK && data != null) { - var extraInfo = data?.getStringExtra(CommonField.EXTRA_TEXT) + val extraInfo = data.getStringExtra(CommonField.EXTRA_TEXT).safe() modifyRoomName = extraInfo modifyRoomName() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ScannerActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ScannerActivity.java new file mode 100644 index 000000000..d3b3d3a80 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/ScannerActivity.java @@ -0,0 +1,345 @@ +package com.tencent.iot.explorer.link.kitlink.activity; + +import android.Manifest; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.graphics.Bitmap; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; + +import android.provider.MediaStore; +import android.util.Log; +import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuItem; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.example.qrcode.Constant; +import com.example.qrcode.camera.CameraManager; +import com.example.qrcode.decode.InactivityTimer; +import com.example.qrcode.decode.ScannerHandler; +import com.example.qrcode.utils.CommonUtils; +import com.example.qrcode.utils.DecodeUtils; +import com.example.qrcode.utils.UriUtils; +import com.example.qrcode.view.ScannerView; +import com.google.zxing.BarcodeFormat; +import com.google.zxing.Result; +import com.tencent.iot.explorer.link.R; +import com.tencent.iot.explorer.link.T; +import com.tencent.iot.explorer.link.core.utils.Utils; +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog; +import com.tencent.iot.explorer.link.kitlink.consts.CommonField; +import com.tencent.iot.explorer.link.kitlink.util.BeepManager; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Set; + + +/** + * Created by yangyu on 17/10/18. + */ + +public class ScannerActivity extends com.example.qrcode.ScannerActivity implements SurfaceHolder.Callback { + private static final String TAG = "ScannerActivity"; + + public static final String BARCODE_FORMAT = "support_barcode_format"; + public final int PERMISSION_REQUEST_CODE_READ_EXTERNAL_STORAGE = 0X11; + public final int REQUEST_CODE_GET_PIC_URI = 0X12; + private final int MESSAGE_DECODE_FROM_BITMAP = 0; + + private Toolbar mToolBar; + private ScannerView mScannerView; + private SurfaceView mSurfaceView; + + private InactivityTimer mInactivityTimer; + private BeepManager beepManager; + + private CameraManager cameraManager; + private ScannerHandler handler; + private Collection decodeFormats; + + private int mScanFocusWidth; + private int mScanFocusHeight; + private int mScanFocusTopPadding; + + private boolean isEnableScanFromPicture; + private boolean hasSurface; + private MyHandler mHandler; + + private Uri picUri; + + private PermissionDialog permissionDialog = null; + + private static class MyHandler extends Handler { + private WeakReference activity; + + MyHandler(ScannerActivity mainActivityWeakReference) { + activity = new WeakReference(mainActivityWeakReference); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + ScannerActivity activity = this.activity.get(); + if (activity != null) { + if (msg.what == activity.MESSAGE_DECODE_FROM_BITMAP) { + Bitmap bm = (Bitmap) msg.obj; + DecodeUtils.DecodeAsyncTask decodeAsyncTask = new DecodeUtils.DecodeAsyncTask(activity); + decodeAsyncTask.execute(bm); + } + } + } + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(com.example.qrcode.R.layout.layout_activity_scanner); + initView(); + hasSurface = false; + Intent intent = getIntent(); + if (intent != null) { + mScanFocusWidth = intent.getIntExtra(Constant.EXTRA_SCANNER_FRAME_WIDTH, -1); + mScanFocusHeight = intent.getIntExtra(Constant.EXTRA_SCANNER_FRAME_HEIGHT, -1); + mScanFocusTopPadding = intent.getIntExtra(Constant.EXTRA_SCANNER_FRAME_TOP_PADDING, -1); + isEnableScanFromPicture = intent.getBooleanExtra(Constant.EXTRA_IS_ENABLE_SCAN_FROM_PIC, false); + Bundle b = intent.getExtras(); + if (b != null) { + HashMap formats = (HashMap) b.getSerializable(Constant.EXTRA_SCAN_CODE_TYPE); + if (formats != null) { + decodeFormats = formats.get(BARCODE_FORMAT); + } else { + decodeFormats = EnumSet.of(BarcodeFormat.QR_CODE + , BarcodeFormat.CODE_128); + } + } else { + decodeFormats = EnumSet.of(BarcodeFormat.QR_CODE + , BarcodeFormat.CODE_128); + } + + } + Log.e(TAG, "onCreate:decodeFormats :" + decodeFormats.size() + "--" + decodeFormats.toString()); + mInactivityTimer = new InactivityTimer(this); + beepManager = new BeepManager(this); + mHandler = new MyHandler(this); + } + + @Override + protected void onResume() { + super.onResume(); + cameraManager = new CameraManager(this); + cameraManager.setManualFramingRect(mScanFocusWidth, mScanFocusHeight, mScanFocusTopPadding); + mScannerView.setCameraManager(cameraManager); + SurfaceHolder holder = mSurfaceView.getHolder(); + + if (hasSurface) { + initCamera(holder); + } else { + holder.addCallback(this); + } + mInactivityTimer.onResume(); + beepManager.updatePrefs(); + } + + @Override + protected void onPause() { + super.onPause(); + if (handler != null) { + handler.quitSynchronously(); + handler = null; + } + cameraManager.closeDriver(); + mInactivityTimer.onPause(); + beepManager.close(); + } + + @Override + protected void onDestroy() { + cameraManager.clearFramingRect(); + mInactivityTimer.shutdown(); + super.onDestroy(); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_DOWN: + //关闭灯光 + cameraManager.setTorch(false); + return true; + case KeyEvent.KEYCODE_VOLUME_UP: + //开启闪光灯 + cameraManager.setTorch(true); + return true; + } + return super.onKeyDown(keyCode, event); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (isEnableScanFromPicture) { + getMenuInflater().inflate(com.example.qrcode.R.menu.menu_scan, menu); + return true; + } else { + return super.onCreateOptionsMenu(menu); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int itemId = item.getItemId(); + if (itemId == com.example.qrcode.R.id.scan_from_picture) { + goPicture(); + } + return true; + } + + private void goPicture() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + startActivityForResult(intent, REQUEST_CODE_GET_PIC_URI); + } + + private void initView() { + mToolBar = (Toolbar) findViewById(com.example.qrcode.R.id.tool_bar); + mToolBar.setTitle("二维码/条形码"); + mToolBar.setTitleTextColor(Color.WHITE); + mToolBar.setBackgroundColor(Color.DKGRAY); + setSupportActionBar(mToolBar); + mToolBar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + mSurfaceView = (SurfaceView) findViewById(com.example.qrcode.R.id.surface); + mScannerView = (ScannerView) findViewById(com.example.qrcode.R.id.scan_view); + } + + + private void initCamera(SurfaceHolder surfaceHolder) { + if (surfaceHolder == null) { + throw new IllegalStateException("No SurfaceHolder provided"); + } + if (cameraManager.isOpen()) { + Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?"); + return; + } + try { + cameraManager.openDriver(surfaceHolder); + if (handler == null) { + handler = new ScannerHandler(this, decodeFormats, "utf-8", cameraManager); + } + } catch (IOException ioe) { + Log.w(TAG, ioe); + } catch (RuntimeException e) { + Log.w(TAG, "Unexpected error initializing camera", e); + } + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + if (holder == null) { + Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!"); + } + if (!hasSurface) { + hasSurface = true; + initCamera(holder); + } + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + hasSurface = false; + } + + //在这里处理扫码结果 + public void handDecode(final Result result) { + mInactivityTimer.onActivity(); + beepManager.playBeepSoundAndVibrate(); +// AlertDialog.Builder mScannerDialogBuilder = new AlertDialog.Builder(this); +// mScannerDialogBuilder.setMessage("codeType:" + result.getBarcodeFormat() + "-----content:" + result.getText()); +// mScannerDialogBuilder.setCancelable(false); +// mScannerDialogBuilder.setPositiveButton("确定", new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// dialog.dismiss(); +// ScannerActivity.this.finish(); +// } +// }); +// mScannerDialogBuilder.create().show(); + Intent data = new Intent(); + BarcodeFormat format = result.getBarcodeFormat(); + String type = format.toString(); + data.putExtra(Constant.EXTRA_RESULT_CODE_TYPE, type); + data.putExtra(Constant.EXTRA_RESULT_CONTENT, result.getText()); + setResult(RESULT_OK, data); + finish(); + } + + public CameraManager getCameraManager() { + return cameraManager; + } + + public Handler getHandler() { + return handler; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + switch (requestCode) { + case REQUEST_CODE_GET_PIC_URI: + picUri = data.getData(); + Bitmap bitmap = null; + try { + bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), picUri); + } catch (IOException e) { + e.printStackTrace(); + } + Message message = mHandler.obtainMessage(MESSAGE_DECODE_FROM_BITMAP, bitmap); + mHandler.sendMessage(message); + Log.e(TAG, "onActivityResult: uri:" + picUri.toString()); + break; + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + permissionDialog.dismiss(); + permissionDialog = null; + if (requestCode == PERMISSION_REQUEST_CODE_READ_EXTERNAL_STORAGE) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + String imagePath = UriUtils.getPicturePathFromUri(ScannerActivity.this, picUri); + Bitmap bitmap = CommonUtils.compressPicture(imagePath); + Message message = mHandler.obtainMessage(MESSAGE_DECODE_FROM_BITMAP, bitmap); + mHandler.sendMessage(message); + Log.e(TAG, "onActivityResult: uri:" + picUri.toString()); + return; + } + } + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SmartConfigStepActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SmartConfigStepActivity.kt index 821e1e055..b6b7e42bf 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SmartConfigStepActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SmartConfigStepActivity.kt @@ -18,6 +18,7 @@ import com.tencent.iot.explorer.link.kitlink.entity.HardwareGuide import com.tencent.iot.explorer.link.kitlink.entity.ProdConfigDetailEntity import com.tencent.iot.explorer.link.kitlink.response.ProductsConfigResponse import com.tencent.iot.explorer.link.kitlink.util.HttpRequest +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import kotlinx.android.synthetic.main.activity_smart_config_step.* import java.util.* @@ -63,7 +64,7 @@ class SmartConfigStepActivity : PActivity() { LoadViewTxtType.LoadLocalViewTxt.ordinal ) if (loadViewTextType != LoadViewTxtType.LoadLocalViewTxt.ordinal) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() } type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SmartConfig.id) if (loadViewTextType == LoadViewTxtType.LoadLocalViewTxt.ordinal) { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftApStepActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftApStepActivity.kt index 56ccb5618..49e84bbda 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftApStepActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftApStepActivity.kt @@ -18,6 +18,7 @@ import com.tencent.iot.explorer.link.kitlink.response.ProductsConfigResponse import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.kitlink.entity.ConfigType +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import kotlinx.android.synthetic.main.activity_soft_ap_step.* import java.util.ArrayList @@ -60,7 +61,7 @@ class SoftApStepActivity : PActivity() { override fun initView() { loadViewTextType = intent.getIntExtra(CommonField.LOAD_VIEW_TXT_TYPE, LoadViewTxtType.LoadLocalViewTxt.ordinal) if (loadViewTextType != LoadViewTxtType.LoadLocalViewTxt.ordinal) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() } type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SoftAp.id) if (loadViewTextType == LoadViewTxtType.LoadLocalViewTxt.ordinal) { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftHotspotActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftHotspotActivity.kt index 4bef3c1a7..09e93fc20 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftHotspotActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/SoftHotspotActivity.kt @@ -18,6 +18,7 @@ import com.tencent.iot.explorer.link.kitlink.entity.ProdConfigDetailEntity import com.tencent.iot.explorer.link.kitlink.fragment.DeviceFragment import com.tencent.iot.explorer.link.kitlink.response.ProductsConfigResponse import com.tencent.iot.explorer.link.kitlink.util.HttpRequest +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import kotlinx.android.synthetic.main.activity_soft_hotspot.* import java.util.* @@ -49,16 +50,16 @@ class SoftHotspotActivity : PActivity() { override fun initView() { loadViewTextType = intent.getIntExtra(CommonField.LOAD_VIEW_TXT_TYPE, LoadViewTxtType.LoadLocalViewTxt.ordinal) if (loadViewTextType != LoadViewTxtType.LoadLocalViewTxt.ordinal) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() } if (intent.hasExtra(CommonField.SSID)) { - extraSsid = intent.getStringExtra(CommonField.SSID) + extraSsid = intent.getStringExtra(CommonField.SSID).safe() } if (intent.hasExtra(CommonField.BSSID)) { - extraBssid = intent.getStringExtra(CommonField.BSSID) + extraBssid = intent.getStringExtra(CommonField.BSSID).safe() } if (intent.hasExtra(CommonField.PWD)) { - extraPwd = intent.getStringExtra(CommonField.PWD) + extraPwd = intent.getStringExtra(CommonField.PWD).safe() } if (loadViewTextType == LoadViewTxtType.LoadLocalViewTxt.ordinal) { loadViewStandradInfo() diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/UserInfoActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/UserInfoActivity.kt index 499064589..8e2386d02 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/UserInfoActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/UserInfoActivity.kt @@ -12,7 +12,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup.MarginLayoutParams import android.widget.TextView +import android.widget.Toast import com.alibaba.fastjson.JSON +import com.alibaba.fastjson.JSONObject import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.core.log.L @@ -25,12 +27,14 @@ import com.tencent.iot.explorer.link.mvp.view.UserInfoView import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.dialog.ListOptionsDialog +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.entity.EditNameValue import com.tencent.iot.explorer.link.kitlink.util.picture.imageselectorbrowser.ImageSelectorActivity import com.tencent.iot.explorer.link.kitlink.util.picture.imageselectorbrowser.ImageSelectorConstant.REQUEST_IMAGE import com.tencent.iot.explorer.link.kitlink.util.picture.imp.ImageManager import com.tencent.iot.explorer.link.kitlink.util.picture.imp.ImageSelectorUtils +import com.tencent.iot.explorer.link.kitlink.util.safe import kotlinx.android.synthetic.main.activity_user_info.* import kotlinx.android.synthetic.main.dialog_temperature.view.* import kotlinx.android.synthetic.main.menu_back_layout.* @@ -52,14 +56,17 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O private val duration = 3 * 1000.toLong() //规定有效时间 private val hits = LongArray(counts) + private var optionDialog: ListOptionsDialog? = null + private var permissionDialog: PermissionDialog? = null + private var clickCamera = false + private var clickAlbum = false + companion object { const val TIMEZONE_REQUESTCODE = 100 } private var permissions = arrayOf( - Manifest.permission.CAMERA, - Manifest.permission.READ_EXTERNAL_STORAGE, - Manifest.permission.WRITE_EXTERNAL_STORAGE + Manifest.permission.READ_EXTERNAL_STORAGE ) override fun getContentView(): Int { @@ -84,6 +91,7 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O override fun setListener() { iv_back.setOnClickListener { finish() } + iv_user_id_copy.setOnClickListener(this) tv_title_nick.setOnClickListener(this) tv_user_info_logout.setOnClickListener(this) tv_user_id.setOnLongClickListener(this) @@ -110,10 +118,13 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O override fun onClick(v: View?) { when (v) { + iv_user_id_copy -> { + // 获取AndroidID,并保存至剪切板 + Utils.copy(this, App.data.userInfo.UserID) + Toast.makeText(App.activity, "内容已复制", Toast.LENGTH_LONG).show() + } tv_title_avatar, iv_avatar, iv_avatar_arrow -> { - if (checkPermissions(permissions)) - showCameraPopup() - else requestPermission(permissions) + showCameraPopup() } tv_title_nick -> { showEditPopup() @@ -141,7 +152,7 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O if (hits[0] >= SystemClock.uptimeMillis() - duration) { if (hits.size == 5) { // 获取AndroidID,并保存至剪切板 - Utils.copy(this, Utils.getAndroidID(T.getContext())) + Utils.copy(this, App.uuid) } } } @@ -168,13 +179,54 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O var options = ArrayList() options.add(getString(R.string.take_photo)) options.add(getString(R.string.select_local_album)) - var optionDialog = ListOptionsDialog(this, options) - optionDialog.show() - optionDialog.setOnDismisListener { + optionDialog = ListOptionsDialog(this, options) + optionDialog!!.show() + optionDialog!!.setOnDismisListener { if (it == 0) { ImageSelectorUtils.show(this, ImageSelectorActivity.Mode.MODE_SINGLE, true, 1) +// if (checkPermissions(arrayOf(Manifest.permission.CAMERA))) +// ImageSelectorUtils.show(this, ImageSelectorActivity.Mode.MODE_SINGLE, true, 1) +// else { +// clickCamera = true +// // 查看请求camera权限的时间是否大于48小时 +// var cameraJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA) +// var cameraJson: JSONObject? = JSONObject.parse(cameraJsonString) as JSONObject? +// val lasttime = cameraJson?.getLong(CommonField.PERMISSION_CAMERA) +// if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { +// T.show(resources.getString(R.string.permission_of_camera_refuse)) +// return@setOnDismisListener +// } +// permissionDialog = PermissionDialog(App.activity, R.mipmap.permission_camera ,getString(R.string.permission_camera_lips), getString(R.string.permission_camera_avatar)) +// permissionDialog!!.show() +// requestPermission(arrayOf(Manifest.permission.CAMERA)) +// +// // 记录请求camera权限的时间 +// var json = JSONObject() +// json.put(CommonField.PERMISSION_CAMERA, System.currentTimeMillis() / 1000) +// Utils.setXmlStringValue(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA, json.toJSONString()) +// } } else if (it == 1) { - ImageSelectorUtils.show(this, ImageSelectorActivity.Mode.MODE_MULTI, false, 1) + if (checkPermissions(permissions)) + ImageSelectorUtils.show(this, ImageSelectorActivity.Mode.MODE_MULTI, false, 1) + else { + clickAlbum = true + // 查看请求album权限的时间是否大于48小时 + var albumJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_ALBUM, CommonField.PERMISSION_ALBUM) + var albumJson: JSONObject? = JSONObject.parse(albumJsonString) as JSONObject? + val lasttime = albumJson?.getLong(CommonField.PERMISSION_ALBUM) + if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { + T.show(resources.getString(R.string.permission_of_album_refuse)) + return@setOnDismisListener + } + permissionDialog = PermissionDialog(App.activity, R.mipmap.permission_album ,getString(R.string.permission_album_lips), getString(R.string.permission_album_avatar)) + permissionDialog!!.show() + requestPermission(permissions) + + // 记录请求album权限的时间 + var json = JSONObject() + json.put(CommonField.PERMISSION_ALBUM, System.currentTimeMillis() / 1000) + Utils.setXmlStringValue(T.getContext(), CommonField.PERMISSION_ALBUM, CommonField.PERMISSION_ALBUM, json.toJSONString()) + } } } } @@ -217,11 +269,25 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O } override fun permissionAllGranted() { - showCameraPopup() + /*if (clickCamera) { + clickCamera = false + ImageSelectorUtils.show(this, ImageSelectorActivity.Mode.MODE_SINGLE, true, 1) + } else */if (clickAlbum) { + clickAlbum = false + ImageSelectorUtils.show(this, ImageSelectorActivity.Mode.MODE_MULTI, false, 1) + } + permissionDialog?.dismiss() + permissionDialog = null } override fun permissionDenied(permission: String) { - requestPermission(arrayOf(permission)) + if (permission.contains(Manifest.permission.CAMERA)) { + T.show(resources.getString(R.string.permission_of_camera_refuse)) + } else if (permissions.contains(permission)) { + T.show(resources.getString(R.string.permission_of_album_refuse)) + } + permissionDialog?.dismiss() + permissionDialog = null } override fun logout() { @@ -317,7 +383,7 @@ class UserInfoActivity : PActivity(), UserInfoView, View.OnClickListener, View.O if (requestCode == CommonField.EDIT_NAME_REQ_CODE && resultCode == Activity.RESULT_OK && data != null) { - var extraInfo = data?.getStringExtra(CommonField.EXTRA_TEXT) + val extraInfo = data.getStringExtra(CommonField.EXTRA_TEXT).safe() presenter.modifyNick(extraInfo) } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WebActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WebActivity.kt index 6eb35d925..12a35227b 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WebActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WebActivity.kt @@ -1,5 +1,7 @@ package com.tencent.iot.explorer.link.kitlink.activity +import android.content.Intent +import android.net.Uri import android.view.View import android.view.ViewGroup import android.webkit.* @@ -53,7 +55,11 @@ class WebActivity : BaseActivity() { val mWebViewClient = object : WebViewClient(){ override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean { if (request?.url.toString().contains(THIRD_SDK_URL_US_ZH)) { - OpensourceLicenseActivity.startWebWithExtra(this@WebActivity, getString(R.string.rule_content_list), THIRD_SDK_URL_US_ZH) + val intent = Intent(this@WebActivity, WebActivity::class.java) + intent.putExtra(CommonField.EXTRA_TITLE, getString(R.string.rule_content_list)) + var url = THIRD_SDK_URL_US_ZH + intent.putExtra(CommonField.EXTRA_TEXT, url) + startActivity(intent) return true } else if (request?.url.toString().contains(THIRD_SDK_URL_US_EN)) { OpensourceLicenseActivity.startWebWithExtra(this@WebActivity, getString(R.string.rule_content_list), THIRD_SDK_URL_US_EN) @@ -68,6 +74,12 @@ class WebActivity : BaseActivity() { wv_web.visibility = View.VISIBLE sv_help.visibility = View.GONE wv_web.loadUrl(url) + wv_web.setDownloadListener(myDownloadListener) + } + private val myDownloadListener = DownloadListener { url, _, _, _, _ -> + val uri: Uri = Uri.parse(url) + val intent = Intent(Intent.ACTION_VIEW, uri) + startActivity(intent) } override fun setListener() { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WifiActivity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WifiActivity.kt index 4c7ee0fd5..c0a1a41b7 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WifiActivity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/WifiActivity.kt @@ -11,16 +11,18 @@ import android.text.Editable import android.text.TextUtils import android.text.TextWatcher import android.view.View +import com.alibaba.fastjson.JSONObject import com.tencent.iot.explorer.link.R import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.core.utils.KeyBoardUtils -import com.tencent.iot.explorer.link.core.utils.LocationUtil +import com.tencent.iot.explorer.link.core.utils.Utils import com.tencent.iot.explorer.link.customview.dialog.WifiHelperDialog import com.tencent.iot.explorer.link.customview.progress.bean.StepBean import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.consts.LoadViewTxtType import com.tencent.iot.explorer.link.kitlink.entity.ConfigType +import com.tencent.iot.explorer.link.kitlink.util.safe import com.tencent.iot.explorer.link.mvp.IPresenter import com.tencent.iot.explorer.link.mvp.presenter.GetBindDeviceTokenPresenter import com.tencent.iot.explorer.link.mvp.view.GetBindDeviceTokenView @@ -43,6 +45,7 @@ class WifiActivity : PActivity(), GetBindDeviceTokenView { Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION ) + private val permissions = arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION) var openWifiDialog: WifiHelperDialog? = null var openLocationServiceDialog: WifiHelperDialog? = null @@ -81,17 +84,17 @@ class WifiActivity : PActivity(), GetBindDeviceTokenView { override fun initView() { loadViewTextType = intent.getIntExtra(CommonField.LOAD_VIEW_TXT_TYPE, LoadViewTxtType.LoadLocalViewTxt.ordinal) if (loadViewTextType != LoadViewTxtType.LoadLocalViewTxt.ordinal) { - productId = intent.getStringExtra(CommonField.PRODUCT_ID) + productId = intent.getStringExtra(CommonField.PRODUCT_ID).safe() } type = intent.getIntExtra(CommonField.CONFIG_TYPE, ConfigType.SmartConfig.id) if (intent.hasExtra(CommonField.SSID)) { - extraSsid = intent.getStringExtra(CommonField.SSID) + extraSsid = intent.getStringExtra(CommonField.SSID).safe() } if (intent.hasExtra(CommonField.BSSID)) { - extraBssid = intent.getStringExtra(CommonField.BSSID) + extraBssid = intent.getStringExtra(CommonField.BSSID).safe() } if (intent.hasExtra(CommonField.PWD)) { - extraPwd = intent.getStringExtra(CommonField.PWD) + extraPwd = intent.getStringExtra(CommonField.PWD).safe() } presenter = GetBindDeviceTokenPresenter(this) refreshTypeView() @@ -125,7 +128,7 @@ class WifiActivity : PActivity(), GetBindDeviceTokenView { openWifiDialog?.show() } else { var ssid2Set = wifiManager.connectionInfo.ssid.replace("\"", "") - if (!LocationUtil.isLocationServiceEnable(this)) { + if (!checkPermissions(permissions)) { tv_select_wifi.hint = getString(R.string.open_location_tip) ssid2Set = "" openLocationServiceDialog?.show() @@ -135,6 +138,16 @@ class WifiActivity : PActivity(), GetBindDeviceTokenView { T.show(getString(R.string.open_location_tip)) } bssid = wifiInfo!!.bssid + + var wifiInfoJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.WIFI_INFO, CommonField.WIFI_INFO) + var locationJson: JSONObject? = JSONObject.parse(wifiInfoJsonString) as JSONObject? + val savedSsid = locationJson?.getString(CommonField.WIFI_SSID) + val savedPwd = locationJson?.getString(CommonField.WIFI_PWD) + if (ssid2Set.equals(savedSsid)) { + if (savedPwd != null) { + et_select_wifi_pwd.setText(savedPwd) + } + } } tv_select_wifi.isEnabled = type == ConfigType.SoftAp.id diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/TRTCAudioCallActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/TRTCAudioCallActivity.java new file mode 100644 index 000000000..b0b6e606d --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/TRTCAudioCallActivity.java @@ -0,0 +1,784 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.Group; +import androidx.core.app.ActivityCompat; + +//import com.blankj.utilcode.util.CollectionUtils; +//import com.blankj.utilcode.util.ToastUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.tencent.iot.explorer.link.T; +import com.tencent.iot.explorer.link.core.utils.Utils; +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog; +import com.tencent.iot.explorer.link.kitlink.consts.CommonField; +import com.tencent.iot.explorer.link.rtc.R; +import com.tencent.iot.explorer.link.rtc.model.IntentParams; +import com.tencent.iot.explorer.link.rtc.model.RoomKey; +import com.tencent.iot.explorer.link.rtc.model.TRTCCalling; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallingDelegate; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallingParamsCallback; +import com.tencent.iot.explorer.link.rtc.model.TRTCUIManager; +import com.tencent.iot.explorer.link.rtc.model.UserInfo; +import com.tencent.iot.explorer.link.rtc.model.impl.TRTCCallingImpl; +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.audiolayout.TRTCAudioLayout; +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.audiolayout.TRTCAudioLayoutManager; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallStatus; +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.utils.NetWorkStateReceiver; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 用于展示语音通话的主界面,通话的接听和拒绝就是在这个界面中完成的。 + */ +public class TRTCAudioCallActivity extends AppCompatActivity implements NetWorkStateReceiver.NetworkStateReceiverListener { + private static final String TAG = TRTCAudioCallActivity.class.getName(); + + public static final String PARAM_TYPE = "type"; + public static final String PARAM_USER = "user_model"; + public static final String PARAM_SELF_INFO = "self_info"; + public static final String PARAM_BEINGCALL_USER = "beingcall_user_model"; + public static final String PARAM_OTHER_INVITING_USER = "other_inviting_user_model"; + public static final int TYPE_BEING_CALLED = 1; + public static final int TYPE_CALL = 2; + private static final int MAX_SHOW_INVITING_USER = 2; + + private ImageView mImageMute; + private ImageView mImageHangup; + private LinearLayout mLayoutMute; + private LinearLayout mLayoutHangup; + private ImageView mImageHandsFree; + private LinearLayout mLayoutHandsFree; + private ImageView mImageDialing; + private LinearLayout mLayoutDialing; + private TRTCAudioLayoutManager mLayoutManagerTRTC; + private Group mGroupInviting; + private LinearLayout mLayoutImgContainer; + private TextView mTextTime; + private TextView mStatusView; + + private Runnable mTimeRunnable; + private int mTimeCount; + private Handler mTimeHandler; + private HandlerThread mTimeHandlerThread; + + private UserInfo mSelfModel; + private List mCallUserInfoList = new ArrayList<>(); // 呼叫方 + private Map mCallUserModelMap = new HashMap<>(); + private UserInfo mSponsorUserInfo; // 被叫方 + private List mOtherInvitingUserInfoList; + private int mCallType; + private TRTCCallingImpl mTRTCCalling; + private boolean isHandsFree = true; + private boolean isMuteMic = false; + private volatile boolean mIsEnterRoom = false; + private Map mUserOfflineMap = new HashMap<>(); + + private TimerTask otherEnterRoomTask = null; + private TimerTask enterRoomTask = null; + private NetWorkStateReceiver netWorkStateReceiver; + + private PermissionDialog permissionDialog = null; + private boolean requestRecordAudioPermission = false; + + /** + * 拨号的回调 + */ + private TRTCCallingDelegate mTRTCAudioCallListener = new TRTCCallingDelegate() { + @Override + public void onError(int code, String msg) { + removeCallbackAndFinish(); + } + + @Override + public void onInvited(String sponsor, List userIdList, boolean isFromGroup, int callType) { + } + + @Override + public void onGroupCallInviteeListUpdate(List userIdList) { + } + + @Override + public void onUserEnter(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + showCallingView(); + removeOtherIsEnterRoom15secondsTask(); + TRTCAudioLayout layout = mLayoutManagerTRTC.findAudioCallLayout(userId); + if (layout != null) { + layout.stopLoading(); + } else { + UserInfo model = new UserInfo(); + model.setUserId(userId); + model.userName = userId; + mCallUserInfoList.add(model); + mCallUserModelMap.put(model.getUserId(), model); + addUserToManager(model); + } + mIsEnterRoom = true; + } + }); + } + + @Override + public void onUserLeave(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + //1. 回收界面元素 + mLayoutManagerTRTC.recyclerAudioCallLayout(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + Boolean offline = mUserOfflineMap.get(userId); + if (offline != null && offline) { + Toast.makeText(TRTCAudioCallActivity.this, R.string.trtccalling_customer_offline, Toast.LENGTH_SHORT).show(); + } else { + mStatusView.setText(R.string.trtccalling_customer_hand_up); + } + removeCallbackAndFinish(); + } + }); + } + + @Override + public void onReject(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mCallUserModelMap.containsKey(userId)) { + // 进入拒绝环节 + //1. 回收界面元素 + mLayoutManagerTRTC.recyclerAudioCallLayout(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + mStatusView.setText(R.string.trtccalling_customer_hand_up); + removeCallbackAndFinish(); + } + } + }); + } + + @Override + public void onNoResp(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mCallUserModelMap.containsKey(userId)) { + // 进入无响应环节 + //1. 回收界面元素 + mLayoutManagerTRTC.recyclerAudioCallLayout(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + mStatusView.setText(R.string.trtccalling_customer_no_resp); + removeCallbackAndFinish(); + } + } + }); + } + + @Override + public void onLineBusy(String userId) { + if (mCallUserModelMap.containsKey(userId)) { + // 进入无响应环节 + //1. 回收界面元素 + mLayoutManagerTRTC.recyclerAudioCallLayout(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + removeCallbackAndFinish(); + } + } + + @Override + public void onCallingCancel() { + removeCallbackAndFinish(); + } + + @Override + public void onCallingTimeout() { + removeCallbackAndFinish(); + } + + @Override + public void onCallEnd() { + removeCallbackAndFinish(); + } + + @Override + public void onUserVideoAvailable(String userId, boolean isVideoAvailable) { } + + @Override + public void onUserAudioAvailable(String userId, boolean isVideoAvailable) { } + + @Override + public void onUserVoiceVolume(Map volumeMap) { + for (Map.Entry entry : volumeMap.entrySet()) { + String userId = entry.getKey(); + TRTCAudioLayout layout = mLayoutManagerTRTC.findAudioCallLayout(userId); + } + } + }; + + /** + * 主动拨打给某个用户 + * + * @param context + * @param roomKey + */ + public static void startCallSomeone(Context context, RoomKey roomKey, String beingCallUserId) { + Intent starter = new Intent(context, TRTCAudioCallActivity.class); + starter.putExtra(PARAM_TYPE, TYPE_CALL); + starter.putExtra(PARAM_SELF_INFO, JSON.toJSONString(roomKey)); + UserInfo beingCallUserInfo = new UserInfo(); + beingCallUserInfo.setUserId(beingCallUserId); + starter.putExtra(PARAM_BEINGCALL_USER, beingCallUserInfo); + context.startActivity(starter); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_CALLING.getValue(); + } + + /** + * 作为用户被叫 + */ + public static void startBeingCall(Context context, RoomKey roomKey, String beingCallUserId) { + Intent starter = new Intent(context, TRTCAudioCallActivity.class); + starter.putExtra(PARAM_TYPE, TYPE_BEING_CALLED); + UserInfo beingCallUserInfo = new UserInfo(); + beingCallUserInfo.setUserId(beingCallUserId); + starter.putExtra(PARAM_BEINGCALL_USER, beingCallUserInfo); + starter.putExtra(PARAM_SELF_INFO, JSON.toJSONString(roomKey)); + starter.putExtra(PARAM_OTHER_INVITING_USER, new IntentParams(new ArrayList())); + starter.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(starter); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_CALLING.getValue(); + } + + private void checkoutOtherIsEnterRoom15seconds() { + if (otherEnterRoomTask == null) { + otherEnterRoomTask = new TimerTask(){ + public void run(){ + //自己已进入房间15秒内对方没有进入房间 则显示对方已挂断,并主动退出,进入了就取消timertask + runOnUiThread(new Runnable() { + @Override + public void run() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_hand_up); + } + }); + removeCallbackAndFinish(); + } + }); + } + }; + Timer timer = new Timer(); + timer.schedule(otherEnterRoomTask, 15000); + } + } + + private void removeOtherIsEnterRoom15secondsTask() { + if (otherEnterRoomTask != null) { + otherEnterRoomTask.cancel(); + otherEnterRoomTask = null; + } + } + + private void checkoutIsEnterRoom60seconds(final boolean calling, final String message) { + if (enterRoomTask == null) { + enterRoomTask = new TimerTask(){ + @Override + public void run(){ + //呼叫了60秒,对方未接听 显示对方无人接听,并退出,进入了就取消timertask + runOnUiThread(new Runnable() { + @Override + public void run() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (calling) { + mStatusView.setText(message); + } + } + }); + removeCallbackAndFinish(); + } + }); + } + }; + Timer timer = new Timer(); + timer.schedule(enterRoomTask, 60000); + } + } + + private void removeIsEnterRoom60secondsTask() { + if (enterRoomTask != null) { + enterRoomTask.cancel(); + enterRoomTask = null; + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == 103) { + permissionDialog.dismiss(); + permissionDialog = null; + if (!requestRecordAudioPermission) { + checkAndRequestPermission(); + } else { + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + } + } + } + + private void checkAndRequestPermission() { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_DENIED && !requestRecordAudioPermission) { + // 查看请求mic权限的时间是否大于48小时 + String micJsonString = Utils.INSTANCE.getStringValueFromXml(this, CommonField.PERMISSION_MIC, CommonField.PERMISSION_MIC); + long lasttime = 0L; + if (micJsonString != null) { + JSONObject micJson = JSON.parseObject(micJsonString); + lasttime = micJson.getLong(CommonField.PERMISSION_MIC); + } + if (micJsonString != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48 * 60 * 60) { + T.show(getString(com.tencent.iot.explorer.link.R.string.permission_of_camera_mic_refuse)); + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + removeCallbackAndFinish(); + return; + } + if (permissionDialog == null) { + permissionDialog = new PermissionDialog(this, com.tencent.iot.explorer.link.R.mipmap.permission_mic ,getString(com.tencent.iot.explorer.link.R.string.permission_mic_lips), getString(com.tencent.iot.explorer.link.R.string.permission_camera_trtc)); + permissionDialog.show(); + requestRecordAudioPermission = true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 103); + + // 记录请求mic权限的时间 + JSONObject json = new JSONObject(); + json.put(CommonField.PERMISSION_MIC, System.currentTimeMillis() / 1000); + Utils.INSTANCE.setXmlStringValue(this, CommonField.PERMISSION_MIC, CommonField.PERMISSION_MIC, json.toJSONString()); + return; + } + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 应用运行时,保持不锁屏、全屏化 + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.trtccalling_audiocall_activity_call_main); + startNetworkBroadcastReceiver(this); + + TRTCUIManager.getInstance().addCallingParamsCallback(new TRTCCallingParamsCallback() { + @Override + public void joinRoom(Integer callingType, String deviceId, RoomKey roomKey) { + //1.分配自己的画面 + mLayoutManagerTRTC.setMySelfUserId(mSelfModel.getUserId()); + addUserToManager(mSelfModel); + mTRTCCalling.enterTRTCRoom(roomKey); + if (roomKey != null) { + removeIsEnterRoom60secondsTask(); + } + showCallingView(); + checkoutOtherIsEnterRoom15seconds(); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_ON_THE_PHONE.getValue(); + } + + @Override + public void exitRoom() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_hand_up); + } + }); + + removeCallbackAndFinish(); + } + + @Override + public void userBusy() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_busy); + } + }); + } + + @Override + public void otherUserAccept() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_other_customer_accpet); + } + }); + + removeCallbackAndFinish(); + } + + @Override + public void userOffline(String deviceId) { + mUserOfflineMap.put(deviceId, true); + } + }); + + initView(); + checkAndRequestPermission(); +// boolean calling = initData(); +// initListener(); +// checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + } + + @Override + public void onBackPressed() { + // 退出这个界面的时候,需要挂断 + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + removeCallbackAndFinish(); + super.onBackPressed(); + } + + private void removeCallbackAndFinish() { + mTRTCCalling.exitRoom(); + finish(); + TRTCUIManager.getInstance().isCalling = false; + TRTCUIManager.getInstance().deviceId = ""; + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_IDLE_OR_REFUSE.getValue(); + TRTCUIManager.getInstance().removeCallingParamsCallback(); + removeIsEnterRoom60secondsTask(); + removeOtherIsEnterRoom15secondsTask(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + stopTimeCount(); + mTimeHandlerThread.quit(); + } + + //在onResume()方法注册 + @Override + protected void onResume() { + registerNetworkBroadcastReceiver(this); + Log.e(TAG, "注册netWorkStateReceiver"); + super.onResume(); + } + + //onPause()方法注销 + @Override + protected void onPause() { + unregisterNetworkBroadcastReceiver(this); + Log.e(TAG, "注销netWorkStateReceiver"); + super.onPause(); + } + + public void startNetworkBroadcastReceiver(Context currentContext) { + netWorkStateReceiver = new NetWorkStateReceiver(); + IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(netWorkStateReceiver, filter); + netWorkStateReceiver.addListener((NetWorkStateReceiver.NetworkStateReceiverListener) currentContext); + registerNetworkBroadcastReceiver(currentContext); + } + + /** + * Register the NetworkStateReceiver with your activity + * + * @param currentContext + */ + public void registerNetworkBroadcastReceiver(Context currentContext) { + currentContext.registerReceiver(netWorkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION)); + } + + /** + * Unregister the NetworkStateReceiver with your activity + * + * @param currentContext + */ + public void unregisterNetworkBroadcastReceiver(Context currentContext) { + currentContext.unregisterReceiver(netWorkStateReceiver); + } + + @Override + public void networkAvailable() { + Log.e(TAG, "networkAvailable"); + if (mIsEnterRoom) { + removeIsEnterRoom60secondsTask(); + } + } + + @Override + public void networkUnavailable() { + Log.e(TAG, "networkUnavailable"); + Toast.makeText(this, R.string.trtccalling_customer_no_net, Toast.LENGTH_SHORT).show(); + checkoutIsEnterRoom60seconds(true, getString(R.string.trtccalling_customer_no_net)); + } + + private void initListener() { + mLayoutMute.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isMuteMic = !isMuteMic; + mTRTCCalling.setMicMute(isMuteMic); + mImageMute.setActivated(isMuteMic); + } + }); + mLayoutHandsFree.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isHandsFree = !isHandsFree; + mTRTCCalling.setHandsFree(isHandsFree); + mImageHandsFree.setActivated(isHandsFree); + } + }); + mImageMute.setActivated(isMuteMic); + mImageHandsFree.setActivated(isHandsFree); + } + + private boolean initData() { + // 初始化成员变量 + mTRTCCalling = new TRTCCallingImpl(this); + mTRTCCalling.setTRTCCallingDelegate(mTRTCAudioCallListener); + mTimeHandlerThread = new HandlerThread("time-count-thread"); + mTimeHandlerThread.start(); + mTimeHandler = new Handler(mTimeHandlerThread.getLooper()); + // 初始化从外界获取的数据 + Intent intent = getIntent(); + //自己的资料 + String roomKeyStr = intent.getStringExtra(PARAM_SELF_INFO); + if (TextUtils.isEmpty(roomKeyStr)) { + finish(); + } + RoomKey roomKey = JSON.parseObject(roomKeyStr, RoomKey.class); + mSelfModel = new UserInfo(); + if (roomKey != null) { + mSelfModel.setUserId(roomKey.getUserId()); + } + + mCallType = intent.getIntExtra(PARAM_TYPE, TYPE_BEING_CALLED); + mSponsorUserInfo = (UserInfo) intent.getSerializableExtra(PARAM_BEINGCALL_USER); + if (mCallType == TYPE_BEING_CALLED) { + // 作为被叫 + IntentParams params = (IntentParams) intent.getSerializableExtra(PARAM_OTHER_INVITING_USER); + if (params != null) { + mOtherInvitingUserInfoList = params.mUserInfos; + } + showWaitingResponseView(); + mStatusView.setText(R.string.trtccalling_customer_calling_audio); + return false; + } else { + // 主叫方 + if (roomKey != null) { + showInvitingView(); + } + return true; + } + } + + private void initView() { + mImageMute = (ImageView) findViewById(R.id.img_mute); + mLayoutMute = (LinearLayout) findViewById(R.id.ll_mute); + mImageHangup = (ImageView) findViewById(R.id.img_hangup); + mLayoutHangup = (LinearLayout) findViewById(R.id.ll_hangup); + mImageHandsFree = (ImageView) findViewById(R.id.img_handsfree); + mLayoutHandsFree = (LinearLayout) findViewById(R.id.ll_handsfree); + mImageDialing = (ImageView) findViewById(R.id.img_dialing); + mLayoutDialing = (LinearLayout) findViewById(R.id.ll_dialing); + mLayoutManagerTRTC = (TRTCAudioLayoutManager) findViewById(R.id.trtc_layout_manager); + mGroupInviting = (Group) findViewById(R.id.group_inviting); + mLayoutImgContainer = (LinearLayout) findViewById(R.id.ll_img_container); + mTextTime = (TextView) findViewById(R.id.tv_time); + mStatusView = (TextView) findViewById(R.id.tv_status); + } + + /** + * 等待接听界面 + */ + public void showWaitingResponseView() { + //1. 展示对方的画面 + TRTCAudioLayout layout = mLayoutManagerTRTC.allocAudioCallLayout(mSponsorUserInfo.getUserId()); + layout.setUserId(mSponsorUserInfo.getUserId()); + //2. 展示电话对应界面 + mLayoutHangup.setVisibility(View.VISIBLE); + mLayoutDialing.setVisibility(View.VISIBLE); + mLayoutHandsFree.setVisibility(View.GONE); + mLayoutMute.setVisibility(View.GONE); + //3. 设置对应的listener + mLayoutHangup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + removeCallbackAndFinish(); + } + }); + mLayoutDialing.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + TRTCUIManager.getInstance().didAcceptJoinRoom(TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + } + }); + //4. 展示其他用户界面 + showOtherInvitingUserView(); + } + + /** + * 展示邀请列表 + */ + public void showInvitingView() { + //1. 展示自己的界面 + mLayoutManagerTRTC.setMySelfUserId(mSelfModel.getUserId()); + addUserToManager(mSelfModel); + //2. 展示对方的画面 + for (UserInfo userInfo : mCallUserInfoList) { + TRTCAudioLayout layout = addUserToManager(userInfo); + layout.startLoading(); + } + //3. 设置底部栏 + mLayoutHangup.setVisibility(View.VISIBLE); + mLayoutHangup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + removeCallbackAndFinish(); + } + }); + mLayoutDialing.setVisibility(View.GONE); + mLayoutHandsFree.setVisibility(View.VISIBLE); + mLayoutMute.setVisibility(View.VISIBLE); + //4. 隐藏中间他们也在界面 + hideOtherInvitingUserView(); + } + + /** + * 展示通话中的界面 + */ + public void showCallingView() { + mLayoutHangup.setVisibility(View.VISIBLE); + mLayoutDialing.setVisibility(View.GONE); + mLayoutHandsFree.setVisibility(View.VISIBLE); + mLayoutMute.setVisibility(View.VISIBLE); + + mLayoutHangup.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mTRTCCalling.exitRoom(); + removeCallbackAndFinish(); + } + }); + showTimeCount(); + hideOtherInvitingUserView(); + mStatusView.setText(R.string.trtccalling_dialed_is_busy_audio); + } + + private void showTimeCount() { + if (mTimeRunnable != null) { + return; + } + mTimeCount = 0; + mTextTime.setText(getShowTime(mTimeCount)); + if (mTimeRunnable == null) { + mTimeRunnable = new Runnable() { + @Override + public void run() { + mTimeCount++; + if (mTextTime != null) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mTextTime.setText(getShowTime(mTimeCount)); + } + }); + } + mTimeHandler.postDelayed(mTimeRunnable, 1000); + } + }; + } + mTimeHandler.postDelayed(mTimeRunnable, 1000); + } + + private void stopTimeCount() { + mTimeHandler.removeCallbacks(mTimeRunnable); + mTimeRunnable = null; + } + + private String getShowTime(int count) { + return getString(R.string.trtccalling_called_time_format, count / 60, count % 60); + } + + private void showOtherInvitingUserView() { + mGroupInviting.setVisibility(View.VISIBLE); + int squareWidth = getResources().getDimensionPixelOffset(R.dimen.trtccalling_small_image_size); + int leftMargin = getResources().getDimensionPixelOffset(R.dimen.trtccalling_small_image_left_margin); + for (int index = 0; index < mOtherInvitingUserInfoList.size() && index < MAX_SHOW_INVITING_USER; index++) { + UserInfo userInfo = mOtherInvitingUserInfoList.get(index); + ImageView imageView = new ImageView(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(squareWidth, squareWidth); + if (index != 0) { + layoutParams.leftMargin = leftMargin; + } + imageView.setLayoutParams(layoutParams); + mLayoutImgContainer.addView(imageView); + } + } + + private void hideOtherInvitingUserView() { + mGroupInviting.setVisibility(View.GONE); + } + + private TRTCAudioLayout addUserToManager(UserInfo userInfo) { + TRTCAudioLayout layout = mLayoutManagerTRTC.allocAudioCallLayout(userInfo.getUserId()); + if (layout == null) { + return null; + } + layout.setUserId(userInfo.getUserId()); + return layout; + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/TRTCAudioLayout.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/TRTCAudioLayout.java new file mode 100644 index 000000000..fe1340486 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/TRTCAudioLayout.java @@ -0,0 +1,71 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.audiolayout; + +import android.content.Context; +import android.graphics.Bitmap; +import android.util.AttributeSet; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.tencent.iot.explorer.link.rtc.R; + + +/** + * 通话界面中,显示单个用户头像的自定义布局 + */ +public class TRTCAudioLayout extends RelativeLayout { + private static final int MIN_AUDIO_VOLUME = 10; + + private ImageView mImageHead; + private TextView mTextName; + private ImageView mImageAudioInput; + private FrameLayout mLayoutShade; + private String mUserId; + + public TRTCAudioLayout(Context context) { + this(context, null); + } + + public TRTCAudioLayout(Context context, AttributeSet attrs) { + super(context, attrs); + inflate(context, R.layout.trtccalling_audiocall_item_user_layout, this); + initView(); + } + + private void initView() { + mImageHead = (ImageView) findViewById(R.id.img_head); + mTextName = (TextView) findViewById(R.id.tv_name); + mImageAudioInput = (ImageView) findViewById(R.id.iv_audio_input); + mLayoutShade = (FrameLayout) findViewById(R.id.fl_shade); + } + + public void setAudioVolume(int vol) { + if (vol > MIN_AUDIO_VOLUME) { + mImageAudioInput.setVisibility(VISIBLE); + } else { + mImageAudioInput.setVisibility(GONE); + } + } + + public void setUserId(String userId) { + mUserId = userId; + mTextName.setText(mUserId); + } + + public void setBitmap(Bitmap bitmap) { + mImageHead.setImageBitmap(bitmap); + } + + public ImageView getImageView() { + return mImageHead; + } + + public void startLoading() { + mLayoutShade.setVisibility(VISIBLE); + } + + public void stopLoading() { + mLayoutShade.setVisibility(GONE); + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/TRTCAudioLayoutManager.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/TRTCAudioLayoutManager.java new file mode 100644 index 000000000..471ada9c4 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/TRTCAudioLayoutManager.java @@ -0,0 +1,240 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.audiolayout; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.RelativeLayout; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * 通话过程中,用来显示/管理所有用户头像的{@link TRTCAudioLayout}自定义布局 + */ +public class TRTCAudioLayoutManager extends RelativeLayout { + private static final String TAG = "TRTCAudioLayoutManager"; + + public static final int MAX_USER = 9; + + private String mSelfUserId; + private Context mContext; + private int mCount = 0; + private boolean mInitParam = false; + + private ArrayList mGrid1ParamList; + private ArrayList mGrid2ParamList; + private ArrayList mGrid3ParamList; + private ArrayList mGrid4ParamList; + private ArrayList mGrid9ParamList; + private ArrayList mLayoutEntityList; + + + public TRTCAudioLayoutManager(Context context) { + super(context); + initView(context); + } + + + public TRTCAudioLayoutManager(Context context, AttributeSet attrs) { + super(context, attrs); + initView(context); + } + + + public TRTCAudioLayoutManager(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + initView(context); + } + + private void initView(Context context) { + Log.i(TAG, "initView: "); + mContext = context; + // 做成正方形 + mLayoutEntityList = new ArrayList(); + this.post(new Runnable() { + @Override + public void run() { + makeGirdLayout(true); + } + }); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int widthSize = MeasureSpec.getSize(widthMeasureSpec); + final int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + if (widthSize == 0 && heightSize == 0) { + // If there are no constraints on size, let FrameLayout measure + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + // Now use the smallest of the measured dimensions for both dimensions + final int minSize = Math.min(getMeasuredWidth(), getMeasuredHeight()); + setMeasuredDimension(minSize, minSize); + return; + } + + final int size; + if (widthSize == 0 || heightSize == 0) { + // If one of the dimensions has no restriction on size, set both dimensions to be the + // on that does + size = Math.max(widthSize, heightSize); + } else { + // Both dimensions have restrictions on size, set both dimensions to be the + // smallest of the two + size = Math.min(widthSize, heightSize); + } + + final int newMeasureSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); + super.onMeasure(newMeasureSpec, newMeasureSpec); + } + + public void setMySelfUserId(String userId) { + mSelfUserId = userId; + } + + /** + * 根据 userId 找到已经分配的 View + */ + public TRTCAudioLayout findAudioCallLayout(String userId) { + if (userId == null) return null; + for (TRTCLayoutEntity layoutEntity : mLayoutEntityList) { + if (layoutEntity.userId.equals(userId)) { + return layoutEntity.layout; + } + } + return null; + } + + /** + * 根据 userId 分配对应的 view + * + * @param userId + * @return + */ + public TRTCAudioLayout allocAudioCallLayout(String userId) { + if (userId == null) return null; + if (mCount > MAX_USER) { + return null; + } + TRTCLayoutEntity layoutEntity = new TRTCLayoutEntity(); + layoutEntity.userId = userId; + layoutEntity.layout = new TRTCAudioLayout(mContext); + layoutEntity.layout.setVisibility(VISIBLE); + mLayoutEntityList.add(layoutEntity); + addView(layoutEntity.layout); + mCount++; + post(new Runnable() { + @Override + public void run() { + makeGirdLayout(true); + } + }); + return layoutEntity.layout; + } + + /** + * 根据 userId 回收对应的 view + * + * @param userId + */ + public void recyclerAudioCallLayout(String userId) { + if (userId == null) return; + Iterator iterator = mLayoutEntityList.iterator(); + while (iterator.hasNext()) { + TRTCLayoutEntity item = (TRTCLayoutEntity) iterator.next(); + if (item.userId.equals(userId)) { + removeView(item.layout); + iterator.remove(); + mCount--; + break; + } + } + post(new Runnable() { + @Override + public void run() { + makeGirdLayout(true); + } + }); + } + + /** + * 设置当前音量 + * + * @param userId + * @param audioVolume + */ + public void updateAudioVolume(String userId, int audioVolume) { + if (userId == null) return; + for (TRTCLayoutEntity entity : mLayoutEntityList) { + if (entity.layout.getVisibility() == VISIBLE) { + if (userId.equals(entity.userId)) { +// entity.layout.setAudioVolume(audioVolume); + } + } + } + } + + private TRTCLayoutEntity findEntity(TRTCAudioLayout layout) { + for (TRTCLayoutEntity entity : mLayoutEntityList) { + if (entity.layout == layout) return entity; + } + return null; + } + + private TRTCLayoutEntity findEntity(String userId) { + for (TRTCLayoutEntity entity : mLayoutEntityList) { + if (entity.userId.equals(userId)) return entity; + } + return null; + } + + /** + * 切换到九宫格布局 + * + * @param needUpdate 是否需要更新布局 + */ + private void makeGirdLayout(boolean needUpdate) { + if (!mInitParam) { + mGrid1ParamList = Utils.initGrid1Param(getContext(), getWidth(), getHeight()); + mGrid2ParamList = Utils.initGrid2Param(getContext(), getWidth(), getHeight()); + mGrid3ParamList = Utils.initGrid3Param(getContext(), getWidth(), getHeight()); + mGrid4ParamList = Utils.initGrid4Param(getContext(), getWidth(), getHeight()); + mGrid9ParamList = Utils.initGrid9Param(getContext(), getWidth(), getHeight()); + mInitParam = true; + } + if (needUpdate) { + ArrayList paramList; + if (mCount <= 1) { + paramList = mGrid1ParamList; + if (mLayoutEntityList.size() <= 0) return; + TRTCLayoutEntity entity = mLayoutEntityList.get(0); + entity.layout.setLayoutParams(paramList.get(0)); + return; + } else if (mCount == 2) { + paramList = mGrid2ParamList; + } else if (mCount == 3) { + paramList = mGrid3ParamList; + } else if (mCount == 4) { + paramList = mGrid4ParamList; + } else { + paramList = mGrid9ParamList; + } + int layoutIndex = 1; + for (int i = 0; i < mLayoutEntityList.size(); i++) { + TRTCLayoutEntity entity = mLayoutEntityList.get(i); + // 我自己要放在布局的左上角 + if (entity.userId.equals(mSelfUserId)) { + entity.layout.setLayoutParams(paramList.get(0)); + } else if (layoutIndex < paramList.size()) { + entity.layout.setLayoutParams(paramList.get(layoutIndex++)); + } + } + } + } + + private static class TRTCLayoutEntity { + public TRTCAudioLayout layout; + public String userId = ""; + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/Utils.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/Utils.java new file mode 100644 index 000000000..57d0870be --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/audiocall/audiolayout/Utils.java @@ -0,0 +1,228 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.audiocall.audiolayout; + +import android.content.Context; +import android.widget.RelativeLayout; + +import java.util.ArrayList; + +public class Utils { + + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + /** + * 一宫格布局,整体居中 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid1Param(Context context, int layoutWidth, int layoutHeight) { + ArrayList list = new ArrayList<>(); + + int margin = dip2px(context, 10); + int grid4W = (layoutWidth - margin * 2) / 2; + int grid4H = (layoutHeight - margin * 2) / 2; + // 使用四宫格的大小 + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams0.addRule(RelativeLayout.CENTER_IN_PARENT); + list.add(layoutParams0); + return list; + } + + /** + * 二宫格布局,两个layout平分 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid2Param(Context context, int layoutWidth, int layoutHeight) { + ArrayList list = new ArrayList<>(); + int margin = dip2px(context, 10); + int grid4W = (layoutWidth - margin * 2) / 2; + int grid4H = (layoutHeight - margin * 2 ) / 2; + // 使用四宫格的大小 + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams0.addRule(RelativeLayout.CENTER_VERTICAL); + layoutParams0.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams1.addRule(RelativeLayout.CENTER_VERTICAL); + layoutParams1.rightMargin = margin; + + list.add(layoutParams0); + list.add(layoutParams1); + return list; + } + + /** + * 三宫格布局,品字形 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid3Param(Context context, int layoutWidth, int layoutHeight) { + int margin = dip2px(context, 10); + + ArrayList list = new ArrayList<>(); + int grid4W = (layoutWidth - margin * 2) / 2; + int grid4H = (layoutHeight - margin * 2) / 2; + + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams0.topMargin = margin; + layoutParams0.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams1.topMargin = margin; + layoutParams1.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams2.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams2.leftMargin = margin; + layoutParams2.bottomMargin = margin; + + list.add(layoutParams0); + list.add(layoutParams1); + list.add(layoutParams2); + return list; + } + + /** + * 四宫格布局参数 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid4Param(Context context, int layoutWidth, int layoutHeight) { + int margin = dip2px(context, 10); + + ArrayList list = new ArrayList<>(); + int grid4W = (layoutWidth - margin * 2) / 2; + int grid4H = (layoutHeight - margin * 2) / 2; + + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams0.topMargin = margin; + layoutParams0.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams1.topMargin = margin; + layoutParams1.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams2.bottomMargin = margin; + layoutParams2.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams3.bottomMargin = margin; + layoutParams3.rightMargin = margin; + + list.add(layoutParams0); + list.add(layoutParams1); + list.add(layoutParams2); + list.add(layoutParams3); + return list; + } + + /** + * 九宫格布局参数 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid9Param(Context context, int layoutWidth, int layoutHeight) { + int margin = dip2px(context, 10); + + ArrayList list = new ArrayList<>(); + + int grid9W = (layoutWidth - margin * 2) / 3; + int grid9H = (layoutHeight - margin * 2) / 3; + + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams0.topMargin = margin; + layoutParams0.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams1.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams1.topMargin = margin; + + RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams2.topMargin = margin; + layoutParams2.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams3.leftMargin = margin; + layoutParams3.topMargin = margin + grid9H; + + RelativeLayout.LayoutParams layoutParams4 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams4.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams4.topMargin = margin + grid9H; + + RelativeLayout.LayoutParams layoutParams5 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams5.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams5.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams5.topMargin = margin + grid9H; + layoutParams5.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams6 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams6.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams6.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams6.bottomMargin = margin; + layoutParams6.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams7 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams7.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams7.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams7.bottomMargin = margin; + + RelativeLayout.LayoutParams layoutParams8 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams8.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams8.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams8.bottomMargin = margin; + layoutParams8.rightMargin = margin; + + list.add(layoutParams0); + list.add(layoutParams1); + list.add(layoutParams2); + list.add(layoutParams3); + list.add(layoutParams4); + list.add(layoutParams5); + list.add(layoutParams6); + list.add(layoutParams7); + list.add(layoutParams8); + return list; + } + +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/utils/NetWorkStateReceiver.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/utils/NetWorkStateReceiver.java new file mode 100644 index 000000000..fae5facb2 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/utils/NetWorkStateReceiver.java @@ -0,0 +1,148 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.utils; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkInfo; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import static android.net.ConnectivityManager.TYPE_MOBILE; +import static android.net.ConnectivityManager.TYPE_WIFI; + +public class NetWorkStateReceiver extends BroadcastReceiver { + + protected List listeners; + protected Boolean connected; + private static final String TAG = "NetworkStateReceiver"; + + public NetWorkStateReceiver() { + listeners = new ArrayList<>(); + connected = null; + } + + @Override + public void onReceive(Context context, Intent intent) { + + isConnected(context); + notifyStateToAll(); + } //After the onReceive() of the receiver class has finished, the Android system is allowed to recycle the receiver + + public boolean isConnected(Context context) { + Log.i(TAG, "网络状态发生变化"); + //检测API是不是小于23,因为到了API23之后getNetworkInfo(int networkType)方法被弃用 + if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) { + + //获得ConnectivityManager对象 + ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + + //获取ConnectivityManager对象对应的NetworkInfo对象 + //获取WIFI连接的信息 + NetworkInfo wifiNetworkInfo = connMgr.getNetworkInfo(TYPE_WIFI); + //获取移动数据连接的信息 + NetworkInfo dataNetworkInfo = connMgr.getNetworkInfo(TYPE_MOBILE); + if (wifiNetworkInfo.isConnected() && dataNetworkInfo.isConnected()) { + connected = true; + Log.i(TAG, "WIFI已连接,移动数据已连接"); + } else if (wifiNetworkInfo.isConnected() && !dataNetworkInfo.isConnected()) { + connected = true; + Log.i(TAG, "WIFI已连接,移动数据已断开"); + } else if (!wifiNetworkInfo.isConnected() && dataNetworkInfo.isConnected()) { + connected = true; + Log.i(TAG, "WIFI已断开,移动数据已连接"); + } else { + connected = false; + Log.i(TAG, "WIFI已断开,移动数据已断开"); + } + //API大于23时使用下面的方式进行网络监听 + } else { + + Log.i(TAG, "API level 大于23"); + //获得ConnectivityManager对象 + ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + + //获取所有网络连接的信息 + Network[] networks = connMgr.getAllNetworks(); + //用于存放网络连接信息 + StringBuilder sb = new StringBuilder(); + //通过循环将网络信息逐个取出来 + for (int i=0; i < networks.length; i++){ + //获取ConnectivityManager对象对应的NetworkInfo对象 + NetworkInfo networkInfo = connMgr.getNetworkInfo(networks[i]); + sb.append(networkInfo.getTypeName() + " connect is " + networkInfo.isConnected()); + } + if (networks.length == 0) {// 断网获取不到网络状态了 + connected = false; + sb.append("no network connect~!"); + } else { + connected = true; + } + Log.i(TAG, sb.toString()); + } + return connected; + } + + /** + * Notify the state to all needed methods + */ + private void notifyStateToAll() { + Log.i(TAG, "Notifying state to " + listeners.size() + " listener(s)"); + for(NetworkStateReceiverListener eachNetworkStateReceiverListener : listeners) + notifyState(eachNetworkStateReceiverListener); + } + + /** + * Notify the network state, triggering interface functions based on the current state + * @param networkStateReceiverListener Object which implements the NetworkStateReceiverListener interface + */ + private void notifyState(NetworkStateReceiverListener networkStateReceiverListener) { + if(connected == null || networkStateReceiverListener == null) + return; + + if(connected == true) { + // Triggering function on the interface towards network availability + networkStateReceiverListener.networkAvailable(); + } else { + // Triggering function on the interface towards network being unavailable + networkStateReceiverListener.networkUnavailable(); + } + } + + /** + * Adds a listener to the list so that it will receive connection state change updates + * @param networkStateReceiverListener Object which implements the NetworkStateReceiverListener interface + */ + public void addListener(NetworkStateReceiverListener networkStateReceiverListener) { + Log.i(TAG, "addListener() - listeners.add(networkStateReceiverListener) + notifyState(networkStateReceiverListener);"); + listeners.add(networkStateReceiverListener); + notifyState(networkStateReceiverListener); + } + + /** + * Removes listener (when no longer necessary) from the list so that it will no longer receive connection state change updates + * @param networkStateReceiverListener Object which implements the NetworkStateReceiverListener interface + */ + public void removeListener(NetworkStateReceiverListener networkStateReceiverListener) { + listeners.remove(networkStateReceiverListener); + } + + /** + * Inner Interface (i.e. to encapsulate behavior in a generic and re-usable way) which handles connection state changes for classes which registered this receiver (Outer class NetworkStateReceiver) + * This interface implements the 'Strategy Pattern', where an execution strategy is evaluated and applied internally at runtime + */ + public interface NetworkStateReceiverListener { + /** + * When the connection state is changed and there is a connection, this method is called + */ + void networkAvailable(); + + /** + * Connection state is changed and there is not a connection, this method is called + */ + void networkUnavailable(); + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/TRTCVideoCallActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/TRTCVideoCallActivity.java new file mode 100644 index 000000000..709d6bd7d --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/TRTCVideoCallActivity.java @@ -0,0 +1,900 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall; + +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.HandlerThread; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.constraintlayout.widget.Group; +import androidx.core.app.ActivityCompat; + +//import com.blankj.utilcode.util.ToastUtils; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.squareup.picasso.Picasso; +import com.tencent.iot.explorer.link.T; +import com.tencent.iot.explorer.link.core.utils.Utils; +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog; +import com.tencent.iot.explorer.link.kitlink.consts.CommonField; +import com.tencent.iot.explorer.link.rtc.R; +import com.tencent.iot.explorer.link.rtc.model.IntentParams; +import com.tencent.iot.explorer.link.rtc.model.RoomKey; +import com.tencent.iot.explorer.link.rtc.model.TRTCCalling; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallingDelegate; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallingParamsCallback; +import com.tencent.iot.explorer.link.rtc.model.TRTCUIManager; +import com.tencent.iot.explorer.link.rtc.model.UserInfo; +import com.tencent.iot.explorer.link.rtc.model.impl.TRTCCallingImpl; +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.utils.NetWorkStateReceiver; +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.videolayout.TRTCVideoLayout; +import com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.videolayout.TRTCVideoLayoutManager; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallStatus; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +/** + * 用于展示视频通话的主界面,通话的接听和拒绝就是在这个界面中完成的。 + * + * @author guanyifeng + */ +public class TRTCVideoCallActivity extends AppCompatActivity implements NetWorkStateReceiver.NetworkStateReceiverListener { + + private static final String TAG = TRTCVideoCallActivity.class.getSimpleName(); + + public static final int TYPE_BEING_CALLED = 1; + public static final int TYPE_CALL = 2; + + public static final String PARAM_TYPE = "type"; + public static final String PARAM_SELF_INFO = "self_info"; + public static final String PARAM_USER = "user_model"; + public static final String PARAM_BEINGCALL_USER = "beingcall_user_model"; + public static final String PARAM_OTHER_INVITING_USER = "other_inviting_user_model"; + private static final int MAX_SHOW_INVITING_USER = 4; + + private ImageView mMuteImg; + private LinearLayout mMuteLl; + private ImageView mHangupImg; + private LinearLayout mHangupLl; + private ImageView mHandsfreeImg; + private LinearLayout mHandsfreeLl; + private ImageView mDialingImg; + private LinearLayout mDialingLl; + private TRTCVideoLayoutManager mLayoutManagerTrtc; + private Group mInvitingGroup; + private LinearLayout mImgContainerLl; + private TextView mTimeTv; + private TextView mStatusView; + private ImageView mSponsorAvatarImg; + private TextView mSponsorUserNameTv; + private Group mSponsorGroup; + private Runnable mTimeRunnable; + private int mTimeCount; + private Handler mTimeHandler; + private HandlerThread mTimeHandlerThread; + + /** + * 拨号相关成员变量 + */ + private UserInfo mSelfModel; + private List mCallUserInfoList = new ArrayList<>(); // 呼叫方 + private Map mCallUserModelMap = new HashMap<>(); + private UserInfo mSponsorUserInfo; // 被叫方 + private List mOtherInvitingUserInfoList; + private int mCallType; + private TRTCCallingImpl mTRTCCalling; + private boolean isHandsFree = true; + private boolean isMuteMic = false; + private volatile boolean mIsEnterRoom = false; + private Map mUserOfflineMap = new HashMap<>(); + + private TimerTask otherEnterRoomTask = null; + private TimerTask enterRoomTask = null; + private NetWorkStateReceiver netWorkStateReceiver; + + private PermissionDialog permissionDialog = null; + private boolean requestCameraPermission = false; + private boolean requestRecordAudioPermission = false; + + /** + * 拨号的回调 + */ + private TRTCCallingDelegate mTRTCCallingDelegate = new TRTCCallingDelegate() { + @Override + public void onError(int code, String msg) { //发生了错误,报错并退出该页面 + stopCameraAndFinish(); + } + + @Override + public void onInvited(String sponsor, List userIdList, boolean isFromGroup, int callType) { } + + @Override + public void onGroupCallInviteeListUpdate(List userIdList) { } + + @Override + public void onUserEnter(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + showCallingView(); + removeOtherIsEnterRoom15secondsTask(); + //1.先造一个虚拟的用户添加到屏幕上 + UserInfo model = new UserInfo(); + model.setUserId(userId); + model.userName = userId; + model.userAvatar = ""; + mCallUserInfoList.add(model); + mCallUserModelMap.put(model.getUserId(), model); + TRTCVideoLayout videoLayout = addUserToManager(model); + if (videoLayout == null) { + return; + } + mIsEnterRoom = true; + videoLayout.setVideoAvailable(false); + mStatusView.setText(R.string.trtccalling_dialed_is_busy_vedio); + mStatusView.setVisibility(View.INVISIBLE); + } + }); + } + + @Override + public void onUserLeave(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + //1. 回收界面元素 + mLayoutManagerTrtc.recyclerCloudViewView(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + Boolean offline = mUserOfflineMap.get(userId); + if (offline != null && offline) { + Toast.makeText(TRTCVideoCallActivity.this, R.string.trtccalling_customer_offline, Toast.LENGTH_SHORT).show(); + } else { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_hand_up); + mStatusView.setVisibility(View.VISIBLE); + } + }); + } + stopCameraAndFinish(); + } + }); + } + + @Override + public void onReject(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mCallUserModelMap.containsKey(userId)) { + // 进入拒绝环节 + //1. 回收界面元素 + mLayoutManagerTrtc.recyclerCloudViewView(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + stopCameraAndFinish(); + } + } + }); + } + + @Override + public void onNoResp(final String userId) { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (mCallUserModelMap.containsKey(userId)) { + // 进入无响应环节 + //1. 回收界面元素 + mLayoutManagerTrtc.recyclerCloudViewView(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + stopCameraAndFinish(); + } + } + }); + } + + @Override + public void onLineBusy(String userId) { + if (mCallUserModelMap.containsKey(userId)) { + // 进入无响应环节 + //1. 回收界面元素 + mLayoutManagerTrtc.recyclerCloudViewView(userId); + //2. 删除用户model + UserInfo userInfo = mCallUserModelMap.remove(userId); + if (userInfo != null) { + mCallUserInfoList.remove(userInfo); + } + stopCameraAndFinish(); + } + } + + @Override + public void onCallingCancel() { + stopCameraAndFinish(); + } + + @Override + public void onCallingTimeout() { + stopCameraAndFinish(); + } + + @Override + public void onCallEnd() { + stopCameraAndFinish(); + } + + @Override + public void onUserVideoAvailable(final String userId, final boolean isVideoAvailable) { + //有用户的视频开启了 + TRTCVideoLayout layout = mLayoutManagerTrtc.findCloudViewView(userId); + if (layout != null) { + layout.setVideoAvailable(isVideoAvailable); + if (isVideoAvailable) { + mTRTCCalling.startRemoteView(userId, layout.getVideoView()); + } else { + mTRTCCalling.stopRemoteView(userId); + } + } + } + + @Override + public void onUserAudioAvailable(String userId, boolean isVideoAvailable) { } + + @Override + public void onUserVoiceVolume(Map volumeMap) { + for (Map.Entry entry : volumeMap.entrySet()) { + String userId = entry.getKey(); + TRTCVideoLayout layout = mLayoutManagerTrtc.findCloudViewView(userId); + if (layout != null) { + layout.setAudioVolumeProgress(entry.getValue()); + } + } + } + }; + + /** + * 主动拨打给某个用户 + * + * @param context + * @param roomKey + */ + public static void startCallSomeone(Context context, RoomKey roomKey, String beingCallUserId) { + Intent starter = new Intent(context, TRTCVideoCallActivity.class); + starter.putExtra(PARAM_TYPE, TYPE_CALL); + starter.putExtra(PARAM_SELF_INFO, JSON.toJSONString(roomKey)); + UserInfo beingCallUserInfo = new UserInfo(); + beingCallUserInfo.setUserId(beingCallUserId); + starter.putExtra(PARAM_BEINGCALL_USER, beingCallUserInfo); + context.startActivity(starter); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_CALLING.getValue(); + } + + /** + * 作为用户被叫 + * + * @param context + * @param beingCallUserId + */ + public static void startBeingCall(Context context, RoomKey roomKey, String beingCallUserId) { + Intent starter = new Intent(context, TRTCVideoCallActivity.class); + starter.putExtra(PARAM_TYPE, TYPE_BEING_CALLED); + starter.putExtra(PARAM_SELF_INFO, JSON.toJSONString(roomKey)); + UserInfo beingCallUserInfo = new UserInfo(); + beingCallUserInfo.setUserId(beingCallUserId); + starter.putExtra(PARAM_BEINGCALL_USER, beingCallUserInfo); + starter.putExtra(PARAM_OTHER_INVITING_USER, new IntentParams(new ArrayList())); + starter.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(starter); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_CALLING.getValue(); + } + + private void checkoutOtherIsEnterRoom15seconds() { + if (otherEnterRoomTask == null) { + otherEnterRoomTask = new TimerTask(){ + public void run(){ + //自己已进入房间15秒内对方没有进入房间 则显示对方已挂断,并主动退出,进入了就取消timertask + runOnUiThread(new Runnable() { + @Override + public void run() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_hand_up); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + }); + } + }; + Timer timer = new Timer(); + timer.schedule(otherEnterRoomTask, 15000); + } + } + + private void removeOtherIsEnterRoom15secondsTask() { + if (otherEnterRoomTask != null) { + otherEnterRoomTask.cancel(); + otherEnterRoomTask = null; + } + } + + private void checkoutIsEnterRoom60seconds(final boolean calling, final String message) { + if (enterRoomTask == null) { + enterRoomTask = new TimerTask(){ + public void run(){ + //呼叫了60秒,对方未接听 显示对方无人接听,并退出,进入了就取消timertask + runOnUiThread(new Runnable() { + @Override + public void run() { + runOnUiThread(new Runnable() { + @Override + public void run() { + if (calling) { + mStatusView.setText(message); + } + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + }); + } + }; + Timer timer = new Timer(); + timer.schedule(enterRoomTask, 60000); + } + } + + private void removeIsEnterRoom60secondsTask() { + if (enterRoomTask != null) { + enterRoomTask.cancel(); + enterRoomTask = null; + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == 102 || requestCode == 103) { + permissionDialog.dismiss(); + permissionDialog = null; + if (!requestCameraPermission || !requestRecordAudioPermission) { + checkAndRequestPermission(); + } else { + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + } + } + } + + private void checkAndRequestPermission() { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED && !requestCameraPermission) { + // 查看请求camera权限的时间是否大于48小时 + String cameraJsonString = Utils.INSTANCE.getStringValueFromXml(this, CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA); + long lasttime = 0L; + if (cameraJsonString != null) { + JSONObject cameraJson = JSON.parseObject(cameraJsonString); + lasttime = cameraJson.getLong(CommonField.PERMISSION_CAMERA); + } + if (cameraJsonString != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48 * 60 * 60) { + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + T.show(getString(com.tencent.iot.explorer.link.R.string.permission_of_camera_refuse)); + return; + } + if (permissionDialog == null) { + permissionDialog = new PermissionDialog(this, com.tencent.iot.explorer.link.R.mipmap.permission_camera ,getString(com.tencent.iot.explorer.link.R.string.permission_camera_lips), getString(com.tencent.iot.explorer.link.R.string.permission_camera_trtc)); + permissionDialog.show(); + requestCameraPermission = true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 102); + + // 记录请求camera权限的时间 + JSONObject json = new JSONObject(); + json.put(CommonField.PERMISSION_CAMERA, System.currentTimeMillis() / 1000); + Utils.INSTANCE.setXmlStringValue(this, CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA, json.toJSONString()); + return; + } + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_DENIED && !requestRecordAudioPermission) { + // 查看请求mic权限的时间是否大于48小时 + String micJsonString = Utils.INSTANCE.getStringValueFromXml(this, CommonField.PERMISSION_MIC, CommonField.PERMISSION_MIC); + long lasttime = 0L; + if (micJsonString != null) { + JSONObject micJson = JSON.parseObject(micJsonString); + lasttime = micJson.getLong(CommonField.PERMISSION_MIC); + } + if (micJsonString != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48 * 60 * 60) { + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + T.show(getString(com.tencent.iot.explorer.link.R.string.permission_of_camera_mic_refuse)); + return; + } + if (permissionDialog == null) { + permissionDialog = new PermissionDialog(this, com.tencent.iot.explorer.link.R.mipmap.permission_mic ,getString(com.tencent.iot.explorer.link.R.string.permission_mic_lips), getString(com.tencent.iot.explorer.link.R.string.permission_camera_trtc)); + permissionDialog.show(); + requestRecordAudioPermission = true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 103); + + // 记录请求mic权限的时间 + JSONObject json = new JSONObject(); + json.put(CommonField.PERMISSION_MIC, System.currentTimeMillis() / 1000); + Utils.INSTANCE.setXmlStringValue(this, CommonField.PERMISSION_MIC, CommonField.PERMISSION_MIC, json.toJSONString()); + return; + } + boolean calling = initData(); + initListener(); + checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + } + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 应用运行时,保持不锁屏、全屏化 + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + requestWindowFeature(Window.FEATURE_NO_TITLE); + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + setContentView(R.layout.trtccalling_videocall_activity_call_main); + startNetworkBroadcastReceiver(this); + + TRTCUIManager.getInstance().addCallingParamsCallback(new TRTCCallingParamsCallback() { + @Override + public void joinRoom(Integer callingType, String deviceId, RoomKey roomKey) { //接听电话 + startInviting(roomKey); + if (roomKey != null) { + removeIsEnterRoom60secondsTask(); + } + showCallingView(); + checkoutOtherIsEnterRoom15seconds(); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_ON_THE_PHONE.getValue(); + } + + @Override + public void exitRoom() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_hand_up); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + + @Override + public void userBusy() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_busy); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + + @Override + public void otherUserAccept() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_other_customer_accpet); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + + @Override + public void userOffline(String deviceId) { + mUserOfflineMap.put(deviceId, true); + } + }); + + initView(); + checkAndRequestPermission(); +// boolean calling = initData(); +// initListener(); +// checkoutIsEnterRoom60seconds(calling, getString(R.string.trtccalling_customer_no_resp)); + } + + @Override + public void onBackPressed() { + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + super.onBackPressed(); + } + + private void stopCameraAndFinish() { + mTRTCCalling.exitRoom(); + mTRTCCalling.closeCamera(); + finish(); + TRTCUIManager.getInstance().isCalling = false; + TRTCUIManager.getInstance().deviceId = ""; + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_IDLE_OR_REFUSE.getValue(); + TRTCUIManager.getInstance().removeCallingParamsCallback(); + removeIsEnterRoom60secondsTask(); + removeOtherIsEnterRoom15secondsTask(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + stopTimeCount(); + mTimeHandlerThread.quit(); + } + + //在onResume()方法注册 + @Override + protected void onResume() { + registerNetworkBroadcastReceiver(this); + Log.e(TAG, "注册netWorkStateReceiver"); + super.onResume(); + } + + //onPause()方法注销 + @Override + protected void onPause() { + unregisterNetworkBroadcastReceiver(this); + Log.e(TAG, "注销netWorkStateReceiver"); + super.onPause(); + } + + public void startNetworkBroadcastReceiver(Context currentContext) { + netWorkStateReceiver = new NetWorkStateReceiver(); + IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); + registerReceiver(netWorkStateReceiver, filter); + netWorkStateReceiver.addListener((NetWorkStateReceiver.NetworkStateReceiverListener) currentContext); + registerNetworkBroadcastReceiver(currentContext); + } + + /** + * Register the NetworkStateReceiver with your activity + * + * @param currentContext + */ + public void registerNetworkBroadcastReceiver(Context currentContext) { + currentContext.registerReceiver(netWorkStateReceiver, new IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION)); + } + + /** + * Unregister the NetworkStateReceiver with your activity + * + * @param currentContext + */ + public void unregisterNetworkBroadcastReceiver(Context currentContext) { + currentContext.unregisterReceiver(netWorkStateReceiver); + } + + @Override + public void networkAvailable() { + Log.e(TAG, "networkAvailable"); + if (mIsEnterRoom) { + removeIsEnterRoom60secondsTask(); + } + } + + @Override + public void networkUnavailable() { + Log.e(TAG, "networkUnavailable"); + Toast.makeText(this, R.string.trtccalling_customer_no_net, Toast.LENGTH_SHORT).show(); + checkoutIsEnterRoom60seconds(true, getString(R.string.trtccalling_customer_no_net)); + } + + private void initListener() { + mMuteLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isMuteMic = !isMuteMic; + mTRTCCalling.setMicMute(isMuteMic); + mMuteImg.setActivated(isMuteMic); + } + }); + mHandsfreeLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + isHandsFree = !isHandsFree; + mTRTCCalling.setHandsFree(isHandsFree); + mHandsfreeImg.setActivated(isHandsFree); + } + }); + mMuteImg.setActivated(isMuteMic); + mHandsfreeImg.setActivated(isHandsFree); + } + + private boolean initData() { + // 初始化成员变量 + mTRTCCalling = new TRTCCallingImpl(this); + mTRTCCalling.setTRTCCallingDelegate(mTRTCCallingDelegate); + mTimeHandlerThread = new HandlerThread("time-count-thread"); + mTimeHandlerThread.start(); + mTimeHandler = new Handler(mTimeHandlerThread.getLooper()); + // 初始化从外界获取的数据 + Intent intent = getIntent(); + + String roomKeyStr = intent.getStringExtra(PARAM_SELF_INFO); + if (TextUtils.isEmpty(roomKeyStr)) { + finish(); + } + RoomKey roomKey = JSON.parseObject(roomKeyStr, RoomKey.class); + mSelfModel = new UserInfo(); + mSelfModel.setUserId(roomKey.getUserId()); + //自己的资料 + mCallType = intent.getIntExtra(PARAM_TYPE, TYPE_BEING_CALLED); + mSponsorUserInfo = (UserInfo) intent.getSerializableExtra(PARAM_BEINGCALL_USER); + if (mCallType == TYPE_BEING_CALLED) { + // 作为被叫 + IntentParams params = (IntentParams) intent.getSerializableExtra(PARAM_OTHER_INVITING_USER); + if (params != null) { + mOtherInvitingUserInfoList = params.mUserInfos; + } + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_customer_calling_vedio); + mStatusView.setVisibility(View.VISIBLE); + } + }); + showWaitingResponseView(); + return false; + } else { + // 主叫方 + if (roomKey != null) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(R.string.trtccalling_waiting_to_hear_vedio); + mStatusView.setVisibility(View.VISIBLE); + } + }); + showInvitingView(); + } + return true; + } + } + + private void startInviting(RoomKey roomKey) { + mTRTCCalling.enterTRTCRoom(roomKey); + } + + private void initView() { + mMuteImg = (ImageView) findViewById(R.id.iv_mute); + mMuteLl = (LinearLayout) findViewById(R.id.ll_mute); + mHangupImg = (ImageView) findViewById(R.id.iv_hangup); + mHangupLl = (LinearLayout) findViewById(R.id.ll_hangup); + mHandsfreeImg = (ImageView) findViewById(R.id.iv_handsfree); + mHandsfreeLl = (LinearLayout) findViewById(R.id.ll_handsfree); + mDialingImg = (ImageView) findViewById(R.id.iv_dialing); + mDialingLl = (LinearLayout) findViewById(R.id.ll_dialing); + mLayoutManagerTrtc = (TRTCVideoLayoutManager) findViewById(R.id.trtc_layout_manager); + mInvitingGroup = (Group) findViewById(R.id.group_inviting); + mImgContainerLl = (LinearLayout) findViewById(R.id.ll_img_container); + mTimeTv = (TextView) findViewById(R.id.tv_time); + mSponsorAvatarImg = (ImageView) findViewById(R.id.iv_sponsor_avatar); + mSponsorUserNameTv = (TextView) findViewById(R.id.tv_sponsor_user_name); + mSponsorGroup = (Group) findViewById(R.id.group_sponsor); + mStatusView = (TextView) findViewById(R.id.tv_status); + } + + /** + * 等待接听界面 + */ + public void showWaitingResponseView() { + //1. 展示自己的画面 + mLayoutManagerTrtc.setMySelfUserId(mSelfModel.getUserId()); + TRTCVideoLayout videoLayout = addUserToManager(mSelfModel); + if (videoLayout == null) { + return; + } + videoLayout.setVideoAvailable(true); + mTRTCCalling.openCamera(true, videoLayout.getVideoView()); + + //2. 展示对方的头像和蒙层 + mSponsorGroup.setVisibility(View.VISIBLE); + mSponsorGroup.setVisibility(View.INVISIBLE); + mSponsorUserNameTv.setText(mSponsorUserInfo.userName); + mSponsorUserNameTv.setVisibility(View.INVISIBLE); + + //3. 展示电话对应界面 + mHangupLl.setVisibility(View.VISIBLE); + mDialingLl.setVisibility(View.VISIBLE); + mHandsfreeLl.setVisibility(View.GONE); + mMuteLl.setVisibility(View.GONE); + //3. 设置对应的listener + mHangupLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + } + }); + mDialingLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().didAcceptJoinRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + } + }); + } + + /** + * 展示邀请列表 + */ + public void showInvitingView() { + //1. 展示自己的界面 + mLayoutManagerTrtc.setMySelfUserId(mSelfModel.getUserId()); + TRTCVideoLayout videoLayout = addUserToManager(mSelfModel); + if (videoLayout == null) { + return; + } + videoLayout.setVideoAvailable(true); + mTRTCCalling.openCamera(true, videoLayout.getVideoView()); + mHangupLl.setVisibility(View.VISIBLE); + mHangupLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + } + }); + mDialingLl.setVisibility(View.GONE); + mHandsfreeLl.setVisibility(View.VISIBLE); + mMuteLl.setVisibility(View.VISIBLE); + //3. 隐藏中间他们也在界面 + hideOtherInvitingUserView(); + //4. sponsor画面也隐藏 + mSponsorGroup.setVisibility(View.GONE); + } + + /** + * 展示通话中的界面 + */ + public void showCallingView() { + //1. 蒙版消失 + mSponsorGroup.setVisibility(View.GONE); + //2. 底部状态栏 + mHangupLl.setVisibility(View.VISIBLE); + mDialingLl.setVisibility(View.GONE); + mHandsfreeLl.setVisibility(View.VISIBLE); + mMuteLl.setVisibility(View.VISIBLE); + + mHangupLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mTRTCCalling.exitRoom(); + stopCameraAndFinish(); + } + }); + showTimeCount(); + hideOtherInvitingUserView(); + } + + private void showTimeCount() { + if (mTimeRunnable != null) { + return; + } + mTimeCount = 0; + mTimeTv.setText(getShowTime(mTimeCount)); + if (mTimeRunnable == null) { + mTimeRunnable = new Runnable() { + @Override + public void run() { + mTimeCount++; + if (mTimeTv != null) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mTimeTv.setText(getShowTime(mTimeCount)); + } + }); + } + mTimeHandler.postDelayed(mTimeRunnable, 1000); + } + }; + } + mTimeHandler.postDelayed(mTimeRunnable, 1000); + } + + private void stopTimeCount() { + mTimeHandler.removeCallbacks(mTimeRunnable); + mTimeRunnable = null; + } + + private String getShowTime(int count) { + return getString(R.string.trtccalling_called_time_format, count / 60, count % 60); + } + + private void showOtherInvitingUserView() { + if (mOtherInvitingUserInfoList == null || mOtherInvitingUserInfoList.size() == 0) { + return; + } + mInvitingGroup.setVisibility(View.VISIBLE); + int squareWidth = getResources().getDimensionPixelOffset(R.dimen.trtccalling_small_image_size); + int leftMargin = getResources().getDimensionPixelOffset(R.dimen.trtccalling_small_image_left_margin); + for (int index = 0; index < mOtherInvitingUserInfoList.size() && index < MAX_SHOW_INVITING_USER; index++) { + UserInfo userInfo = mOtherInvitingUserInfoList.get(index); + ImageView imageView = new ImageView(this); + LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(squareWidth, squareWidth); + if (index != 0) { + layoutParams.leftMargin = leftMargin; + } + imageView.setLayoutParams(layoutParams); + Picasso.get().load(userInfo.userAvatar).into(imageView); + mImgContainerLl.addView(imageView); + } + } + + private void hideOtherInvitingUserView() { + mInvitingGroup.setVisibility(View.GONE); + } + + private TRTCVideoLayout addUserToManager(UserInfo userInfo) { + TRTCVideoLayout layout = mLayoutManagerTrtc.allocCloudVideoView(userInfo.getUserId()); + if (layout == null) { + return null; + } + return layout; + } +} diff --git a/sdk/explorer-link-rtc/src/main/java/com/tencent/iot/explorer/link/rtc/ui/videocall/videolayout/SquareImageView.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/SquareImageView.java similarity index 93% rename from sdk/explorer-link-rtc/src/main/java/com/tencent/iot/explorer/link/rtc/ui/videocall/videolayout/SquareImageView.java rename to app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/SquareImageView.java index 7edd19d75..38f17aee3 100644 --- a/sdk/explorer-link-rtc/src/main/java/com/tencent/iot/explorer/link/rtc/ui/videocall/videolayout/SquareImageView.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/SquareImageView.java @@ -1,4 +1,4 @@ -package com.tencent.iot.explorer.link.rtc.ui.videocall.videolayout; +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.videolayout; import android.content.Context; import android.util.AttributeSet; diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/TRTCVideoLayout.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/TRTCVideoLayout.java new file mode 100644 index 000000000..dfacf1201 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/TRTCVideoLayout.java @@ -0,0 +1,92 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.videolayout; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.tencent.iot.explorer.link.rtc.R; +import com.tencent.rtmp.ui.TXCloudVideoView; + +/** + * Module: TRTCVideoLayout + *

+ * Function: + *

+ * 此 TRTCVideoLayout 封装了{@link TXCloudVideoView} 以及业务逻辑 UI 控件 + */ +public class TRTCVideoLayout extends RelativeLayout { + private boolean mMoveAble; + private TXCloudVideoView mTCCloudViewTRTC; + private SquareImageView mImageHead; + private TextView mTextUserName; + private FrameLayout mLayoutNoVideo; + private ProgressBar mProgressAudio; + + + public TRTCVideoLayout(Context context) { + this(context, null); + } + + public TRTCVideoLayout(Context context, AttributeSet attrs) { + super(context, attrs); + initView(); + setClickable(true); + } + + public TXCloudVideoView getVideoView() { + return mTCCloudViewTRTC; + } + + public SquareImageView getHeadImg() { + return mImageHead; + } + + public TextView getUserNameTv() { + return mTextUserName; + } + + public void setVideoAvailable(boolean available) { + if (available) { + mTCCloudViewTRTC.setVisibility(VISIBLE); + mLayoutNoVideo.setVisibility(GONE); + } else { + mTCCloudViewTRTC.setVisibility(GONE); + mLayoutNoVideo.setVisibility(GONE); + } + } + + public void setAudioVolumeProgress(int progress) { + if (mProgressAudio != null) { + mProgressAudio.setProgress(progress); + } + } + + public void setAudioVolumeProgressBarVisibility(int visibility) { + if (mProgressAudio != null) { + mProgressAudio.setVisibility(visibility); + } + } + + private void initView() { + LayoutInflater.from(getContext()).inflate(R.layout.trtccalling_videocall_item_user_layout, this, true); + mTCCloudViewTRTC = (TXCloudVideoView) findViewById(R.id.trtc_tc_cloud_view); + mImageHead = (SquareImageView) findViewById(R.id.iv_avatar); + mTextUserName = (TextView) findViewById(R.id.tv_user_name); + mLayoutNoVideo = (FrameLayout) findViewById(R.id.fl_no_video); + mProgressAudio = (ProgressBar) findViewById(R.id.progress_bar_audio); + mProgressAudio.setVisibility(View.GONE); + } + + public boolean isMoveAble() { + return mMoveAble; + } + + public void setMoveAble(boolean enable) { + mMoveAble = enable; + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/TRTCVideoLayoutManager.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/TRTCVideoLayoutManager.java new file mode 100644 index 000000000..6c8f8fc04 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/TRTCVideoLayoutManager.java @@ -0,0 +1,386 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.videolayout; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.RelativeLayout; + +import com.tencent.rtmp.ui.TXCloudVideoView; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * Module: TRTCVideoViewLayout + *

+ * Function: {@link TXCloudVideoView} 的管理类 + *

+ * 1.在多人通话中,您的布局可能会比较复杂,Demo 也是如此,因此需要统一的管理类进行管理,这样子有利于写出高可维护的代码 + *

+ * 2.Demo 中提供堆叠布局、宫格布局两种展示方式;若您的项目也有相关的 UI 交互,您可以参考实现代码,能够快速集成。 + *

+ * 3.堆叠布局:{@link TRTCVideoLayoutManager#makeFloatLayout()} 思路是初始化一系列的 x、y、padding、margin 组合 LayoutParams 直接对 View 进行定位 + *

+ * 4.宫格布局:{@link TRTCVideoLayoutManager#makeGirdLayout(boolean)} 思路与堆叠布局一致,也是初始化一些列的 LayoutParams 直接对 View 进行定位 + *

+ * 5.如何实现管理: + * A. 使用{@link TRTCLayoutEntity} 实体类,保存 {@link TRTCVideoLayout} 的分配信息,能够与对应的用户绑定起来,方便管理与更新UI + * B. {@link TRTCVideoLayout} 专注实现业务 UI 相关的,控制逻辑放在此类中 + *

+ * 6.布局切换,见 {@link TRTCVideoLayoutManager#switchMode()} + *

+ * 7.堆叠布局与宫格布局参数,见{@link Utils} 工具类 + */ +public class TRTCVideoLayoutManager extends RelativeLayout { + private final static String TAG = TRTCVideoLayoutManager.class.getSimpleName(); + + public static final int MODE_FLOAT = 1; // 前后堆叠模式 + public static final int MODE_GRID = 2; // 九宫格模式 + public static final int MAX_USER = 9; + + private LinkedList mLayoutEntityList; + private ArrayList mFloatParamList; + private ArrayList mGrid4ParamList; + private ArrayList mGrid9ParamList; + private int mCount = 0; + private int mMode; + private String mSelfUserId; + private Context mContext; + + /** + * ===============================View相关=============================== + */ + public TRTCVideoLayoutManager(Context context) { + this(context, null); + } + + + public TRTCVideoLayoutManager(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + initView(context); + } + + private void initView(Context context) { + Log.i(TAG, "initView: "); + + mLayoutEntityList = new LinkedList<>(); + // 默认为堆叠模式 + mMode = MODE_FLOAT; + this.post(new Runnable() { + @Override + public void run() { + makeFloatLayout(); + } + }); + } + + public void setMySelfUserId(String userId) { + mSelfUserId = userId; + } + + /** + * 宫格布局与悬浮布局切换 + * + * @return + */ + public int switchMode() { + if (mMode == MODE_FLOAT) { + mMode = MODE_GRID; + makeGirdLayout(true); + } else { + mMode = MODE_FLOAT; + makeFloatLayout(); + } + return mMode; + } + + /** + * 根据 userId 找到已经分配的 View + * + * @param userId + * @return + */ + public TRTCVideoLayout findCloudViewView(String userId) { + if (userId == null) return null; + for (TRTCLayoutEntity layoutEntity : mLayoutEntityList) { + if (layoutEntity.userId.equals(userId)) { + return layoutEntity.layout; + } + } + return null; + } + + /** + * 根据 userId 分配对应的 view + * + * @param userId + * @return + */ + public TRTCVideoLayout allocCloudVideoView(String userId) { + if (userId == null) return null; + if (mCount > MAX_USER) { + return null; + } + TRTCLayoutEntity layoutEntity = new TRTCLayoutEntity(); + layoutEntity.userId = userId; + layoutEntity.layout = new TRTCVideoLayout(mContext); + layoutEntity.layout.setVisibility(VISIBLE); + initGestureListener(layoutEntity.layout, userId); + mLayoutEntityList.add(layoutEntity); + addView(layoutEntity.layout); + mCount++; + switchModeInternal(); + return layoutEntity.layout; + } + + private void switchModeInternal() { + if (mCount == 2) { + mMode = MODE_FLOAT; + makeFloatLayout(); + return; + } + if (mCount == 3) { + mMode = MODE_GRID; + makeGirdLayout(true); + return; + } + if (mCount >= 4 && mMode == MODE_GRID) { + makeGirdLayout(true); + return; + } + + } + + public interface OnSingleTapUpListener { + boolean onSingleTapUp(MotionEvent e); + } + + private void initGestureListener(final TRTCVideoLayout layout, final String userid) { + final GestureDetector detector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() { + @Override + public boolean onSingleTapUp(MotionEvent e) { + layout.performClick(); + makeFullVideoView(userid); + return super.onSingleTapUp(e); +// return true; + } + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + if (!layout.isMoveAble()) return false; + ViewGroup.LayoutParams params = layout.getLayoutParams(); + // 当 TRTCVideoView 的父容器是 RelativeLayout 的时候,可以实现拖动 + if (params instanceof LayoutParams) { + LayoutParams layoutParams = (LayoutParams) layout.getLayoutParams(); + int newX = (int) (layoutParams.leftMargin + (e2.getX() - e1.getX())); + int newY = (int) (layoutParams.topMargin + (e2.getY() - e1.getY())); + if (newX >= 0 && newX <= (getWidth() - layout.getWidth()) && newY >= 0 && newY <= (getHeight() - layout.getHeight())) { + layoutParams.leftMargin = newX; + layoutParams.topMargin = newY; + layout.setLayoutParams(layoutParams); + } + } + return true; + } + }); + layout.setOnTouchListener(new OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return detector.onTouchEvent(event); + } + }); + } + + + /** + * 根据 userId 和 视频类型,回收对应的 view + * + * @param userId + */ + public void recyclerCloudViewView(String userId) { + if (userId == null) return; + if (mMode == MODE_FLOAT) { + TRTCLayoutEntity entity = mLayoutEntityList.get(mLayoutEntityList.size() - 1); + // 当前离开的是处于0号位的人,那么需要将我换到这个位置 + if (userId.equals(entity.userId)) { + makeFullVideoView(mSelfUserId); + } + } else { + } + Iterator iterator = mLayoutEntityList.iterator(); + while (iterator.hasNext()) { + TRTCLayoutEntity item = (TRTCLayoutEntity) iterator.next(); + if (item.userId.equals(userId)) { + removeView(item.layout); + iterator.remove(); + mCount--; + break; + } + } + switchModeInternal(); + } + + /** + * 隐藏所有音量的进度条 + */ + public void hideAllAudioVolumeProgressBar() { + for (TRTCLayoutEntity entity : mLayoutEntityList) { + entity.layout.setAudioVolumeProgressBarVisibility(View.GONE); + } + } + + /** + * 显示所有音量的进度条 + */ + public void showAllAudioVolumeProgressBar() { + for (TRTCLayoutEntity entity : mLayoutEntityList) { + entity.layout.setAudioVolumeProgressBarVisibility(View.VISIBLE); + } + } + + /** + * 设置当前音量 + * + * @param userId + * @param audioVolume + */ + public void updateAudioVolume(String userId, int audioVolume) { + if (userId == null) return; + for (TRTCLayoutEntity entity : mLayoutEntityList) { + if (entity.layout.getVisibility() == VISIBLE) { + if (userId.equals(entity.userId)) { + entity.layout.setAudioVolumeProgress(audioVolume); + } + } + } + } + + private TRTCLayoutEntity findEntity(TRTCVideoLayout layout) { + for (TRTCLayoutEntity entity : mLayoutEntityList) { + if (entity.layout == layout) return entity; + } + return null; + } + + private TRTCLayoutEntity findEntity(String userId) { + for (TRTCLayoutEntity entity : mLayoutEntityList) { + if (entity.userId.equals(userId)) return entity; + } + return null; + } + + /** + * 切换到九宫格布局 + * + * @param needUpdate 是否需要更新布局 + */ + private void makeGirdLayout(boolean needUpdate) { + if (mGrid4ParamList == null || mGrid4ParamList.size() == 0 || mGrid9ParamList == null || mGrid9ParamList.size() == 0) { + mGrid4ParamList = Utils.initGrid4Param(getContext(), getWidth(), getHeight()); + mGrid9ParamList = Utils.initGrid9Param(getContext(), getWidth(), getHeight()); + } + if (needUpdate) { + ArrayList paramList; + if (mCount <= 4) { + paramList = mGrid4ParamList; + } else { + paramList = mGrid9ParamList; + } + int layoutIndex = 1; + for (int i = 0; i < mLayoutEntityList.size(); i++) { + TRTCLayoutEntity entity = mLayoutEntityList.get(i); + entity.layout.setMoveAble(false); + entity.layout.setOnClickListener(null); + // 我自己要放在布局的左上角 + if (entity.userId.equals(mSelfUserId)) { + entity.layout.setLayoutParams(paramList.get(0)); + } else if (layoutIndex < paramList.size()) { + entity.layout.setLayoutParams(paramList.get(layoutIndex++)); + } + } + } + } + + + /** + * ===============================九宫格布局相关=============================== + */ + + /** + * 切换到堆叠布局: + * 1. 如果堆叠布局参数未初始化先进行初始化:大画面+左右各三个画面 + * 2. 修改布局参数 + */ + private void makeFloatLayout() { + // 初始化堆叠布局的参数 + if (mFloatParamList == null || mFloatParamList.size() == 0) { + mFloatParamList = Utils.initFloatParamList(getContext(), getWidth(), getHeight()); + } + + // 根据堆叠布局参数,将每个view放到适当的位置,后加入的放在最大位 + int size = mLayoutEntityList.size(); + for (int i = 0; i < size; i++) { + TRTCLayoutEntity entity = mLayoutEntityList.get(size - i - 1); + LayoutParams layoutParams = mFloatParamList.get(i); + entity.layout.setLayoutParams(layoutParams); + if (i == 0) { + entity.layout.setMoveAble(false); + } else { + entity.layout.setMoveAble(true); + } + addFloatViewClickListener(entity); + bringChildToFront(entity.layout); + } + } + + /** + * ===============================堆叠布局相关=============================== + */ + + /** + * 对堆叠布局情况下的 View 添加监听器 + *

+ * 用于点击切换两个 View 的位置 + */ + private void addFloatViewClickListener(final TRTCLayoutEntity entity) { + final String userId = entity.userId; + entity.layout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (!TextUtils.isEmpty(userId)) { + makeFullVideoView(userId); + } + } + }); + } + + /** + * 堆叠模式下,将 userId 的 view 换到 0 号位,全屏化渲染 + * + * @param userId + */ + public void makeFullVideoView(String userId) { + Log.i(TAG, "makeFullVideoView: from = " + userId); + TRTCLayoutEntity entity = findEntity(userId); + mLayoutEntityList.remove(entity); + mLayoutEntityList.addLast(entity); + makeFloatLayout(); + } + + private static class TRTCLayoutEntity { + public TRTCVideoLayout layout; + public String userId = ""; + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/Utils.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/Utils.java new file mode 100644 index 000000000..3ac286756 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/rtcui/videocall/videolayout/Utils.java @@ -0,0 +1,179 @@ +package com.tencent.iot.explorer.link.kitlink.activity.rtcui.videocall.videolayout; + +import android.content.Context; +import android.view.ViewGroup; +import android.widget.RelativeLayout; + +import java.util.ArrayList; + +public class Utils { + public static int dip2px(Context context, float dpValue) { + final float scale = context.getResources().getDisplayMetrics().density; + return (int) (dpValue * scale + 0.5f); + } + + /** + * 堆叠布局初始化参数: + *

+ * 大画面在最下面,左右两排各三个小画面 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initFloatParamList(Context context, int layoutWidth, int layoutHeight) { + ArrayList list = new ArrayList(); + // 底部最大的布局 + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); + list.add(layoutParams0); + + final int midMargin = Utils.dip2px(context, 10); + final int lrMargin = Utils.dip2px(context, 15); + final int bottomMargin = Utils.dip2px(context, 50); + final int subWidth = Utils.dip2px(context, 120); + final int subHeight = Utils.dip2px(context, 180); + + for (int i = 2; i >= 0; i--) { + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(subWidth, subHeight); + layoutParams.leftMargin = layoutWidth - lrMargin - subWidth; + layoutParams.topMargin = layoutHeight - (bottomMargin + midMargin * (i + 1) + subHeight * i) - subHeight; + list.add(layoutParams); + } + + for (int i = 2; i >= 0; i--) { + RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(subWidth, subHeight); + layoutParams.leftMargin = lrMargin; + layoutParams.topMargin = layoutHeight - (bottomMargin + midMargin * (i + 1) + subHeight * i) - subHeight; + list.add(layoutParams); + } + return list; + } + + /** + * 四宫格布局参数 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid4Param(Context context, int layoutWidth, int layoutHeight) { + int margin = dip2px(context, 10); + int bottomMargin = dip2px(context, 50); + + ArrayList list = new ArrayList<>(); + int grid4W = (layoutWidth - margin * 2) / 2; + int grid4H = (layoutHeight - margin * 2 - bottomMargin) / 2; + + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams0.topMargin = margin; + layoutParams0.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams1.topMargin = margin; + layoutParams1.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams2.bottomMargin = margin + bottomMargin; + layoutParams2.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(grid4W, grid4H); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams3.bottomMargin = margin + bottomMargin; + layoutParams3.rightMargin = margin; + + list.add(layoutParams0); + list.add(layoutParams1); + list.add(layoutParams2); + list.add(layoutParams3); + return list; + } + + /** + * 九宫格布局参数 + * + * @param context + * @param layoutWidth + * @param layoutHeight + * @return + */ + public static ArrayList initGrid9Param(Context context, int layoutWidth, int layoutHeight) { + int margin = dip2px(context, 10); + int bottomMargin = dip2px(context, 50); + + ArrayList list = new ArrayList<>(); + + int grid9W = (layoutWidth - margin * 2) / 3; + int grid9H = (layoutHeight - margin * 2 - bottomMargin) / 3; + + RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams0.topMargin = margin; + layoutParams0.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams1.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams1.topMargin = margin; + + RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams2.topMargin = margin; + layoutParams2.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams3.leftMargin = margin; + layoutParams3.topMargin = margin + grid9H; + + RelativeLayout.LayoutParams layoutParams4 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams4.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams4.topMargin = margin + grid9H; + + RelativeLayout.LayoutParams layoutParams5 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams5.addRule(RelativeLayout.ALIGN_PARENT_TOP); + layoutParams5.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams5.topMargin = margin + grid9H; + layoutParams5.rightMargin = margin; + + RelativeLayout.LayoutParams layoutParams6 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams6.addRule(RelativeLayout.ALIGN_PARENT_LEFT); + layoutParams6.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams6.bottomMargin = margin + bottomMargin; + layoutParams6.leftMargin = margin; + + RelativeLayout.LayoutParams layoutParams7 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams7.addRule(RelativeLayout.CENTER_HORIZONTAL); + layoutParams7.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams7.bottomMargin = margin + bottomMargin; + + RelativeLayout.LayoutParams layoutParams8 = new RelativeLayout.LayoutParams(grid9W, grid9H); + layoutParams8.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); + layoutParams8.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); + layoutParams8.bottomMargin = margin + bottomMargin; + layoutParams8.rightMargin = margin; + + list.add(layoutParams0); + list.add(layoutParams1); + list.add(layoutParams2); + list.add(layoutParams3); + list.add(layoutParams4); + list.add(layoutParams5); + list.add(layoutParams6); + list.add(layoutParams7); + list.add(layoutParams8); + return list; + } + +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/videoui/ParamSettingActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/videoui/ParamSettingActivity.java new file mode 100644 index 000000000..ceed41d67 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/videoui/ParamSettingActivity.java @@ -0,0 +1,128 @@ +package com.tencent.iot.explorer.link.kitlink.activity.videoui; + +import android.hardware.Camera; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.tencent.iot.explorer.link.App; +import com.tencent.iot.explorer.link.R; +import com.tencent.iot.explorer.link.kitlink.activity.BaseActivity; +import com.tencent.iot.explorer.link.kitlink.adapter.FrameRateListAdapter; +import com.tencent.iot.explorer.link.kitlink.adapter.ResolutionListAdapter; +import com.tencent.iot.explorer.link.kitlink.entity.FrameRateEntity; +import com.tencent.iot.explorer.link.kitlink.entity.ResolutionEntity; +import com.tencent.iot.video.link.consts.CameraConstants; + +import java.util.ArrayList; +import java.util.List; + +public class ParamSettingActivity extends BaseActivity { + + private static final String TAG = ParamSettingActivity.class.getSimpleName(); + + private TextView titleTv; + private ImageView backIv; + private RecyclerView resolutionRv; + private ResolutionListAdapter resolutionAdapter = null; + private ArrayList resolutionDatas = new ArrayList<>(); + private RecyclerView frameRateRv; + private FrameRateListAdapter frameRateAdapter = null; + private ArrayList frameRateDatas = new ArrayList<>(); + private EditText frameDropEt; + private EditText frameSpeedEt; + private Button confirm; + + @Override + public int getContentView() { + return R.layout.activity_param_setting; + } + + @Override + public void initView() { + titleTv = findViewById(R.id.tv_title); + titleTv.setText("参数设置"); + backIv = findViewById(R.id.iv_back); + backIv.setVisibility(View.INVISIBLE); + resolutionRv = findViewById(R.id.rv_resolution); + getSupportedPreviewSizes(); + LinearLayoutManager resolutionLayoutManager = new LinearLayoutManager(this); + resolutionRv.setLayoutManager(resolutionLayoutManager); + resolutionRv.setHasFixedSize(false); + resolutionAdapter = new ResolutionListAdapter(ParamSettingActivity.this, resolutionDatas); + resolutionRv.setAdapter(resolutionAdapter); + + frameRateRv = findViewById(R.id.rv_frame_rate); + frameRateDatas = new ArrayList(); + frameRateDatas.add(new FrameRateEntity(15, true)); + frameRateDatas.add(new FrameRateEntity(30)); + LinearLayoutManager frameLayoutManager = new LinearLayoutManager(this); + frameRateRv.setLayoutManager(frameLayoutManager); + frameRateRv.setHasFixedSize(false); + frameRateAdapter = new FrameRateListAdapter(ParamSettingActivity.this, frameRateDatas); + frameRateRv.setAdapter(frameRateAdapter); + + frameDropEt = findViewById(R.id.et_framedrop); + frameSpeedEt = findViewById(R.id.et_framespeed); + confirm = findViewById(R.id.confirm); + } + + @Override + public void setListener() { + confirm.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + ResolutionEntity selectedResolutionEntity = resolutionAdapter.selectedResolutionEntity(); + FrameRateEntity selectedFrameRateEntity = frameRateAdapter.selectedFrameRateEntity(); + App.Companion.getData().setResolutionWidth(selectedResolutionEntity.getWidth()); + App.Companion.getData().setResolutionHeight(selectedResolutionEntity.getHeight()); + App.Companion.getData().setFrameRate(selectedFrameRateEntity.getRate()); + String framedrop = frameDropEt.getText().toString(); + if (!TextUtils.isEmpty(framedrop)) { + int fd = Integer.parseInt(framedrop); + App.Companion.getData().setFrameDrop(fd); + } + String framespeed = frameSpeedEt.getText().toString(); + if (!TextUtils.isEmpty(framespeed)) { + float fs = Float.parseFloat(framespeed); + App.Companion.getData().setFrameSpeed(fs); + } + finish(); + } + }); + } + + /** + * 获取设备支持的最大分辨率 + */ + private void getSupportedPreviewSizes() { + Camera camera = Camera.open(CameraConstants.facing.BACK); + //获取相机参数 + Camera.Parameters parameters = camera.getParameters(); + List list = parameters.getSupportedPreviewSizes(); + resolutionDatas = new ArrayList(); + for (Camera.Size size : list) { + Log.e(TAG, "****========== " + size.width + " " + size.height); + ResolutionEntity entity = new ResolutionEntity(size.width, size.height); + resolutionDatas.add(entity); + } + if (resolutionDatas.size() > 0) { + ResolutionEntity entity = resolutionDatas.get(resolutionDatas.size() - 1); + entity.setIsSelect(true); + } else { + Toast.makeText(ParamSettingActivity.this, "无法获取到设备Camera支持的分辨率", Toast.LENGTH_SHORT).show(); + } + camera.setPreviewCallback(null); + camera.stopPreview(); + camera.release(); + camera = null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/videoui/RecordVideoActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/videoui/RecordVideoActivity.java new file mode 100644 index 000000000..fd71f7127 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/activity/videoui/RecordVideoActivity.java @@ -0,0 +1,1002 @@ +package com.tencent.iot.explorer.link.kitlink.activity.videoui; + +import android.Manifest; +import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.Camera; +import android.media.AudioFormat; +import android.media.MediaRecorder; +import android.os.Build; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.TextureView; +import android.view.View; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; +import androidx.core.app.ActivityCompat; +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.tencent.iot.explorer.link.App; +import com.tencent.iot.explorer.link.T; +import com.tencent.iot.explorer.link.core.utils.Utils; +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog; +import com.tencent.iot.explorer.link.kitlink.activity.BaseActivity; +import com.tencent.iot.explorer.link.R; +import com.tencent.iot.explorer.link.kitlink.consts.CommonField; +import com.tencent.iot.explorer.link.kitlink.util.VideoUtils; +import com.tencent.iot.explorer.link.rtc.model.IntentParams; +import com.tencent.iot.explorer.link.rtc.model.RoomKey; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallStatus; +import com.tencent.iot.explorer.link.rtc.model.TRTCCalling; +import com.tencent.iot.explorer.link.rtc.model.TRTCCallingParamsCallback; +import com.tencent.iot.explorer.link.rtc.model.TRTCUIManager; +import com.tencent.iot.explorer.link.rtc.model.UserInfo; +import com.tencent.iot.thirdparty.flv.FLVListener; +import com.tencent.iot.thirdparty.flv.FLVPacker; +import com.tencent.iot.video.link.encoder.AudioEncoder; +import com.tencent.iot.video.link.encoder.VideoEncoder; +import com.tencent.iot.video.link.entity.DeviceStatus; +import com.tencent.iot.video.link.listener.OnEncodeListener; +import com.tencent.iot.video.link.param.AudioEncodeParam; +import com.tencent.iot.video.link.param.MicParam; +import com.tencent.iot.video.link.param.VideoEncodeParam; +import com.tencent.iot.video.link.consts.CallingType; +import com.tencent.iot.video.link.consts.CameraConstants; +import com.tencent.iot.video.link.util.CameraUtils; +import com.tencent.xnet.XP2P; + +import java.io.IOException; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import tv.danmaku.ijk.media.player.IjkMediaPlayer; + +import static com.tencent.iot.video.link.consts.LogConst.RTC_TAG; + +public class RecordVideoActivity extends BaseActivity implements TextureView.SurfaceTextureListener, OnEncodeListener, SurfaceHolder.Callback { + + private static Timer bitRateTimer; + private TextView mStatusView; + private LinearLayout mHangupLl; + private LinearLayout mDialingLl; + + public static final int TYPE_BEING_CALLED = 1; + public static final int TYPE_CALL = 2; + + public static final String PARAM_TYPE = "type"; + public static final String PARAM_IS_VIDEO = "is_video"; + public static final String PARAM_BEINGCALL_USER = "beingcall_user_model"; + public static final String PARAM_OTHER_INVITING_USER = "other_inviting_user_model"; + + private static final String TAG = RecordVideoActivity.class.getSimpleName(); + + private SurfaceView surfaceView; + private SurfaceHolder holder; + private Camera camera; + private Button btnSwitch; + private Handler handler = new Handler(); + private IjkMediaPlayer player; + private volatile Surface surface; + private TextureView playView; + private TextView tvTcpSpeed; + private TextView tvVCache; + private TextView tvACache; + private TextView tvVideoWH; + private volatile long basePts = 0; + private long startShowVideoTime = 0L; + + private volatile boolean isPause = true; + private volatile boolean isRenderView = false; + + private volatile long lastAudioCache5Time = 0L; + private volatile long lastPlayerSpeed0Time = 0L; + + private final FLVListener flvListener = + data -> { +// Log.e(TAG, "===== dataLen:" + data.length); + if (!isPause) { + XP2P.dataSend(TRTCUIManager.getInstance().deviceId, data, data.length); + } + }; + + private AudioEncoder audioEncoder; + private VideoEncoder videoEncoder; + private FLVPacker flvPacker; + private volatile boolean startEncodeVideo = false; + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + private TimerTask enterRoomTask = null; + + private int vw = 320; + private int vh = 240; + private int mFrameDrop = -1; + private float mFrameSpeed = 1.5f; + + /** + * 拨号相关成员变量 + */ + private UserInfo mSponsorUserInfo; // 被叫方 + private int mCallType; + private boolean mIsVideo; //是否为视频对话,true为视频 false为音频 + + private boolean isFirst = true; + + private PermissionDialog permissionDialog = null; + private boolean requestCameraPermission = false; + private boolean requestRecordAudioPermission = false; + + private long lastClickTime = 0L; + //两次点击间隔不少于1000ms + private static final int FAST_CLICK_DELAY_TIME = 1000; + + /** + * 主动拨打给某个用户 + * + * @param context + */ + public static void startCallSomeone(Context context, String beingCallUserId, boolean isVideo) { + Intent starter = new Intent(context, RecordVideoActivity.class); + starter.putExtra(PARAM_TYPE, TYPE_CALL); + starter.putExtra(PARAM_IS_VIDEO, isVideo); + UserInfo beingCallUserInfo = new UserInfo(); + beingCallUserInfo.setUserId(beingCallUserId); + starter.putExtra(PARAM_BEINGCALL_USER, beingCallUserInfo); + context.startActivity(starter); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_CALLING.getValue(); + TRTCUIManager.getInstance().isP2PCall = true; + } + + /** + * 作为用户被叫 + * + * @param context + * @param beingCallUserId + */ + public static void startBeingCall(Context context, String beingCallUserId, boolean isVideo) { + Intent starter = new Intent(context, RecordVideoActivity.class); + starter.putExtra(PARAM_TYPE, TYPE_BEING_CALLED); + starter.putExtra(PARAM_IS_VIDEO, isVideo); + UserInfo beingCallUserInfo = new UserInfo(); + beingCallUserInfo.setUserId(beingCallUserId); + starter.putExtra(PARAM_BEINGCALL_USER, beingCallUserInfo); + starter.putExtra(PARAM_OTHER_INVITING_USER, new IntentParams(new ArrayList())); + starter.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(starter); + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_CALLING.getValue(); + TRTCUIManager.getInstance().isP2PCall = true; + } + + @Override + public int getContentView() { + return R.layout.activity_record_video; + } + + @Override + protected void onResume() { + super.onResume(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregistVideoOverBrodcast(); + removeIsEnterRoom60secondsTask(); + isPause = true; + stopRecord(); + executor.shutdown(); + XP2P.stopSendService(TRTCUIManager.getInstance().deviceId, null); + TRTCUIManager.getInstance().deviceId = ""; + releaseCamera(camera); + if (player != null) { + mHandler.removeMessages(MSG_UPDATE_HUD); + player.release(); + player = null; + } + } + + @Override + public void initView() { + setContentView(R.layout.activity_record_video); + surfaceView = findViewById(R.id.camera_view); + holder = surfaceView.getHolder(); + holder.addCallback(this); + btnSwitch = findViewById(R.id.btn_switch); + playView = findViewById(R.id.v_play); + playView.setSurfaceTextureListener(this); + mStatusView = findViewById(R.id.tv_status); + mHangupLl = findViewById(R.id.ll_hangup); + mDialingLl = findViewById(R.id.ll_dialing); + tvTcpSpeed = findViewById(R.id.tv_tcp_speed); + tvVCache = findViewById(R.id.tv_v_cache); + tvACache = findViewById(R.id.tv_a_cache); + tvVideoWH = findViewById(R.id.tv_v_width_height); + + registVideoOverBrodcast(); + vw = App.Companion.getData().getResolutionWidth(); + vh = App.Companion.getData().getResolutionHeight(); + mFrameDrop = App.Companion.getData().getFrameDrop(); + mFrameSpeed = App.Companion.getData().getFrameSpeed(); + + initAudioEncoder(); + initVideoEncoder(); + + TRTCUIManager.getInstance().addCallingParamsCallback(new TRTCCallingParamsCallback() { + @Override + public void joinRoom(Integer callingType, String deviceId, RoomKey roomKey) { //设备方接听了电话 + runOnUiThread(() -> { + if (isFirst) { //由于android设备端目前上报了两次status=2,所以会回调两次,这里暂时规避下 + mStatusView.setVisibility(View.GONE); + showCallingView(); + isFirst = false; + } + }); + } + + @Override + public void exitRoom() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(com.tencent.iot.explorer.link.rtc.R.string.trtccalling_customer_hand_up); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + + @Override + public void userBusy() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(com.tencent.iot.explorer.link.rtc.R.string.trtccalling_customer_busy); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + + @Override + public void otherUserAccept() { + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(com.tencent.iot.explorer.link.rtc.R.string.trtccalling_other_customer_accpet); + mStatusView.setVisibility(View.VISIBLE); + } + }); + stopCameraAndFinish(); + } + + @Override + public void userOffline(String deviceId) { + + } + }); + checkAndRequestPermission(); + } + + private void initAudioEncoder() { + MicParam micParam = new MicParam.Builder() + .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) + .setSampleRateInHz(16000) // 采样率 + .setChannelConfig(AudioFormat.CHANNEL_IN_MONO) + .setAudioFormat(AudioFormat.ENCODING_PCM_16BIT) // PCM + .build(); + AudioEncodeParam audioEncodeParam = new AudioEncodeParam.Builder().build(); + audioEncoder = new AudioEncoder(micParam, audioEncodeParam,true, true); + audioEncoder.setOnEncodeListener(this); + } + + private void initVideoEncoder() { + VideoEncodeParam videoEncodeParam = new VideoEncodeParam.Builder().setSize(vw, vh) + .setFrameRate(App.Companion.getData().getFrameRate()) + .setBitRate(vw*vh).build(); + videoEncoder = new VideoEncoder(videoEncodeParam); + videoEncoder.setEncoderListener(this); + } + + public class AdapterBitRateTask extends TimerTask { + @Override + public void run() { + System.out.println("检测时间到:" +new Date()); + if (isPause) return; + + + int bufsize = XP2P.getStreamBufSize(TRTCUIManager.getInstance().deviceId); +// return String.format(Locale.US, "buf=>%d<=", bufsize); + +// videoEncoder.setVideoBitRate(10000); +// RecordVideoActivity.this.videoEncoder.setVideoBitRate(10000); + + int p2p_wl_avg = XP2P.getAvgMaxMin(bufsize); + + int now_video_rate = RecordVideoActivity.this.videoEncoder.getVideoBitRate(); + + Log.e(TAG,"send_bufsize==" + bufsize + ",now_video_rate==" + now_video_rate + ",avg_index==" + p2p_wl_avg); + + // 降码率 + // 当发现p2p的水线超过一定值时,降低视频码率,这是一个经验值,一般来说要大于 [视频码率/2] + // 实测设置为 80%视频码率 到 120%视频码率 比较理想 + // 在10组数据中,获取到平均值,并将平均水位与当前码率比对。 + + int video_rate_byte = (now_video_rate / 8) * 3 / 4; + if (p2p_wl_avg > video_rate_byte) { + + videoEncoder.setVideoBitRate(video_rate_byte*8); + + }else if (p2p_wl_avg < (now_video_rate / 8) / 3) { + + // 升码率 + // 测试发现升码率的速度慢一些效果更好 + // p2p水线经验值一般小于[视频码率/2],网络良好的情况会小于 [视频码率/3] 甚至更低 + videoEncoder.setVideoBitRate(now_video_rate + (now_video_rate-p2p_wl_avg*8)/5); + } + } + } + + private void startBitRateAdapter() { + + XP2P.resetAvg(); + isPause = false; + bitRateTimer = new Timer(); + bitRateTimer.schedule(new AdapterBitRateTask(),3000,1000); + } + + private void stopBitRateAdapter() { + if (bitRateTimer != null) { + bitRateTimer.cancel(); + bitRateTimer = null; + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == 102 || requestCode == 103) { + permissionDialog.dismiss(); + permissionDialog = null; + if (!requestCameraPermission || !requestRecordAudioPermission) { + checkAndRequestPermission(); + } else { + initData(); + } + } + } + + private void checkAndRequestPermission() { + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED && !requestCameraPermission) { + // 查看请求camera权限的时间是否大于48小时 + String cameraJsonString = Utils.INSTANCE.getStringValueFromXml(this, CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA); + long lasttime = 0L; + if (cameraJsonString != null) { + JSONObject cameraJson = JSON.parseObject(cameraJsonString); + lasttime = cameraJson.getLong(CommonField.PERMISSION_CAMERA); + } + if (cameraJsonString != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48 * 60 * 60) { + initData(); + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + T.show(getString(com.tencent.iot.explorer.link.R.string.permission_of_camera_refuse)); + return; + } + if (permissionDialog == null) { + permissionDialog = new PermissionDialog(this, com.tencent.iot.explorer.link.R.mipmap.permission_camera ,getString(com.tencent.iot.explorer.link.R.string.permission_camera_lips), getString(com.tencent.iot.explorer.link.R.string.permission_camera_trtc)); + permissionDialog.show(); + requestCameraPermission = true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 102); + + // 记录请求camera权限的时间 + JSONObject json = new JSONObject(); + json.put(CommonField.PERMISSION_CAMERA, System.currentTimeMillis() / 1000); + Utils.INSTANCE.setXmlStringValue(this, CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA, json.toJSONString()); + return; + } + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_DENIED && !requestRecordAudioPermission) { + // 查看请求mic权限的时间是否大于48小时 + String micJsonString = Utils.INSTANCE.getStringValueFromXml(this, CommonField.PERMISSION_MIC, CommonField.PERMISSION_MIC); + long lasttime = 0L; + if (micJsonString != null) { + JSONObject micJson = JSON.parseObject(micJsonString); + lasttime = micJson.getLong(CommonField.PERMISSION_MIC); + } + if (micJsonString != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48 * 60 * 60) { + boolean calling = initData(); + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(TRTCCalling.TYPE_VIDEO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + T.show(getString(com.tencent.iot.explorer.link.R.string.permission_of_camera_mic_refuse)); + return; + } + if (permissionDialog == null) { + permissionDialog = new PermissionDialog(this, com.tencent.iot.explorer.link.R.mipmap.permission_mic ,getString(com.tencent.iot.explorer.link.R.string.permission_mic_lips), getString(com.tencent.iot.explorer.link.R.string.permission_camera_trtc)); + permissionDialog.show(); + requestRecordAudioPermission = true; + } + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 103); + + // 记录请求mic权限的时间 + JSONObject json = new JSONObject(); + json.put(CommonField.PERMISSION_MIC, System.currentTimeMillis() / 1000); + Utils.INSTANCE.setXmlStringValue(this, CommonField.PERMISSION_MIC, CommonField.PERMISSION_MIC, json.toJSONString()); + return; + } + initData(); + } + + private boolean initData() { + // 初始化从外界获取的数据 + Intent intent = getIntent(); + //自己的资料 + mCallType = intent.getIntExtra(PARAM_TYPE, TYPE_BEING_CALLED); + mIsVideo = intent.getBooleanExtra(PARAM_IS_VIDEO, true); + mSponsorUserInfo = (UserInfo) intent.getSerializableExtra(PARAM_BEINGCALL_USER); + if (mCallType == TYPE_BEING_CALLED) { + // 作为被叫 + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(mIsVideo ? com.tencent.iot.explorer.link.rtc.R.string.trtccalling_customer_calling_vedio : com.tencent.iot.explorer.link.rtc.R.string.trtccalling_customer_calling_audio); + mStatusView.setVisibility(View.VISIBLE); + } + }); + showWaitingResponseView(); + return false; + } else { + // 主叫方 + runOnUiThread(new Runnable() { + @Override + public void run() { + mStatusView.setText(mIsVideo ? com.tencent.iot.explorer.link.rtc.R.string.trtccalling_waiting_to_hear_vedio : com.tencent.iot.explorer.link.rtc.R.string.trtccalling_waiting_to_hear_audio); + mStatusView.setVisibility(View.VISIBLE); + } + }); + showInvitingView(); + return true; + } + } + + @Override + public void setListener() { + btnSwitch.setOnClickListener(v -> { + if (System.currentTimeMillis() - lastClickTime >= FAST_CLICK_DELAY_TIME) { + switchCamera(); + lastClickTime = System.currentTimeMillis(); + } + }); + } + + private void stopCameraAndFinish() { + finish(); + TRTCUIManager.getInstance().isCalling = false; + TRTCUIManager.getInstance().callStatus = TRTCCallStatus.TYPE_IDLE_OR_REFUSE.getValue(); + TRTCUIManager.getInstance().removeCallingParamsCallback(); + TRTCUIManager.getInstance().isP2PCall = false; + } + + /** + * app被叫等待接听界面 + */ + public void showWaitingResponseView() { + if (mIsVideo) { // 需要绘制视频本地和对端画面 + openCamera(); + } else { // 需要绘制音频本地和对端画面 + btnSwitch.setVisibility(View.INVISIBLE); + surfaceView.setVisibility(View.INVISIBLE); + } + + //3. 展示电话对应界面 + mHangupLl.setVisibility(View.VISIBLE); + mDialingLl.setVisibility(View.VISIBLE); + //4. 设置对应的listener + mHangupLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(mIsVideo?TRTCCalling.TYPE_VIDEO_CALL:TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + } + }); + mDialingLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().didAcceptJoinRoom(mIsVideo?TRTCCalling.TYPE_VIDEO_CALL:TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + } + }); + } + + /** + * app主动呼叫界面 + */ + public void showInvitingView() { + if (mIsVideo) { // 需要绘制视频本地和对端画面 + openCamera(); + } else { // 需要绘制音频本地和对端画面 + btnSwitch.setVisibility(View.INVISIBLE); + surfaceView.setVisibility(View.INVISIBLE); + } + //1. 展示自己的界面 + mHangupLl.setVisibility(View.VISIBLE); + mHangupLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mSponsorUserInfo == null) { + stopCameraAndFinish(); + return; + } + TRTCUIManager.getInstance().refuseEnterRoom(mIsVideo?TRTCCalling.TYPE_VIDEO_CALL:TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + } + }); + mDialingLl.setVisibility(View.GONE); + } + + /** + * 展示通话中的界面 + */ + public void showCallingView() { + + handler.postDelayed(() -> { + if (mIsVideo) { // 需要绘制视频本地和对端画面 + play(CallingType.TYPE_VIDEO_CALL); + } else { // 需要绘制音频本地和对端画面 + surfaceView.setVisibility(View.INVISIBLE); + play(CallingType.TYPE_AUDIO_CALL); + } + }, 1000); + //2. 底部状态栏 + mHangupLl.setVisibility(View.VISIBLE); + mDialingLl.setVisibility(View.GONE); + + mHangupLl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + TRTCUIManager.getInstance().refuseEnterRoom(mIsVideo?TRTCCalling.TYPE_VIDEO_CALL:TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + //p2p挂断需要处理 + stopCameraAndFinish(); + } + }); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + if (surface != null) { + this.surface = new Surface(surface); + } + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + isRenderView = true; + if (startShowVideoTime > 0L) { + long showVideoTime = System.currentTimeMillis() - startShowVideoTime; + startShowVideoTime = 0L; + Log.i(RTC_TAG, "onSurfaceTextureUpdated, first show video time: " + showVideoTime); + } + } + + private void play(int callType) { + if (player != null) { + player.stop(); + player.setDisplay(null); + player.release(); + } else { + startShowVideoTime = System.currentTimeMillis(); + } + player = new IjkMediaPlayer(); + player.reset(); + mHandler.sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500); + if (!mIsVideo) { + player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1000); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 64); + } else { +// player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "analyzeduration", 1000000); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT, "probesize", 25 * 1024); + } + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", mFrameDrop); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_frame", 32);//保留关键帧跳帧 + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "packet-buffering", 0); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 1); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "threads", 1); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "sync-av-start", 0); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec",1); + player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1); + + player.setFrameSpeed(mFrameSpeed); + player.setMaxPacketNum(2); + player.setSurface(surface); + String url = XP2P.delegateHttpFlv(TRTCUIManager.getInstance().deviceId) + "ipc.flv?action=live"; +// Toast.makeText(this, url, Toast.LENGTH_LONG).show(); + Log.e(TAG, "======" + url); + try { + player.setDataSource(url); + } catch (IOException e) { + e.printStackTrace(); + } + player.prepareAsync(); + player.start(); + Log.e(TAG, "*====== player frameDrop: " + mFrameDrop + ", frameSpeed: " + mFrameSpeed); + + // 开始推流 + XP2P.runSendService(TRTCUIManager.getInstance().deviceId, "channel=0", false); + handler.postDelayed(() -> startRecord(callType), 1000); + } + + private void startRecord(int callType) { + if (callType == CallingType.TYPE_VIDEO_CALL) { + startEncodeVideo = true; + } + audioEncoder.start(); + + startBitRateAdapter(); + } + + private void stopRecord() { +// isRenderView = false; + if (audioEncoder != null) { + audioEncoder.stop(); + } + if (videoEncoder != null) { + videoEncoder.stop(); + } + startEncodeVideo = false; + + stopBitRateAdapter(); + } + + /** + * 打开相机 + */ + private void openCamera() { + releaseCamera(camera); + camera = Camera.open(facing); + //获取相机参数 + Camera.Parameters parameters = camera.getParameters(); + parameters.setRecordingHint(true); + parameters.setPreviewFrameRate(App.Companion.getData().getFrameRate()); + + //设置预览格式(也就是每一帧的视频格式)YUV420下的NV21 + parameters.setPreviewFormat(ImageFormat.NV21); + + if (this.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { + parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); + } + + int cameraIndex = -1; + if (facing == CameraConstants.facing.BACK) { + cameraIndex = Camera.CameraInfo.CAMERA_FACING_BACK; + } else if (facing == CameraConstants.facing.FRONT) { + cameraIndex = Camera.CameraInfo.CAMERA_FACING_FRONT; + camera.setDisplayOrientation(180); + } + + try { + camera.setDisplayOrientation(CameraUtils.getDisplayOrientation(this, cameraIndex)); + } catch (Exception e) { + e.printStackTrace(); + } + + + Camera.Size previewSize = getCameraPreviewSize(parameters); + //设置预览图像分辨率 + parameters.setPreviewSize(vw, vh); + //设置帧率 + parameters.setPreviewFrameRate(App.Companion.getData().getFrameRate()); + + //配置camera参数 + camera.setParameters(parameters); + try { + camera.setPreviewDisplay(holder); + } catch (IOException e) { + e.printStackTrace(); + } + //设置监听获取视频流的每一帧 + camera.setPreviewCallback(new Camera.PreviewCallback() { + @Override + public void onPreviewFrame(byte[] data, Camera camera) { + if (startEncodeVideo && videoEncoder != null) { + videoEncoder.encoderH264(data, facing == CameraConstants.facing.FRONT); + } + } + }); + //调用startPreview()用以更新preview的surface + camera.startPreview(); + } + + private void checkoutIsEnterRoom60seconds(String message) { + if (enterRoomTask == null) { + enterRoomTask = new TimerTask(){ + public void run(){ + //60秒重连失败退出,进入了就取消timertask + runOnUiThread(new Runnable() { + @Override + public void run() { +// Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); + Log.e(TAG, "*========stop send video data over 60"); + TRTCUIManager.getInstance().refuseEnterRoom(mIsVideo?TRTCCalling.TYPE_VIDEO_CALL:TRTCCalling.TYPE_AUDIO_CALL, mSponsorUserInfo.getUserId()); + stopCameraAndFinish(); + } + }); + } + }; + Timer timer = new Timer(); + timer.schedule(enterRoomTask, 60000); + } + } + + private void removeIsEnterRoom60secondsTask() { + if (enterRoomTask != null) { + enterRoomTask.cancel(); + enterRoomTask = null; + } + } + + /** + * 获取设备支持的最大分辨率 + */ + private Camera.Size getCameraPreviewSize(Camera.Parameters parameters) { + List list = parameters.getSupportedPreviewSizes(); + Camera.Size needSize = null; + for (Camera.Size size : list) { + Log.e(TAG, "****========== " + size.width + " " + size.height); + if (needSize == null) { + needSize = size; + continue; + } + if (size.width >= needSize.width) { + if (size.height > needSize.height) { + needSize = size; + } + } + } + return needSize; + } + + // 默认摄像头方向 + private int facing = CameraConstants.facing.BACK; + + private void switchCamera() { + if (facing == CameraConstants.facing.BACK) { + facing = CameraConstants.facing.FRONT; + } else { + facing = CameraConstants.facing.BACK; + } + openCamera(); + } + + /** + * 关闭相机 + */ + public void releaseCamera(Camera camera) { + if (camera != null) { + camera.setPreviewCallback(null); + camera.stopPreview(); + camera.release(); + camera = null; + } + } + + public void updateDashboard() { + long videoCachedDuration = player.getVideoCachedDuration(); + long audioCachedDuration = player.getAudioCachedDuration(); + long videoCachedBytes = player.getVideoCachedBytes(); + long audioCachedBytes = player.getAudioCachedBytes(); // (long)Math.random()*10;// + long tcpSpeed = player.getTcpSpeed(); + + float vdps = player.getVideoDecodeFramesPerSecond(); + float vfps = player.getVideoOutputFramesPerSecond(); + + if (audioCachedDuration > 5000) { //当前有大于5s的音频缓存 + Log.e(RTC_TAG, "audioCachedDuration:-----" + audioCachedDuration); + if (lastAudioCache5Time == 0L) { //之前有清空过lastAudioCache5Time时间,首出图后,再记录一下当前的时间 + if (isRenderView) { + lastAudioCache5Time = System.currentTimeMillis(); + } + } else { //前一次更新时没有清空过lastAudioCache5Time时间,计算并打印一下持续的时间。 + long tempTime1 = System.currentTimeMillis() - lastAudioCache5Time; + if (tempTime1 > 999) { //当前有大于5s的音频缓存,已经持续了20s,算作网络不好重新reset P2P服务 + Log.e(RTC_TAG, "lastAudioCache5Time:" + lastAudioCache5Time + ", audioCachedDuration" + audioCachedDuration + ", tempTime" + tempTime1 + ", need to reset P2P"); +// Toast.makeText(getApplicationContext(), "a_cache触发清缓存", Toast.LENGTH_LONG).show(); + lastAudioCache5Time = 0; + if (player != null) { + player.flushCache(); + } + } + } + } else { //小于等于5s的音频缓存,清空一下记录的时间 + lastAudioCache5Time = 0; + Log.e(RTC_TAG, "lastAudioCache5Time:" + lastAudioCache5Time + ", audioCachedDuration" + audioCachedDuration + ", reset 0"); + } + + if (tcpSpeed == 0) {//没有速度了,可能是播放器没有数据了 + if (lastPlayerSpeed0Time == 0) { //记录一下首次没有速度的时间 + if (isRenderView) { + lastPlayerSpeed0Time = System.currentTimeMillis(); + } + } else { //前一次更新时没有清空过lastPlayerSpeed0Time时间,计算并打印一下持续的时间。 + long tempTime2 = System.currentTimeMillis() - lastPlayerSpeed0Time; + if (tempTime2 > 5*1000) { //当前播放器没有速度,已经持续了20s,算作网络不好重新reset P2P服务 + Log.e(RTC_TAG, "lastPlayerSpeed0Time:" + lastPlayerSpeed0Time + ", tcpSpeed" + tcpSpeed + ", tempTime" + tempTime2 + ", need to reset P2P"); +// Toast.makeText(getApplicationContext(), "tcpSpeed触发重连", Toast.LENGTH_LONG).show(); + lastPlayerSpeed0Time = 0; + isPause = true; + flvPacker = null; + stopRecord(); + VideoUtils.sendNeedResetP2PBroadcast(App.Companion.getActivity(), 100); + } + } + } else { + lastPlayerSpeed0Time = 0; + Log.e(RTC_TAG, "lastPlayerSpeed0Time:" + lastPlayerSpeed0Time + ", tcpSpeed" + tcpSpeed + ", reset 0"); + } + + tvACache.setText(String.format(Locale.US, "%s, %s", + Utils.INSTANCE.formatedDurationMilli(audioCachedDuration), + Utils.INSTANCE.formatedSize(audioCachedBytes))); + tvVCache.setText(String.format(Locale.US, "%s, %s", + Utils.INSTANCE.formatedDurationMilli(videoCachedDuration), + Utils.INSTANCE.formatedSize(videoCachedBytes))); + tvTcpSpeed.setText(String.format(Locale.US, "%s", + Utils.INSTANCE.formatedSpeed(tcpSpeed, 1000))); + tvVideoWH.setText(player.getVideoWidth() + " x " + player.getVideoHeight()); + Log.i(RTC_TAG, String.format(Locale.US, "player fps : %.2f / %.2f", vdps, vfps)); + } + + private static final int MSG_UPDATE_HUD = 1; + + BroadcastReceiver recevier = new BroadcastReceiver() { + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onReceive(Context context, Intent intent) { + int refreshTag = intent.getIntExtra(VideoUtils.VIDEO_RESUME, 0); + + Log.d(TAG, "refreshTag: " + refreshTag); + if (refreshTag == 2) {//p2p链路断开2 + isPause = true; + flvPacker = null; + stopRecord(); +// XP2P.stopSendService(TRTCUIManager.getInstance().deviceId, null); + checkoutIsEnterRoom60seconds("通话结束..."); + } + if (refreshTag == 1) { //p2p连接成功1 + + initAudioEncoder(); + initVideoEncoder(); + + if (mIsVideo) { // 需要绘制视频本地和对端画面 + play(CallingType.TYPE_VIDEO_CALL); + } else { // 需要绘制音频本地和对端画面 + surfaceView.setVisibility(View.INVISIBLE); + play(CallingType.TYPE_AUDIO_CALL); + } + removeIsEnterRoom60secondsTask(); + } + } + }; + + private void registVideoOverBrodcast() { + Log.e(TAG, "registVideoOverBrodcast"); + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(RecordVideoActivity.this); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction("android.intent.action.CART_BROADCAST"); + broadcastManager.registerReceiver(recevier, intentFilter); + } + + private void unregistVideoOverBrodcast() { + Log.e(TAG, "unregistVideoOverBrodcast"); + LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(RecordVideoActivity.this); + broadcastManager.unregisterReceiver(recevier); + } + + @Override + public void onAudioEncoded(byte[] datas, long pts, long seq) { + if (executor.isShutdown()) return; + executor.submit(() -> { + if (!isPause) { + if (flvPacker == null) { + flvPacker = new FLVPacker(flvListener, true, true); + basePts = pts; + } + flvPacker.encodeFlv(datas, FLVPacker.TYPE_AUDIO, pts - basePts); + } + }); + } + + @Override + public void onVideoEncoded(byte[] datas, long pts, long seq) { + if (executor.isShutdown()) return; + executor.submit(() -> { + if (!isPause) { + if (flvPacker == null) { + flvPacker = new FLVPacker(flvListener, true, true); + basePts = pts; + } + flvPacker.encodeFlv(datas, FLVPacker.TYPE_VIDEO, pts - basePts); + } + }); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + openCamera(); + Log.d(TAG, "surface created."); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.d(TAG, "surface changed."); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.d(TAG, "surface destroyed."); + } + + private static class MyHandler extends Handler { + private final WeakReference mActivity; + + public MyHandler(RecordVideoActivity mActivity) { + this.mActivity = new WeakReference<>(mActivity); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + RecordVideoActivity activity = mActivity.get(); + if (activity != null) { + if (msg.what == MSG_UPDATE_HUD) { + activity.updateDashboard(); + removeMessages(MSG_UPDATE_HUD); + sendEmptyMessageDelayed(MSG_UPDATE_HUD, 500); + } + } + } + } + private final Handler mHandler = new MyHandler(this); +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/BleDeviceAdapter.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/BleDeviceAdapter.kt index e00940a9c..940e6495d 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/BleDeviceAdapter.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/BleDeviceAdapter.kt @@ -51,8 +51,8 @@ class BleDeviceAdapter(list: MutableList) : RecyclerView.Adapter 0) { titleTxt?.visibility = View.VISIBLE diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/FrameRateListAdapter.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/FrameRateListAdapter.java new file mode 100644 index 000000000..7d0c490e4 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/FrameRateListAdapter.java @@ -0,0 +1,85 @@ +package com.tencent.iot.explorer.link.kitlink.adapter; + +import android.content.Context; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.RequiresApi; +import androidx.recyclerview.widget.RecyclerView; + + +import com.tencent.iot.explorer.link.R; +import com.tencent.iot.explorer.link.kitlink.entity.FrameRateEntity; + +import java.util.ArrayList; + +public class FrameRateListAdapter extends RecyclerView.Adapter { + private ArrayList mDatas = null; + private LayoutInflater mInflater = null; + private Context mContext; + + public FrameRateListAdapter(Context context, ArrayList datas) { + this.mDatas = datas; + mContext = context; + this.mInflater = LayoutInflater.from(context); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mInflater.inflate(R.layout.frame_rate_list_item, parent, false); + ViewHolder vewHolder = new ViewHolder(view); + return vewHolder; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + final FrameRateEntity entity = mDatas.get(position); + holder.tv.setText(String.format("%d", entity.getRate())); + if (entity.getIsSelect()) { + holder.selectIv.setBackground(mContext.getDrawable(R.drawable.selected)); + } else { + holder.selectIv.setBackground(mContext.getDrawable(R.drawable.unselect)); + } + holder.selectIv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + for (FrameRateEntity enti : mDatas) { + enti.setIsSelect(false); + } + mDatas.get(position).setIsSelect(true); + notifyDataSetChanged(); + } + }); + } + + @Override + public int getItemCount() { + return mDatas == null ? 0 : mDatas.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + public ImageView selectIv = null; + public TextView tv = null; + + public ViewHolder(View itemView) { + super(itemView); + + selectIv = (ImageView) itemView.findViewById(R.id.iv_select); + tv = (TextView) itemView.findViewById(R.id.tv_frame_rate); + } + } + + public FrameRateEntity selectedFrameRateEntity() { + for (FrameRateEntity entity : mDatas) { + if (entity.getIsSelect()) { + return entity; + } + } + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/ResolutionListAdapter.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/ResolutionListAdapter.java new file mode 100644 index 000000000..0efdb36c1 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/adapter/ResolutionListAdapter.java @@ -0,0 +1,84 @@ +package com.tencent.iot.explorer.link.kitlink.adapter; + +import android.content.Context; +import android.os.Build; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.RequiresApi; +import androidx.recyclerview.widget.RecyclerView; + +import com.tencent.iot.explorer.link.R; +import com.tencent.iot.explorer.link.kitlink.entity.ResolutionEntity; + +import java.util.ArrayList; + +public class ResolutionListAdapter extends RecyclerView.Adapter { + private ArrayList mDatas = null; + private LayoutInflater mInflater = null; + private Context mContext; + + public ResolutionListAdapter(Context context, ArrayList datas) { + this.mDatas = datas; + mContext = context; + this.mInflater = LayoutInflater.from(context); + } + + @Override + public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View view = mInflater.inflate(R.layout.resolution_list_item, parent, false); + ViewHolder vewHolder = new ViewHolder(view); + return vewHolder; + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public void onBindViewHolder(final ViewHolder holder, int position) { + final ResolutionEntity entity = mDatas.get(position); + holder.tv.setText(String.format("%d * %d", entity.getWidth(), entity.getHeight())); + if (entity.getIsSelect()) { + holder.selectIv.setBackground(mContext.getDrawable(R.drawable.selected)); + } else { + holder.selectIv.setBackground(mContext.getDrawable(R.drawable.unselect)); + } + holder.selectIv.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + for (ResolutionEntity enti : mDatas) { + enti.setIsSelect(false); + } + mDatas.get(position).setIsSelect(true); + notifyDataSetChanged(); + } + }); + } + + @Override + public int getItemCount() { + return mDatas == null ? 0 : mDatas.size(); + } + + public class ViewHolder extends RecyclerView.ViewHolder { + public ImageView selectIv = null; + public TextView tv = null; + + public ViewHolder(View itemView) { + super(itemView); + + selectIv = (ImageView) itemView.findViewById(R.id.iv_select); + tv = (TextView) itemView.findViewById(R.id.tv_resolution); + } + } + + public ResolutionEntity selectedResolutionEntity() { + for (ResolutionEntity entity : mDatas) { + if (entity.getIsSelect()) { + return entity; + } + } + return null; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/CommonField.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/CommonField.kt index 11c3b4c7c..72a13f07f 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/CommonField.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/CommonField.kt @@ -39,7 +39,8 @@ object CommonField { const val HANDLER_NAME = "handlerName" const val MSG_TYPE = "msgType" const val CONFIG_QUESTION_LIST = "configQuestionList" - const val FIREBASE_USER_ID = "UserID" + const val FEEDBACK_DEVICE = "feedbackDevice" + const val FEEDBACK_CATEGORY = "feedbackCategory" const val LOAD_VIEW_TXT_TYPE = "loadViewTxtType" const val CONFIG_TYPE = "configType" const val HARD_WARE_GUIDE = "hardwareGuide" @@ -96,10 +97,14 @@ object CommonField { /*************** 注册 ****************/ const val TYPE = "Type" const val REG_COUNTRY_INFO = "RegCountryInfo" - const val USA_USER_REG_TIME_INFO = "RegTimeInfo" - const val USA_USER_REG_TIME_INFO_YEAR = "RegTimeInfoYear" - const val USA_USER_REG_TIME_INFO_MONTH = "RegTimeInfoMonth" - const val USA_USER_REG_TIME_INFO_DAY = "RegTimeInfoDay" + const val USA_USER_REG_TIME_INFO = "USARegTimeInfo" + const val USA_USER_REG_TIME_INFO_YEAR = "USARegTimeInfoYear" + const val USA_USER_REG_TIME_INFO_MONTH = "USARegTimeInfoMonth" + const val USA_USER_REG_TIME_INFO_DAY = "USARegTimeInfoDay" + const val CHINA_MAINLAND_USER_REG_TIME_INFO = "ChinaMainlandRegTimeInfo" + const val CHINA_MAINLAND_USER_REG_TIME_INFO_YEAR = "ChinaMainlandRegTimeInfoYear" + const val CHINA_MAINLAND_USER_REG_TIME_INFO_MONTH = "ChinaMainlandRegTimeInfoMonth" + const val CHINA_MAINLAND_USER_REG_TIME_INFO_DAY = "ChinaMainlandRegTimeInfoDay" const val COUNTRY_INFO = "CountryInfo" const val COUNTRY_CODE = "CountryCode" const val AGREED_RULE_FLAG = "agreeRule" @@ -123,6 +128,7 @@ object CommonField { const val OPENSOURCE_LICENSE_URL_EN = OPENSOURCE_LICENSE_URL const val OPENSOURCE_LICENSE_URL_ZH = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config11.js" + const val PRIVACY_POLICY_URL_CN_ZH = "https://privacy.qq.com/document/preview/0da010f1b4944e88bf78520d88642513" const val PRIVACY_POLICY_URL_CN_EN = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config10.js" const val PRIVACY_POLICY_URL_US_ZH = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config8.js" const val PRIVACY_POLICY_URL_US_EN = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config4.js" @@ -130,8 +136,12 @@ object CommonField { const val SERVICE_AGREEMENT_URL_US_ZH = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config7.js" const val SERVICE_AGREEMENT_URL_US_EN = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config3.js" const val DELET_ACCOUNT_POLICY_EN = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config6.js" - const val THIRD_SDK_URL_US_ZH = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config12.js" + const val THIRD_SDK_URL_US_ZH = "https://privacy.qq.com/document/preview/bd3470c6063f4ac6831900edcb60c9bd" const val THIRD_SDK_URL_US_EN = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config13.js" + const val PERSONAL_INFO_URL_US_ZH = "https://privacy.qq.com/document/preview/278bbbbbdd5748999fce491cfa6dd424" + const val PERSONAL_INFO_URL_US_EN = "http://qzonestyle.gtimg.cn/qzone/qzactStatics/qcloud/data/42/config15.js" + const val PRIVACY_POLICY_SUMMARY_URL_CN_ZH = "https://privacy.qq.com/document/preview/4f262b760a4543a7a26409064d1391c2" + const val PRIVACY_POLICY_SUMMARY_URL_CN_EN = "https://privacy.qq.com/document/preview/4f262b760a4543a7a26409064d1391c2" /************返回结果通用字段*************/ const val RESPONSE = "Response" @@ -220,4 +230,17 @@ object CommonField { const val DEFINE_TYPE_TIMESTAMP = "timestamp" const val DEFINE_TYPE_STRUCT = "struct" + + const val PERMISSION_SMS = "PermissionSms" + const val PERMISSION_CAMERA = "PermissionCamera" + const val PERMISSION_ALBUM = "PermissionAlbum" + const val PERMISSION_LOCATION = "PermissionLocation" + const val PERMISSION_MIC = "PermissionMic" + const val PERMISSION_STORAGE = "PermissionStorage" + + const val WIFI_INFO = "WiFiInfo" + const val WIFI_SSID = "WiFiSsid" + const val WIFI_PWD = "WiFiPassword" + const val CONFIG_NET_ERROR_CODE = "ConfigNetErrorCode" + } \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/SocketConstants.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/SocketConstants.kt index 411728ad4..0ae4db704 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/SocketConstants.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/consts/SocketConstants.kt @@ -4,7 +4,7 @@ object SocketConstants { //主机 - const val host = "wss://iot.cloud.tencent.com/ws/explorer" + const val host = "wss://iot.cloud.tencent.com/iotstudio_v2_weapp_1" //获取手机验证码的类型 const val register = "register" diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ConfigType.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ConfigType.kt index 41f319608..2a6258d51 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ConfigType.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ConfigType.kt @@ -3,5 +3,6 @@ package com.tencent.iot.explorer.link.kitlink.entity enum class ConfigType (val id: Int) { SoftAp(1), SmartConfig(0), - BleConfig(2); + BleConfig(2), + BleBindConfig(3); } \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/DevicePropertyEntity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/DevicePropertyEntity.kt index c431e3336..55c108d15 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/DevicePropertyEntity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/DevicePropertyEntity.kt @@ -13,6 +13,8 @@ import java.lang.Exception */ class DevicePropertyEntity { + var index = -1 + var id = "" var name = "" diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/FrameRateEntity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/FrameRateEntity.java new file mode 100644 index 000000000..9c85d73bc --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/FrameRateEntity.java @@ -0,0 +1,37 @@ +package com.tencent.iot.explorer.link.kitlink.entity; + +public class FrameRateEntity { + public static String TAG = FrameRateEntity.class.getSimpleName(); + private int rate; + private boolean isSelect = false; + + public FrameRateEntity() { + + } + + public FrameRateEntity(int rate) { + this.rate = rate; + this.isSelect = false; + } + + public FrameRateEntity(int rate, boolean isSelect) { + this.rate = rate; + this.isSelect = isSelect; + } + + public int getRate() { + return rate; + } + + public void setRate(int rate) { + this.rate = rate; + } + + public boolean getIsSelect() { + return isSelect; + } + + public void setIsSelect(boolean isSelect) { + this.isSelect = isSelect; + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ProductEntity.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ProductEntity.kt index 1e79a5d60..ef4583700 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ProductEntity.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ProductEntity.kt @@ -1,6 +1,7 @@ package com.tencent.iot.explorer.link.kitlink.entity class ProductEntity { + var CategoryId = 0 var ProductId = "" var NetType = "" var Name = "" diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ResolutionEntity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ResolutionEntity.java new file mode 100644 index 000000000..2b6cb105b --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/entity/ResolutionEntity.java @@ -0,0 +1,48 @@ +package com.tencent.iot.explorer.link.kitlink.entity; + +public class ResolutionEntity { + public static String TAG = ResolutionEntity.class.getSimpleName(); + private int width; + private int height; + private boolean isSelect = false; + + public ResolutionEntity(){ + + } + + public ResolutionEntity(int width, int height) { + this.width = width; + this.height = height; + this.isSelect = false; + } + + public ResolutionEntity(int width, int height, boolean isSelect) { + this.width = width; + this.height = height; + this.isSelect = isSelect; + } + + public int getWidth() { + return width; + } + + public void setWidth(int width) { + this.width = width; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public boolean getIsSelect() { + return isSelect; + } + + public void setIsSelect(boolean isSelect) { + this.isSelect = isSelect; + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/CommentFragment.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/CommentFragment.kt index 45816bcd8..9467a74c3 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/CommentFragment.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/CommentFragment.kt @@ -129,7 +129,7 @@ class CommentFragment : BaseFragment(), View.OnClickListener, MyCallback { if (response.code == 0) { var js = JSON.parse(response.data.toString()) as JSONObject var url = CommonField.H5_BASE_URL + "?ticket=" + js[CommonField.TOKEN_TICKET] - url += "&uin=${Utils.getAndroidID(context!!)}" + url += "&uin=${App.uuid}" url += "&lang=${Utils.getLang()}" if (!App.isOEMApp()) { url += "&appID=" + T.getContext().applicationInfo.packageName diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/DeviceFragment.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/DeviceFragment.kt index 2a11014d3..ce5d0a0fa 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/DeviceFragment.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/DeviceFragment.kt @@ -13,6 +13,7 @@ import android.view.ViewGroup import android.widget.* import androidx.core.content.ContextCompat import com.alibaba.fastjson.JSON +import com.alibaba.fastjson.JSONObject import com.squareup.picasso.Picasso import com.tencent.iot.explorer.link.App import com.tencent.iot.explorer.link.ErrorMessage @@ -35,6 +36,8 @@ import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.auth.response.BaseResponse import com.tencent.iot.explorer.link.core.link.entity.BleDevice import com.tencent.iot.explorer.link.core.link.service.BleConfigService +import com.tencent.iot.explorer.link.core.utils.Utils +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog import com.tencent.iot.explorer.link.kitlink.activity.BleConfigHardwareActivity import com.tencent.iot.explorer.link.kitlink.activity.BuleToothActivity import com.tencent.iot.explorer.link.kitlink.activity.SmartConfigStepActivity @@ -63,6 +66,7 @@ class DeviceFragment() : BaseFragment(), MyCallback, AdapterView.OnItemClickList @Volatile private var conditionPrefix = false + private var permissionDialog: PermissionDialog? = null private var permissions = arrayOf( Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.CHANGE_WIFI_STATE, @@ -221,7 +225,23 @@ class DeviceFragment() : BaseFragment(), MyCallback, AdapterView.OnItemClickList isRecommDeviceClicked = false } } + // 查看请求location权限的时间是否大于48小时 + var locationJsonString = Utils.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_LOCATION, CommonField.PERMISSION_LOCATION) + var locationJson: JSONObject? = JSONObject.parse(locationJsonString) as JSONObject? + val lasttime = locationJson?.getLong(CommonField.PERMISSION_LOCATION) + if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { + T.show(getString(R.string.permission_of_location_add_device_refuse)) + return + } + permissionDialog = PermissionDialog(App.activity, R.mipmap.permission_location ,getString(R.string.permission_location_lips), getString(R.string.permission_location_ssid)) + permissionDialog!!.show() requestPermissions(permissions,1) + + // 记录请求location权限的时间 + var json = JSONObject() + json.put(CommonField.PERMISSION_LOCATION, System.currentTimeMillis() / 1000) + Utils.setXmlStringValue(T.getContext(), CommonField.PERMISSION_LOCATION, CommonField.PERMISSION_LOCATION, json.toJSONString()) + } else { when (parent.id) { R.id.gv_recommend_devices->{ // 根据推荐设备的配网方式,跳转到SmartConfig或者SoftAp配网界面 @@ -262,6 +282,8 @@ class DeviceFragment() : BaseFragment(), MyCallback, AdapterView.OnItemClickList jumpActivity(SmartConfigStepActivity::class.java) } } + permissionDialog?.dismiss() + permissionDialog = null } fun setListener() { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/HomeFragment.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/HomeFragment.kt index 90ecddd20..09cc57142 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/HomeFragment.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/HomeFragment.kt @@ -1,18 +1,22 @@ package com.tencent.iot.explorer.link.kitlink.fragment +import android.Manifest import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.content.pm.PackageManager import android.os.Handler import android.text.TextUtils import android.view.View +import androidx.core.app.ActivityCompat import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.alibaba.fastjson.JSON import com.alibaba.fastjson.JSONException +import com.alibaba.fastjson.JSONObject import com.google.android.material.appbar.AppBarLayout import com.scwang.smart.refresh.footer.ClassicsFooter import com.scwang.smart.refresh.header.ClassicsHeader @@ -34,6 +38,7 @@ import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.customview.dialog.DevModeSetDialog import com.tencent.iot.explorer.link.customview.dialog.entity.KeyBooleanValue import com.tencent.iot.explorer.link.customview.dialog.MoreOptionDialog +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog import com.tencent.iot.explorer.link.customview.dialog.entity.DevOption import com.tencent.iot.explorer.link.customview.dialog.entity.OptionMore import com.tencent.iot.explorer.link.kitlink.activity.ControlPanelActivity @@ -52,6 +57,7 @@ import com.tencent.iot.explorer.link.mvp.IPresenter import com.tencent.iot.explorer.link.mvp.presenter.HomeFragmentPresenter import com.tencent.iot.explorer.link.mvp.view.HomeFragmentView import com.tencent.iot.explorer.link.rtc.model.RoomKey +import com.tencent.iot.explorer.link.core.utils.Utils import kotlinx.android.synthetic.main.fragment_main.* import kotlinx.android.synthetic.main.header.* import kotlinx.android.synthetic.main.inside_fixed_bar.* @@ -68,6 +74,8 @@ class HomeFragment : BaseFragment(), HomeFragmentView, MyCallback, PayloadMessag private var roomList: ArrayList = ArrayList() private var roomsAdapter: RoomsAdapter? = null private var handler = Handler() + private var deviceListEnd = false + private var shareDeviceListEnd = false override fun getContentView(): Int { return R.layout.fragment_main @@ -210,6 +218,11 @@ class HomeFragment : BaseFragment(), HomeFragmentView, MyCallback, PayloadMessag } } + override fun onResume() { + super.onResume() + requestData() + } + private fun initView() { var devGridLayoutManager = GridLayoutManager(context, 2) roomDevAdapter = RoomDevAdapter(devList) @@ -392,7 +405,7 @@ class HomeFragment : BaseFragment(), HomeFragmentView, MyCallback, PayloadMessag override fun onSaveClicked() { var value = "" if (!devOption.modeInt!!.ifInteger) { - var len = Utils.length(devOption!!.modeInt!!.step) + var len = w_length(devOption!!.modeInt!!.step) value = String.format("%.${len}f", dialog.progress) } else { value = dialog.progress.toInt().toString() @@ -404,6 +417,24 @@ class HomeFragment : BaseFragment(), HomeFragmentView, MyCallback, PayloadMessag }) } + fun w_length(num: Float): Int { + var len = 1 + val str = num.toString() + val parts = str.split(".") + if (parts != null && parts.size == 2) { + for (i in parts[1].length - 1 downTo 1) { + if (parts[1][i].toString() != "0") { + len = i + 1 + break + } + } + } + if (len <= 0) { + len = 1 + } + return len + } + private fun convertOptionMore(dev: DeviceEntity, shortCut: ProductUIDevShortCutConfig) : OptionMore { var optionMore = OptionMore() optionMore.title = dev.getAlias() @@ -589,6 +620,22 @@ class HomeFragment : BaseFragment(), HomeFragmentView, MyCallback, PayloadMessag } else { layout_no_dev_2_show.visibility = View.GONE } + + if (deviceListEnd) { + this.deviceListEnd = true + } + if (shareDeviceListEnd) { + this.shareDeviceListEnd = true + } + } + + private fun checkPermissions(permission: String): Boolean { + if (App.activity?.let { ActivityCompat.checkSelfPermission(it, permission) } == PackageManager.PERMISSION_DENIED) { + L.e(permission + "被拒绝") + return false + } + L.e(permission + "已经申请成功") + return true } override fun showDeviceOnline() { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/MeFragment.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/MeFragment.kt index c19af4e7e..d0ae87d77 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/MeFragment.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/fragment/MeFragment.kt @@ -68,7 +68,7 @@ class MeFragment : BaseFragment(), View.OnClickListener, MyCallback { jumpActivity(HelpWebViewActivity::class.java) } tv_me_feedback -> { - jumpActivity(FeedbackActivity::class.java) +// jumpActivity(FeedbackActivity::class.java) } tv_me_about -> { jumpActivity(AboutUsActivity::class.java) diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FamilyInfoHeaderHolder.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FamilyInfoHeaderHolder.kt index d8c57b4aa..ffbc68ce3 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FamilyInfoHeaderHolder.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FamilyInfoHeaderHolder.kt @@ -23,6 +23,7 @@ class FamilyInfoHeaderHolder : CRecyclerView.HeadViewHolder { try { var address = JSON.parseObject(Address, com.tencent.iot.explorer.link.kitlink.entity.Address::class.java) itemView.tv_head_family_address.text = address?.name + itemView.tv_head_family_address.visibility = if(showAddress) View.VISIBLE else View.GONE } catch (e : JSONException) { e.printStackTrace() } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FeedbackViewHolder.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FeedbackViewHolder.kt deleted file mode 100644 index 8e79deee0..000000000 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/holder/FeedbackViewHolder.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.tencent.iot.explorer.link.kitlink.holder - -import android.text.TextUtils -import android.view.View -import com.tencent.iot.explorer.link.R -import com.tencent.iot.explorer.link.kitlink.activity.FeedbackActivity -import com.tencent.iot.explorer.link.customview.recyclerview.CRecyclerView -import com.tencent.iot.explorer.link.kitlink.util.picture.imp.ImageManager -import kotlinx.android.synthetic.main.item_feekback.view.* - -class FeedbackViewHolder : CRecyclerView.CViewHolder { - constructor(itemView: View) : super(itemView) - - override fun show(position: Int) { - entity?.run { - if (!TextUtils.isEmpty(url)) { - itemView.iv_feedback.let { - ImageManager.setImagePath(itemView.context, it, url, it.width, it.height) - } - itemView.iv_feedback_delete.visibility = View.VISIBLE - } else { - itemView.iv_feedback.setImageResource(R.drawable.image_add) - itemView.iv_feedback_delete.visibility = View.INVISIBLE - } - itemView.setOnClickListener { - recyclerItemView?.doAction( - this@FeedbackViewHolder, - itemView, - position - ) - } - itemView.iv_feedback_delete.setOnClickListener { - recyclerItemView?.doAction( - this@FeedbackViewHolder, - itemView.iv_feedback_delete, - position - ) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/BeepManager.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/BeepManager.java new file mode 100644 index 000000000..5b4994842 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/BeepManager.java @@ -0,0 +1,112 @@ +package com.tencent.iot.explorer.link.kitlink.util; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.res.AssetFileDescriptor; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Vibrator; +import android.preference.PreferenceManager; +import android.util.Log; + +import com.example.qrcode.Constant; + +import java.io.Closeable; +import java.io.IOException; + +/** + * Created by yangyu on 17/10/19. + */ + +public final class BeepManager implements MediaPlayer.OnErrorListener, Closeable { + + private static final String TAG = BeepManager.class.getSimpleName(); + + private static final float BEEP_VOLUME = 0.10f; + private static final long VIBRATE_DURATION = 200L; + + private final Activity activity; + private MediaPlayer mediaPlayer; + private boolean playBeep; + private boolean vibrate; + + public BeepManager(Activity activity) { + this.activity = activity; + this.mediaPlayer = null; + updatePrefs(); + } + + public synchronized void updatePrefs() { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity); + playBeep = shouldBeep(prefs, activity); + vibrate = prefs.getBoolean(Constant.KEY_VIBRATE, false); + if (playBeep && mediaPlayer == null) { + // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud, + // so we now play on the music stream. + activity.setVolumeControlStream(AudioManager.STREAM_MUSIC); + mediaPlayer = buildMediaPlayer(activity); + } + } + + public synchronized void playBeepSoundAndVibrate() { + if (playBeep && mediaPlayer != null) { + mediaPlayer.start(); + } + if (vibrate) { + Vibrator vibrator = (Vibrator) activity.getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(VIBRATE_DURATION); + } + } + + private static boolean shouldBeep(SharedPreferences prefs, Context activity) { + boolean shouldPlayBeep = prefs.getBoolean(Constant.KEY_PLAY_BEEP, true); + if (shouldPlayBeep) { + // See if sound settings overrides this + AudioManager audioService = (AudioManager) activity.getSystemService(Context.AUDIO_SERVICE); + if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) { + shouldPlayBeep = false; + } + } + return shouldPlayBeep; + } + + private MediaPlayer buildMediaPlayer(Context activity) { + MediaPlayer mediaPlayer = new MediaPlayer(); + try (AssetFileDescriptor file = activity.getResources().openRawResourceFd(com.example.qrcode.R.raw.beep)) { + mediaPlayer.setDataSource(file.getFileDescriptor(), file.getStartOffset(), file.getLength()); + mediaPlayer.setOnErrorListener(this); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + mediaPlayer.setLooping(false); + mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME); + mediaPlayer.prepare(); + return mediaPlayer; + } catch (IOException ioe) { + Log.w(TAG, ioe); + mediaPlayer.release(); + return null; + } + } + + @Override + public synchronized boolean onError(MediaPlayer mp, int what, int extra) { + if (what == MediaPlayer.MEDIA_ERROR_SERVER_DIED) { + // we are finished, so put up an appropriate error toast if required and finish + activity.finish(); + } else { + // possibly media player error, so release and recreate + close(); + updatePrefs(); + } + return true; + } + + @Override + public synchronized void close() { + if (mediaPlayer != null) { + mediaPlayer.release(); + mediaPlayer = null; + } + } + +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/HttpRequest.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/HttpRequest.kt index 44f5e536b..1772de59e 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/HttpRequest.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/HttpRequest.kt @@ -54,7 +54,7 @@ class HttpRequest private constructor() { const val BUSI_APP = "studioapp" const val BUSI_OPENSOURCE = "studioappOpensource" - val ANDROID_ID = Utils.getAndroidID(T.getContext()) + val ANDROID_ID = App.uuid val PLATFORM_TAG = "android" } @@ -953,7 +953,7 @@ class HttpRequest private constructor() { val param = tokenParams("AppSigBindDeviceInFamily") param["FamilyId"] = familyId param["RoomId"] = roomId - param["DeviceId"] = deviceInfo.productId + "/" + deviceInfo.deviceName + param["DeviceId"] = "${deviceInfo.productId}/${deviceInfo.deviceName}" param["DeviceTimestamp"] = deviceInfo.timestamp param["ConnId"] = deviceInfo.connId param["SignMethod"] = deviceInfo.signMethod diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/LogcatHelper.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/LogcatHelper.java index 6a5d9c8e1..f91551ea2 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/LogcatHelper.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/LogcatHelper.java @@ -96,12 +96,7 @@ public void run() { while (mRunning && (line = mReader.readLine()) != null) { if (!mRunning) break; if (line.length() == 0) continue; - if (out != null && line.contains(mPID) && !line.contains("FA ") - && !line.contains("Firebase") && !line.contains("zygote") - && !line.contains("libEGL") && !line.contains("TPush") - && !line.contains("Hw") && !line.contains("OpenGL") - && !line.contains("CubicBezier") && !line.contains("PressGestureDetector") - && !line.contains("AppCompatDelegate") && !line.contains("VideoCapabilities")) { + if (out != null && line.contains(mPID)) { out.write((line + "\n").getBytes()); } } @@ -131,7 +126,7 @@ public void run() { } } private String getFileName() { - SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm"); return format.format(new Date(System.currentTimeMillis())); } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/Utils.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/Utils.kt index a5cdf62db..6312baf64 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/Utils.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/Utils.kt @@ -34,4 +34,20 @@ class Utils { return len } } -} \ No newline at end of file +} + +fun Int?.asBoolean(): Boolean = if (this == null) false else this != 0 + +fun Int?.safe(default: Int = 0): Int = this ?: default + +fun Long?.safe(default: Long = 0): Long = this ?: default + +fun Double?.safe(default: Double = 0.0): Double = this ?: default + +fun Float?.safe(default: Float = 0.0f): Float = this ?: default + +fun Boolean?.safe(default: Boolean = false): Boolean = this ?: default + +fun String?.safe(): String = this ?: "" + +fun String?.safe(default: String): String = this ?: default \ No newline at end of file diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/VideoUtils.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/VideoUtils.java new file mode 100644 index 000000000..7484bd2f3 --- /dev/null +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/VideoUtils.java @@ -0,0 +1,25 @@ +package com.tencent.iot.explorer.link.kitlink.util; + +import android.content.Context; +import android.content.Intent; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; + +public class VideoUtils { + public static String VIDEO_RESUME = "videoResume"; + public static String VIDEO_RESET = "videoReset"; + + public static void sendVideoBroadcast(Context context, int value) { + Intent intent = new Intent("android.intent.action.CART_BROADCAST"); + intent.putExtra(VIDEO_RESUME, value); + LocalBroadcastManager.getInstance(context).sendBroadcast(intent); + context.sendBroadcast(intent); + } + + public static void sendNeedResetP2PBroadcast(Context context, int value) { + Intent intent = new Intent("android.intent.action.CART_BROADCAST"); + intent.putExtra(VIDEO_RESET, value); + LocalBroadcastManager.getInstance(context).sendBroadcast(intent); + context.sendBroadcast(intent); + } +} diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/WeatherUtils.kt b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/WeatherUtils.kt index 7358d8dc9..7eca1cfc3 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/WeatherUtils.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/WeatherUtils.kt @@ -4,12 +4,9 @@ import android.content.Context import com.alibaba.fastjson.JSON import com.tencent.iot.explorer.link.BuildConfig import com.tencent.iot.explorer.link.T -import com.tencent.iot.explorer.link.core.utils.LocationUtil import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.entity.CityInfo import com.tencent.iot.explorer.link.kitlink.entity.WeatherInfo -import com.tencent.map.geolocation.TencentLocation -import com.tencent.map.geolocation.TencentLocationListener import okhttp3.* import java.io.IOException @@ -65,25 +62,7 @@ object WeatherUtils { } fun getLocalCurrentWeatherInfo(context: Context, listener: OnWeatherListener) { - LocationUtil.getCurrentLocation(context, object : TencentLocationListener { - override fun onStatusUpdate(p0: String?, p1: Int, p2: String?) {} - override fun onLocationChanged(p0: TencentLocation?, p1: Int, p2: String?) { - if (p0 == null || p1 != 0) { - if (listener != null) { - listener.onWeatherFailed(p1) - } - return - } - - var cityInfo = CityInfo() - cityInfo.lat = p0.latitude.toString() - cityInfo.lon = p0.longitude.toString() - cityInfo.name = p0.district - getWeatherInfo(cityInfo, listener) - } - - }) } fun getWeatherInfoByLocation(lat: Double, lon: Double, listener: OnWeatherListener) { diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorActivity.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorActivity.java index fa60951a0..80549b3a9 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorActivity.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorActivity.java @@ -15,6 +15,7 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; @@ -26,6 +27,7 @@ import java.io.File; import java.io.InputStreamReader; import java.util.ArrayList; +import java.util.List; /** @@ -345,6 +347,15 @@ public static String getSDCardPath() { return Environment.getExternalStorageDirectory().getPath(); } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + List fragments = getSupportFragmentManager().getFragments(); + if (fragments.size() > 0) { + fragments.get(0).onRequestPermissionsResult(requestCode, permissions, grantResults); + } + } + /** * ClassName: Mode * Description: 单选MODE_SINGLE 多选MODE_MULTI diff --git a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorFragment.java b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorFragment.java index b6d33de71..0a604496e 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorFragment.java +++ b/app/src/main/java/com/tencent/iot/explorer/link/kitlink/util/picture/imageselectorbrowser/ImageSelectorFragment.java @@ -1,10 +1,12 @@ package com.tencent.iot.explorer.link.kitlink.util.picture.imageselectorbrowser; +import android.Manifest; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.ContentValues; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.database.Cursor; import android.graphics.Color; @@ -27,13 +29,20 @@ import android.widget.TextView; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; import androidx.fragment.app.Fragment; import androidx.loader.app.LoaderManager; import androidx.loader.content.CursorLoader; import androidx.loader.content.Loader; +import com.alibaba.fastjson.JSONObject; import com.tencent.iot.explorer.link.R; +import com.tencent.iot.explorer.link.T; import com.tencent.iot.explorer.link.core.log.L; +import com.tencent.iot.explorer.link.core.utils.Utils; +import com.tencent.iot.explorer.link.customview.dialog.PermissionDialog; +import com.tencent.iot.explorer.link.kitlink.consts.CommonField; import com.tencent.iot.explorer.link.kitlink.util.picture.imageselectorbrowser.ImageSelectorActivity.Mode; import com.tencent.iot.explorer.link.kitlink.util.picture.imp.ImageManager; import com.tencent.iot.explorer.link.kitlink.util.picture.utils.FileUtils; @@ -101,6 +110,8 @@ public class ImageSelectorFragment extends Fragment implements OnClickListener{ private File mTmpFile; private String mTmpPath; + private PermissionDialog permissionDialog; + @Override public void onAttach(Activity activity) { super.onAttach(activity); @@ -242,7 +253,28 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { Toast.makeText(getActivity(), R.string.imageselector_msg_amount_limit, Toast.LENGTH_SHORT).show(); return; } - showCameraAction(); + String[] permissions = { Manifest.permission.CAMERA }; + if (checkPermissions(permissions)) { + showCameraAction(); + } else { + // 查看请求camera权限的时间是否大于48小时 + String cameraJsonString = Utils.INSTANCE.getStringValueFromXml(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA); + JSONObject cameraJson = (JSONObject) JSONObject.parse(cameraJsonString); + if (cameraJson != null) { + Long lasttime = cameraJson.getLong(CommonField.PERMISSION_CAMERA); + if (lasttime != null && lasttime > 0 && System.currentTimeMillis() / 1000 - lasttime < 48*60*60) { + T.show(getResources().getString(R.string.permission_of_camera_refuse)); + return; + } + } + permissionDialog = new PermissionDialog(getActivity(), R.mipmap.permission_camera ,getResources().getString(R.string.permission_camera_lips), getResources().getString(R.string.permission_camera_avatar)); + permissionDialog.show(); + ActivityCompat.requestPermissions(getActivity(), permissions, 102); + // 记录请求camera权限的时间 + JSONObject json = new JSONObject(); + json.put(CommonField.PERMISSION_CAMERA, System.currentTimeMillis() / 1000); + Utils.INSTANCE.setXmlStringValue(T.getContext(), CommonField.PERMISSION_CAMERA, CommonField.PERMISSION_CAMERA, json.toJSONString()); + } }else{ // 正常操作 ImageSelectorImageBean image = (ImageSelectorImageBean) adapterView.getAdapter().getItem(i); @@ -265,6 +297,32 @@ public void onItemClick(AdapterView adapterView, View view, int i, long l) { } } + private boolean checkPermissions(String[] permissions) { + for (String ele: permissions) { + if (ActivityCompat.checkSelfPermission(getActivity(), ele) == PackageManager.PERMISSION_DENIED) { + L.INSTANCE.e(ele + "被拒绝"); + return false; + } + L.INSTANCE.e(ele + "已经申请成功"); + } + return true; + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode == 102) { + if (permissionDialog != null) { + permissionDialog.dismiss(); + } + for (int i = 0; i < grantResults.length; i++) { + if (grantResults[i] == PackageManager.PERMISSION_DENIED) { + return; + } + } + showCameraAction(); + } + } + private void setGridViewAutoWidthAndHeight(GridView gridView){ final int width = gridView.getWidth(); final int height = gridView.getHeight(); diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ConnectModel.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ConnectModel.kt index 2da806aca..c13a0a884 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ConnectModel.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ConnectModel.kt @@ -19,6 +19,7 @@ import com.tencent.iot.explorer.link.core.link.listener.BleDeviceConnectionListe import com.tencent.iot.explorer.link.core.link.listener.SmartConfigListener import com.tencent.iot.explorer.link.core.link.listener.SoftAPListener import com.tencent.iot.explorer.link.core.link.service.BleConfigService +import com.tencent.iot.explorer.link.core.link.service.LLSyncErrorCode import com.tencent.iot.explorer.link.core.link.service.SmartConfigService import com.tencent.iot.explorer.link.core.link.service.SoftAPService import com.tencent.iot.explorer.link.core.log.L @@ -27,6 +28,7 @@ import com.tencent.iot.explorer.link.kitlink.util.* import com.tencent.iot.explorer.link.mvp.ParentModel import com.tencent.iot.explorer.link.mvp.view.ConnectView import kotlinx.coroutines.* +import java.util.* /** * 配网进度、绑定设备 @@ -52,6 +54,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba @Volatile var job: Job? = null var bleDevice: BleDevice? = null + var needToCheckBleState = true fun initService(type: Int, context: Context) { this.type = type @@ -91,7 +94,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba // 先关闭当前的扫描 BleConfigService.get().stopScanBluetoothDevices() bleDevice?:let { // 没有解析出内容,直接返回 - bleFailed("param error") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_PARAMS_ERROR_CODE,"param error") return } @@ -113,18 +116,21 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba Thread { job = GlobalScope.launch(Dispatchers.Default) { delay(240000) // 超时 - bleFailed("time out") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_TIMEOUT_ERROR_CODE,"time out") } }.start() } - private fun bleFailed(msg: String?) { + private fun bleFailed(errorCode: String, msg: String?) { launch (Dispatchers.Main){ - job?.cancel() // 结束超时任务 - BleConfigService.get().connetionListener = null - L.e(TAG, msg?:"") - view?.connectFail("bind bluetooth device", msg?:"") - bluetoothGatt?.close() + if (needToCheckBleState) { + job?.cancel() // 结束超时任务 + BleConfigService.get().connetionListener = null + L.e(TAG, msg?:"") + view?.connectFail(errorCode, msg?:"") + bluetoothGatt?.close() + needToCheckBleState = true + } } } @@ -145,7 +151,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba } } override fun onBleDeviceDisconnected(exception: TCLinkException) { - bleFailed(exception.errorMessage) + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_DISCONNECT_ERROR_CODE, exception.errorMessage) } override fun onBleDeviceInfo(bleDeviceInfo: BleDeviceInfo) { L.d(TAG, "onBleDeviceInfo ${JSON.toJSONString(bleDeviceInfo)}") @@ -157,7 +163,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba return@launch // 设置成功则直接退出 } } - bleFailed("request device info failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_MODE_ERROR_CODE,"onBleDeviceInfo set wifi mode failed") } } @@ -166,16 +172,21 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba launch { bluetoothGatt?.let { delay(3000) - if (BleConfigService.get().setMtuSize(it, 512)) return@launch + if (BleConfigService.get().currentConnectBleDevice?.type == 1) { + if (BleConfigService.get().sendUNTX(it)) return@launch + bleFailed(LLSyncErrorCode.PURE_BLE_SET_UNIX_TIMESTAMP_NONCE_ERROR_CODE, "send untx failed") + } else if (BleConfigService.get().currentConnectBleDevice?.type == 0) {//纯蓝牙按照之前的习惯设置一下mtu + if (BleConfigService.get().setMtuSize(it, 512)) return@launch + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_SET_MTU_ERROR_CODE, "config mtu failed") + } } - bleFailed("config mtu failed") } } override fun onBleSetWifiModeResult(success: Boolean) { L.d(TAG, "onBleSetWifiModeResult ${success}") if (!success) { - bleFailed("set wifi mode failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_MODE_RESPONSE_ERROR_CODE, "onBleSetWifiModeResult set wifi mode failed") return } @@ -186,7 +197,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba L.d(TAG, "bleDeviceWifiInfo ${JSON.toJSONString(bleDeviceWifiInfo)}") this@ConnectModel.view?.connectStep(BleConfigStep.STEP_SEND_WIFI_INFO.ordinal) if (BleConfigService.get().sendWifiInfo(it, bleDeviceWifiInfo)) return@launch - bleFailed("send wifi info failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_INFO_ERROR_CODE, "send wifi info failed") } } } @@ -194,7 +205,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba override fun onBleSendWifiInfoResult(success: Boolean) { L.d(TAG, "onBleSendWifiInfoResult ${success}") if (!success) { - bleFailed("send wifi info failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_SET_WIFI_INFO_ERROR_CODE, "send wifi info failed") return } @@ -202,7 +213,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba delay(500) bluetoothGatt?.let { if (BleConfigService.get().requestConnectWifi(it)) return@launch - bleFailed("connect wifi failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_REQUEST_WIFI_CONNECT_ERROR_CODE, "connect wifi failed") } } } @@ -210,7 +221,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba override fun onBleWifiConnectedInfo(wifiConnectInfo: BleWifiConnectInfo) { L.d(TAG, "onBleWifiConnectedInfo ${wifiConnectInfo.connected}") if (!wifiConnectInfo.connected) { - bleFailed("connect wifi failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_WIFI_CONNECT_RESPONSE_ERROR_CODE, "connect wifi failed") return } @@ -219,7 +230,7 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba bluetoothGatt?.let { this@ConnectModel.view?.connectStep(BleConfigStep.STEP_SEND_TOKEN.ordinal) if (BleConfigService.get().configToken(it, App.data.bindDeviceToken)) return@launch - bleFailed("send token failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_REQUEST_BIND_TOKEN_ERROR_CODE, "send token failed") } } } @@ -227,16 +238,17 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba override fun onBlePushTokenResult(success: Boolean) { L.d(TAG, "onBlePushTokenResult ${success}") if (success) { + needToCheckBleState = false checkDeviceBindTokenState() } else { - bleFailed("send wifi info failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_TOKEN_RESPONSE_ERROR_CODE, "send wifi info failed") } } override fun onMtuChanged(mtu: Int, status: Int) { L.d(TAG, "onMtuChanged mtu $mtu status $status") if (BluetoothGatt.GATT_SUCCESS != status) { - bleFailed("config mtu $mtu failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_SET_MTU_RESPONSE_ERROR_CODE, "config mtu $mtu failed") return } @@ -247,10 +259,66 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba return@launch } } - bleFailed("request device info failed") + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_REQUEST_GET_DEVICE_INFO_ERROR_CODE, "request device info failed") } } + override fun onBleBindSignInfo(bleDevBindCondition: BleDevBindCondition) { + this@ConnectModel.view?.connectStep(BleConfigStep.STEP_SEND_TOKEN.ordinal) + BleConfigService.get().currentConnectBleDevice?.let { + var deviceInfo = BleSubDeviceInfo() + deviceInfo.signMethod = "hmacsha1" + deviceInfo.deviceName = bleDevBindCondition.deviceName + deviceInfo.signature = bleDevBindCondition.signInfo + deviceInfo.productId = it.productId + deviceInfo.connId = BleConfigService.get().nonceKeep.toString() + deviceInfo.timestamp = BleConfigService.get().unixTimestemp + 60 + HttpRequest.instance.sigBindDevice(App.data.getCurrentFamily().FamilyId, App.data.getCurrentRoom().RoomId, + deviceInfo, "bluetooth_sign", object : MyCallback { + override fun fail(msg: String?, reqCode: Int) { + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_BLE_DEVICE_NET_ERROR_CODE , msg) + BleConfigService.get().sendBindfailedResult(bluetoothGatt, false) + } + + override fun success(response: BaseResponse, reqCode: Int) { + if (!response.isSuccess()) { + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_BLE_DEVICE_NET_OTHER_ERROR_CODE, response.msg) + BleConfigService.get().sendBindfailedResult(bluetoothGatt, false) + return + } + launch { + bluetoothGatt?.let { + if (BleConfigService.get().sendBindSuccessedResult(it, bleDevBindCondition.deviceName)) { + this@ConnectModel.view?.connectStep(BleConfigStep.STEP_LINK_SUCCESS.ordinal) + view?.connectSuccess() + return@launch + } + } + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_BIND_BLE_DEVICE_RESPONSE_ERROR_CODE, "send bind result failed") + BleConfigService.get().sendBindfailedResult(bluetoothGatt, false) + } + } + }) + return + } + + bleFailed(LLSyncErrorCode.WIFI_CONFIG_BLE_PARAMS_ERROR_CODE, "param error") + } + override fun onBleSendSignInfo(bleDevSignResult: BleDevSignResult) {} + override fun onBleUnbindSignInfo(signInfo: String) {} + override fun onBlePropertyValue(bleDeviceProperty: BleDeviceProperty) {} + override fun onBleControlPropertyResult(result: Int) {} + override fun onBleRequestCurrentProperty() {} + override fun onBleNeedPushProperty(eventId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleReportActionResult(reason: Int, actionId: Int, bleDeviceProperty: BleDeviceProperty) {} + override fun onBleDeviceFirmwareVersion(firmwareVersion: BleDeviceFirmwareVersion) {} + override fun onBleDevOtaUpdateResponse(otaUpdateResponse: BleDevOtaUpdateResponse) {} + override fun onBleDevOtaUpdateResult(success: Boolean, errorCode: Int) {} + + override fun onBleDevOtaReceivedProgressResponse(progress: Int) {} + + override fun onBleDeviceMtuSize(size: Int) {} + override fun onBleDeviceTimeOut(timeLong: Int) {} } } @@ -403,8 +471,10 @@ class ConnectModel(view: ConnectView) : ParentModel(view), MyCallba } } view?.connectSuccess() + needToCheckBleState = true } else { view?.connectFail("bind_fail", response.msg) + needToCheckBleState = true } //绑定操作响应后,不论结果如何,一律停止监听。 Reconnect.instance.stop(connectionListener) diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ControlPanelModel.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ControlPanelModel.kt index 0b69b5506..31851a7ba 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ControlPanelModel.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/ControlPanelModel.kt @@ -3,16 +3,16 @@ package com.tencent.iot.explorer.link.mvp.model import android.os.Build import android.os.Handler import android.text.TextUtils -import android.util.Log import com.alibaba.fastjson.JSON +import com.alibaba.fastjson.JSONArray import com.tencent.iot.explorer.link.App +import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.auth.IoTAuth import com.tencent.iot.explorer.link.core.auth.callback.MyCallback import com.tencent.iot.explorer.link.core.auth.entity.* import com.tencent.iot.explorer.link.core.auth.message.MessageConst import com.tencent.iot.explorer.link.core.auth.message.MessageConst.TRTC_AUDIO_CALL_STATUS import com.tencent.iot.explorer.link.core.auth.message.MessageConst.TRTC_VIDEO_CALL_STATUS -import com.tencent.iot.explorer.link.core.auth.message.MessageConst.USERID import com.tencent.iot.explorer.link.core.auth.message.payload.Payload import com.tencent.iot.explorer.link.core.auth.message.upload.ArrayString import com.tencent.iot.explorer.link.core.auth.response.BaseResponse @@ -21,6 +21,8 @@ import com.tencent.iot.explorer.link.core.auth.response.DeviceDataResponse import com.tencent.iot.explorer.link.core.auth.response.DeviceProductResponse import com.tencent.iot.explorer.link.core.auth.socket.callback.ActivePushCallback import com.tencent.iot.explorer.link.core.auth.util.JsonManager +import com.tencent.iot.explorer.link.core.link.service.BleConfigService +import com.tencent.iot.explorer.link.core.link.service.LLSyncTLVEntity import com.tencent.iot.explorer.link.core.log.L import com.tencent.iot.explorer.link.core.utils.SharePreferenceUtil import com.tencent.iot.explorer.link.kitlink.consts.CommonField @@ -28,12 +30,14 @@ import com.tencent.iot.explorer.link.kitlink.entity.DevicePropertyEntity import com.tencent.iot.explorer.link.kitlink.response.UserSettingResponse import com.tencent.iot.explorer.link.kitlink.util.HttpRequest import com.tencent.iot.explorer.link.kitlink.util.RequestCode +import com.tencent.iot.explorer.link.kitlink.util.VideoUtils import com.tencent.iot.explorer.link.mvp.ParentModel import com.tencent.iot.explorer.link.mvp.view.ControlPanelView import com.tencent.iot.explorer.link.rtc.model.TRTCUIManager +import com.tencent.iot.video.link.entity.DeviceStatus +import com.tencent.xnet.XP2P +import com.tencent.xnet.XP2PCallback import java.util.* -import kotlin.collections.ArrayList -import kotlin.collections.LinkedHashSet /** @@ -44,14 +48,21 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( init { IoTAuth.addActivePushCallback(this) + startReconnectCycle() } + var categoryId = 0 + var netType = "" var productId = "" var deviceName = "" var deviceId = "" private var hasPanel = false private var hasProduct = false + private var firstTime = true + private var isP2PConnect = false //p2p是否连接通(包含请求获取设备状态信令成功才算连接通) + var otherNeedP2PConnect = false //其他类型需要重连p2p + private var reconnectCycleTask: TimerTask? = null //面板UI列表 private val uiList = ArrayList() @@ -71,6 +82,8 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( //是否显示云端定时 private var hasTimerCloud = false + private var xp2pInfo = "" + /** * 断网后重新连上服务器 */ @@ -84,8 +97,24 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( override fun success(payload: Payload) { L.e("Payoad", payload.data) payload.keySet()?.forEachIndexed { _, id -> + propertyList.forEachIndexed { index, it -> + var jsonObject = JSON.parseObject(payload.payload) + val method = jsonObject.getString(MessageConst.METHOD) + if (method.equals(MessageConst.CONTROL) && netType.equals("ble") && id == it.id) { //ble收到控制指令,向ble设备发送 二进制远程控制指令 + var byteArr = LLSyncTLVEntity().getControlBleDevicebyteArr(JSON.parseObject(it.define).getString("type"), index, payload.getValue(id), it.define) + BleConfigService.get().bluetoothGatt?.let { + BleConfigService.get().controlBleDevice(it, byteArr) + } + return@forEachIndexed + } + } run set@{ devicePropertyList.forEach { + var jsonObject = JSON.parseObject(payload.payload) + val method = jsonObject.getString(MessageConst.METHOD) + if (method.equals(MessageConst.CONTROL)) { + return@set //control消息不在控制面板上变化。 + } if (id == it.id) { it.setValue(payload.getValue(id)) view?.showControlPanel(navBar, hasTimerCloud) @@ -126,6 +155,11 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( } } + fun removeReconnectCycleTasktask() { + reconnectCycleTask?.cancel() + reconnectCycleTask = null + } + /** * 当前产品控制面板风格主题 */ @@ -162,8 +196,8 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( if (value == "1") { //并且状态值为1,代表应用正在call设备 TRTCUIManager.getInstance().callingDeviceId = "$productId/$deviceName" //保存下设备id(productId/deviceName) val userId = SharePreferenceUtil.getString(App.activity, App.CONFIG, CommonField.USER_ID) - val agent = "android/1.4.0 (Android" + Build.VERSION.SDK_INT + ";" + Build.BRAND + " " + Build.MODEL + ";" + Locale.getDefault().language + "-" + Locale.getDefault().country + ")" - data = "{\"$id\":$value, \"$USERID\":\"$userId\", \"${MessageConst.AGENT}\":\"$agent\"}" + val agent = "android/1.4.0 (Android" + Build.VERSION.SDK_INT + ";" + App.data.getMobileBrand() + " " + App.data.getMobileModel() + ";" + Locale.getDefault().language + "-" + Locale.getDefault().country + ")" + data = "{\"$id\":$value, \"${MessageConst.AGENT}\":\"$agent\", \"${MessageConst.TRTC_CALLEDID}\":\"$deviceId\", \"${MessageConst.TRTC_CALLERID}\":\"$userId\"}" } } @@ -233,6 +267,35 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( private fun parseDeviceData(response: BaseResponse) { if (!response.isSuccess()) return response.parse(DeviceDataResponse::class.java)?.run { + if (categoryId == 567) { + val deviceDatas = parseList() + L.e("deviceDatas", JsonManager.toJson(deviceDatas)) + deviceDatas.forEach { + if (it.id == "_sys_xp2p_info") { + val xp2pInfo = it.value + this@ControlPanelModel.xp2pInfo = xp2pInfo + L.e("ControlPanel","firstTime: ${firstTime}, isP2PConnect: ${isP2PConnect}") + if (firstTime) { + XP2P.setCallback(xp2pCallback) + XP2P.startService(deviceId, productId, deviceName) + XP2P.setParamsForXp2pInfo(deviceId, "", "", xp2pInfo) + firstTime = false + } else if (!isP2PConnect) {//p2p链路断开 或者 p2p未断开但给设备发送信令失败 + XP2P.stopService(deviceId) + XP2P.setCallback(xp2pCallback) + XP2P.startService(deviceId, productId, deviceName) + XP2P.setParamsForXp2pInfo(deviceId, "", "", xp2pInfo) + } else if (otherNeedP2PConnect) { + otherNeedP2PConnect = false + XP2P.stopService(deviceId) + XP2P.setCallback(xp2pCallback) + XP2P.startService(deviceId, productId, deviceName) + XP2P.setParamsForXp2pInfo(deviceId, "", "", xp2pInfo) + } + + } + } + } deviceDataList.clear() deviceDataList.addAll(parseList()) L.e("deviceDataList", JsonManager.toJson(deviceDataList)) @@ -251,6 +314,145 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( } } + val xp2pCallback = object : XP2PCallback { + override fun fail(msg: String?, errorCode: Int) {} + + override fun commandRequest(id: String?, msg: String?) {} + + override fun xp2pEventNotify(id: String?, msg: String?, event: Int) { + //1003 p2p链路断开 + //1004 p2p链路初始化成功 + //1005 p2p链路初始化失败 + when (event) { + 1003 -> { + App.activity?.runOnUiThread { + VideoUtils.sendVideoBroadcast(App.activity, 2) +// T.show("1003p2p链路断开,尝试重连") + L.e("=========p2p链路断开,尝试重连") + requestXp2pInfo() + startReconnectCycle() + isP2PConnect = false + } + } + 1004 -> { + App.activity?.runOnUiThread { +// T.show("1004p2p链路初始化成功") + L.e("=========p2p链路初始化成功") + getDeviceStatus() + } + } + 1005 -> { + App.activity?.runOnUiThread { +// T.show("1005p2p链路初始化失败") + L.e("=========p2p链路初始化失败") + isP2PConnect = false + } + } + } + } + + override fun avDataRecvHandle(id: String?, data: ByteArray?, len: Int) {} + + override fun avDataCloseHandle(id: String?, msg: String?, errorCode: Int) {} + + override fun onDeviceMsgArrived(id: String?, data: ByteArray?, len: Int): String { + return "" + } + } + + private fun startReconnectCycle() { + L.e("=========startReconnectCycle: $isP2PConnect") + if (reconnectCycleTask == null) { + L.e("=========reconnectCycleTask: $isP2PConnect") + var reconnectTimer = Timer("p2p connect cycle") + reconnectCycleTask = object :TimerTask() { + override fun run() { + L.e("=========isP2PConnect: $isP2PConnect") + if (!isP2PConnect) { + // 请求最新Xp2pInfo去初始化p2p + requestXp2pInfo() + } else { + removeReconnectCycleTasktask() + } + } + } + reconnectTimer.schedule(reconnectCycleTask, 5000, 5000) + } + } + + private fun getDeviceStatus(): Boolean { + val command = + "action=inner_define&channel=0&cmd=get_device_st&type=live&quality=standard".toByteArray() + val repStatus = XP2P.postCommandRequestSync( + deviceId, + command, command.size.toLong(), (2 * 1000 * 1000).toLong() + ) + val deviceStatuses = JSONArray.parseArray( + repStatus, + DeviceStatus::class.java + ) + // 0 接收请求 + // 1 拒绝请求 + // 404 error request message + // 405 connect number too many + // 406 current command don't support + // 407 device process error + if (deviceStatuses != null && deviceStatuses.size > 0) { + when (deviceStatuses[0].status) { + 0 -> { +// T.show("设备状态正常") + VideoUtils.sendVideoBroadcast(App.activity, 1) + isP2PConnect = true + return true + } + 1 -> { +// T.show("设备状态异常, 拒绝请求: $repStatus") + return false + } + 404 -> { +// T.show("设备状态异常, error request message: $repStatus") + return false + } + 405 -> { +// T.show("设备状态异常, connect number too many: $repStatus") + return false + } + 406 -> { +// T.show("设备状态异常, current command don't support: $repStatus") + return false + } + 407 -> { +// T.show("设备状态异常, device process error: $repStatus") + return false + } + } + } else { +// T.show("获取设备状态失败") + return false + } + return false + } + + fun getBleDeviceStatus(): ByteArray? { + var totalSpecsByteArr = ByteArray(0) + devicePropertyList.forEachIndexed { _, devicePropertyEntity -> + if (devicePropertyEntity.getValue() != "") { + var tempSpecs = LLSyncTLVEntity().getControlBleDevicebyteArr(devicePropertyEntity.valueType, devicePropertyEntity.index, devicePropertyEntity.getValue(), "") + var lastTotalSpecsByteArr = totalSpecsByteArr.copyOf() + totalSpecsByteArr = ByteArray(lastTotalSpecsByteArr.size +tempSpecs.size) + System.arraycopy(lastTotalSpecsByteArr, 0, totalSpecsByteArr, 0, lastTotalSpecsByteArr.size) + System.arraycopy(tempSpecs, 0, totalSpecsByteArr, lastTotalSpecsByteArr.size, tempSpecs.size) + } + } +// var byteArr = ByteArray(4+totalSpecsByteArr.size) +// byteArr[0] = 0x22.toByte() +// byteArr[1] = 0x00.toByte() //Reply_Result 0x00成功 0x01失败 0x02数据解析错误 +// var lengthByteArr = BleConfigService.get().number2Bytes(totalSpecsByteArr.size.toLong(), 2) +// System.arraycopy(lengthByteArr, 0, byteArr, 2, lengthByteArr.size) +// System.arraycopy(totalSpecsByteArr, 0, byteArr, 2+lengthByteArr.size, totalSpecsByteArr.size) + return totalSpecsByteArr + } + /** * 解析面板数据 */ @@ -288,7 +490,12 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( hasProduct = true mergeData() } - + netType = Products[0].NetType + categoryId = Products[0].CategoryId + if (categoryId == 567) { //双向音视频类别 + view?.refreshCategrayId(categoryId) + requestXp2pInfo() + } if (uiList.size == 0) { processPropertyList() mergeData() @@ -297,6 +504,10 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( } } + private fun requestXp2pInfo() { + HttpRequest.instance.deviceData(productId, deviceName, this) + } + private fun processPropertyList() { uiList.clear() var firstProperty = true @@ -359,8 +570,9 @@ class ControlPanelModel(view: ControlPanelView) : ParentModel( } private fun completeProperty(entity: DevicePropertyEntity) { - propertyList.forEach { + propertyList.forEachIndexed { index, it -> if (it.id == entity.id) { + entity.index = index entity.name = it.name entity.desc = it.desc entity.mode = it.mode diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/GetCodeModel.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/GetCodeModel.kt index 23ffc04b8..61d6cf35f 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/GetCodeModel.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/GetCodeModel.kt @@ -160,7 +160,7 @@ class GetCodeModel(view: GetCodeView) : ParentModel(view), MyCallba } override fun onDestroy() { - handler?.removeCallbacks(runnable) + runnable?.let { handler?.removeCallbacks(it) } handler = null runnable = null super.onDestroy() diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/HomeFragmentModel.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/HomeFragmentModel.kt index 9a044fcb0..5e9dae509 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/HomeFragmentModel.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/HomeFragmentModel.kt @@ -119,13 +119,17 @@ class HomeFragmentModel(view: HomeFragmentView) : ParentModel( if (!TextUtils.isEmpty(productEntity.Name) && !TextUtils.isEmpty(productEntity.ProductId)) { for (devEls in deviceList) { if (devEls.ProductId == productEntity.ProductId) { - devEls.AliasName = productEntity.Name + if (devEls.AliasName.isEmpty()) { + devEls.AliasName = productEntity.Name + } + devEls.CategoryId = productEntity.CategoryId } } for (devEls in shareDeviceList) { if (devEls.ProductId == productEntity.ProductId) { devEls.AliasName = productEntity.Name + devEls.CategoryId = productEntity.CategoryId } } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/UserInfoModel.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/UserInfoModel.kt index 3e53792f8..86728a02b 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/UserInfoModel.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/model/UserInfoModel.kt @@ -11,8 +11,11 @@ import com.tencent.iot.explorer.link.mvp.ParentModel import com.tencent.iot.explorer.link.mvp.view.UploadView import com.tencent.iot.explorer.link.mvp.view.UserInfoView import com.tencent.cos.xml.exception.CosXmlClientException +import com.tencent.iot.explorer.link.T import com.tencent.iot.explorer.link.core.auth.response.BaseResponse import com.tencent.iot.explorer.link.core.log.L +import com.tencent.iot.explorer.link.core.utils.Utils +import com.tencent.iot.explorer.link.kitlink.consts.CommonField import com.tencent.iot.explorer.link.kitlink.response.UserSettingResponse @@ -36,6 +39,9 @@ class UserInfoModel(view: UserInfoView) : ParentModel(view), Uploa RequestCode.logout -> { view?.logout() App.data.setAppUser(null) + Utils.setXmlStringValue( + T.getContext(), CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO, + CommonField.CHINA_MAINLAND_USER_REG_TIME_INFO, "{}") } RequestCode.update_user_info -> { App.data.userInfo.Avatar = avatar diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ConnectPresenter.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ConnectPresenter.kt index fbfdda765..bc6143bf1 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ConnectPresenter.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ConnectPresenter.kt @@ -38,7 +38,7 @@ class ConnectPresenter(view: ConnectView) : when(type) { ConfigType.SmartConfig.id -> startSmartConnect() ConfigType.SoftAp.id -> startSoftAppConnect() - ConfigType.BleConfig.id -> startBleConfigNet() + ConfigType.BleConfig.id, ConfigType.BleBindConfig.id -> startBleConfigNet() } } } diff --git a/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ControlPanelPresenter.kt b/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ControlPanelPresenter.kt index e53fd93ce..546172df2 100644 --- a/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ControlPanelPresenter.kt +++ b/app/src/main/java/com/tencent/iot/explorer/link/mvp/presenter/ControlPanelPresenter.kt @@ -13,18 +13,39 @@ class ControlPanelPresenter(view: ControlPanelView) : ParentPresenter diff --git a/app/src/main/res/drawable/selected.png b/app/src/main/res/drawable/selected.png new file mode 100644 index 000000000..c3f1a0bff Binary files /dev/null and b/app/src/main/res/drawable/selected.png differ diff --git a/app/src/main/res/drawable/unselect.png b/app/src/main/res/drawable/unselect.png new file mode 100644 index 000000000..bca701d6e Binary files /dev/null and b/app/src/main/res/drawable/unselect.png differ diff --git a/app/src/main/res/layout/activity_about_us.xml b/app/src/main/res/layout/activity_about_us.xml index b66f2213e..4634fa874 100644 --- a/app/src/main/res/layout/activity_about_us.xml +++ b/app/src/main/res/layout/activity_about_us.xml @@ -138,6 +138,74 @@ app:layout_constraintTop_toBottomOf="@+id/tv_title_user_agreement" android:background="@color/gray_E7E8EB" /> + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/tv_title_collected_personal_information" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_choose_country.xml b/app/src/main/res/layout/activity_choose_country.xml index 2e1219f1b..f5fe5ed07 100644 --- a/app/src/main/res/layout/activity_choose_country.xml +++ b/app/src/main/res/layout/activity_choose_country.xml @@ -54,7 +54,7 @@ android:layout_width="0dp" android:layout_height="48dp" android:layout_marginStart="24dp" - android:text="@string/default_country" + android:text="@string/please_choose" android:gravity="center_vertical" android:textColor="@color/gray_6C7078" android:textSize="@dimen/ts_14" diff --git a/app/src/main/res/layout/activity_control_panel.xml b/app/src/main/res/layout/activity_control_panel.xml index a2f72fdc6..dc69f48b4 100644 --- a/app/src/main/res/layout/activity_control_panel.xml +++ b/app/src/main/res/layout/activity_control_panel.xml @@ -8,14 +8,6 @@ android:background="@color/white" tools:context=".kitlink.activity.ControlPanelActivity"> - - + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_forgot_password.xml b/app/src/main/res/layout/activity_forgot_password.xml index a3d65d839..769cf5158 100644 --- a/app/src/main/res/layout/activity_forgot_password.xml +++ b/app/src/main/res/layout/activity_forgot_password.xml @@ -89,6 +89,6 @@ android:text="@string/register_get_code" android:textColor="@color/white" android:textSize="@dimen/ts_16" - app:layout_constraintTop_toBottomOf="@+id/tv_register_tip_click" /> + app:layout_constraintTop_toBottomOf="@+id/vp_forgot" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login2.xml b/app/src/main/res/layout/activity_login2.xml index 080465720..aa06e664d 100644 --- a/app/src/main/res/layout/activity_login2.xml +++ b/app/src/main/res/layout/activity_login2.xml @@ -25,6 +25,69 @@ android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@+id/back_btn_login"/> + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/ll_register_agreement"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_param_setting.xml b/app/src/main/res/layout/activity_param_setting.xml new file mode 100644 index 000000000..7c4514c4e --- /dev/null +++ b/app/src/main/res/layout/activity_param_setting.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + +