init
10
README.md
@ -1,2 +1,12 @@
|
||||
# cordova-plugin-trtc
|
||||
|
||||
腾讯实时音视频(TRTC)封装cordova插件 (示例)
|
||||
|
||||
具体原生项目参考tencent的demo, 源码大部分从这里搬运:
|
||||
|
||||
[https://github.com/zhaoyang21cn/Android_TRTC](https://github.com/zhaoyang21cn/Android_TRTC)
|
||||
|
||||
|
||||
[https://github.com/zhaoyang21cn/iOS_TRTC](https://github.com/zhaoyang21cn/iOS_TRTC)
|
||||
|
||||
iOS的sdk需要自己添加到src/ios/libs下, 可以从官网获取
|
||||
|
18
package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "cordova-plugin-trtc",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"cordova": {
|
||||
"id": "cordova-plugin-trtc",
|
||||
"platforms": [
|
||||
"android",
|
||||
"ios"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"ecosystem:cordova",
|
||||
"cordova-android"
|
||||
],
|
||||
"author": "",
|
||||
"license": "ISC"
|
||||
}
|
291
plugin.xml
Normal file
@ -0,0 +1,291 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
id="cordova-plugin-trtc"
|
||||
version="1.0.0">
|
||||
<name>Trtc</name>
|
||||
<description>TRTC Plugin</description>
|
||||
<keywords>cordova,TRTC,live</keywords>
|
||||
<license>MIT License</license>
|
||||
|
||||
<dependency id="cordova-plugin-androidx"/>
|
||||
<dependency id="cordova-plugin-androidx-adapter"/>
|
||||
|
||||
<preference name="APP_ID" default="sdk_app_id"/>
|
||||
<preference name="PERMISSION_MIC_DESC" default="应用需要访问您的麦克风"/>
|
||||
<preference name="PERMISSION_CAMERA_DESC" default="应用需要访问您的摄像头"/>
|
||||
|
||||
<engines>
|
||||
<engine name="cordova-android" version=">=4.0.0"/>
|
||||
</engines>
|
||||
|
||||
<js-module src="www/trtc.js" name="trtc">
|
||||
<clobbers target="cordova.plugin.trtc"/>
|
||||
</js-module>
|
||||
|
||||
<platform name="ios">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="Trtc">
|
||||
<param name="ios-package" value="TrtcPlugin"/>
|
||||
<!-- <param name="onload" value="true" /> -->
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<config-file target="*-Info.plist" parent="NSMicrophoneUsageDescription">
|
||||
<string>$PERMISSION_MIC_DESC</string>
|
||||
</config-file>
|
||||
<config-file target="*-Info.plist" parent="NSCameraUsageDescription">
|
||||
<string>$PERMISSION_CAMERA_DESC</string>
|
||||
</config-file>
|
||||
|
||||
<header-file src="src/ios/TrtcPlugin.h"/>
|
||||
<source-file src="src/ios/TrtcPlugin.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveConfigDefine.h"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveJoinRoomViewController.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveJoinRoomViewController.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRequestManager.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRequestManager.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.m"/>
|
||||
<header-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.h"/>
|
||||
<source-file src="src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.m"/>
|
||||
|
||||
<resource-file src="src/ios/TrtcConfig.plist"/>
|
||||
|
||||
<!-- images -->
|
||||
<resource-file src="src/ios/res/beauty-dis.png"/>
|
||||
<resource-file src="src/ios/res/beauty.png"/>
|
||||
<resource-file src="src/ios/res/bg.png"/>
|
||||
<resource-file src="src/ios/res/camera-gray.png"/>
|
||||
<resource-file src="src/ios/res/camera.png"/>
|
||||
<resource-file src="src/ios/res/chat.png"/>
|
||||
<resource-file src="src/ios/res/doubleroom.png"/>
|
||||
<resource-file src="src/ios/res/feedback.png"/>
|
||||
<resource-file src="src/ios/res/ic_toast_success@2x.png"/>
|
||||
<resource-file src="src/ios/res/icon_sign@2x.png"/>
|
||||
<resource-file src="src/ios/res/log.png"/>
|
||||
<resource-file src="src/ios/res/log2.png"/>
|
||||
<resource-file src="src/ios/res/mic-dis.png"/>
|
||||
<resource-file src="src/ios/res/mic.png"/>
|
||||
<resource-file src="src/ios/res/muti_room_bg.png"/>
|
||||
<resource-file src="src/ios/res/role.png"/>
|
||||
<resource-file src="src/ios/res/ui_title_arrow_left.png"/>
|
||||
|
||||
<!-- lib -->
|
||||
<framework src="Accelerate.framework"/>
|
||||
<framework src="AssetsLibrary.framework"/>
|
||||
<framework src="AVFoundation.framework"/>
|
||||
<framework src="CoreGraphics.framework"/>
|
||||
<framework src="CoreMedia.framework"/>
|
||||
<framework src="CoreTelephony.framework"/>
|
||||
<framework src="CoreVideo.framework"/>
|
||||
<framework src="ImageIO.framework"/>
|
||||
<framework src="JavaScriptCore.framework"/>
|
||||
<framework src="OpenAL.framework"/>
|
||||
<framework src="OpenGLES.framework"/>
|
||||
<framework src="QuartzCore.framework"/>
|
||||
<framework src="SystemConfiguration.framework"/>
|
||||
<framework src="VideoToolbox.framework"/>
|
||||
<framework src="libbz2.tbd"/>
|
||||
<framework src="libc++.tbd"/>
|
||||
<framework src="libiconv.tbd"/>
|
||||
<framework src="libicucore.tbd"/>
|
||||
<framework src="libprotobuf.tbd"/>
|
||||
<framework src="libresolv.tbd"/>
|
||||
<framework src="libsqlite3.tbd"/>
|
||||
<framework src="libstdc++.6.tbd"/>
|
||||
<framework src="libstdc++.tbd"/>
|
||||
<framework src="libz.tbd"/>
|
||||
|
||||
<!-- sdk -->
|
||||
<framework src="src/ios/libs/AVSDK/QAVSDK.framework" custom="true"/>
|
||||
|
||||
<framework src="src/ios/libs/IMSDK/IMCore.framework" custom="true"/>
|
||||
<framework src="src/ios/libs/IMSDK/ImSDK.framework" custom="true"/>
|
||||
<framework src="src/ios/libs/IMSDK/IMSDKBugly.framework" custom="true"/>
|
||||
<framework src="src/ios/libs/IMSDK/QALSDK.framework" custom="true"/>
|
||||
<framework src="src/ios/libs/IMSDK/TLSSDK.framework" custom="true"/>
|
||||
|
||||
<framework src="src/ios/libs/ILiveSDK/ILiveSDK.framework" custom="true"/>
|
||||
<framework src="src/ios/libs/ILiveSDK/ILiveLogReport.framework" custom="true"/>
|
||||
<framework src="src/ios/libs/ILiveSDK/TILLiveSDK.framework" custom="true"/>
|
||||
|
||||
<framework src="src/ios/libs/BeautySDK/Pitu/GPUImage.framework" custom="true"/>
|
||||
<source-file src="src/ios/libs/BeautySDK/Pitu/libcrypto.a" framework="true"/>
|
||||
<source-file src="src/ios/libs/BeautySDK/Pitu/libssl.a" framework="true"/>
|
||||
<header-file src="src/ios/libs/BeautySDK/include/TXCRenderView.h"/>
|
||||
<header-file src="src/ios/libs/BeautySDK/include/TXCVideoPreprocessor.h"/>
|
||||
<header-file src="src/ios/libs/BeautySDK/include/TXEVideoPreprocessorDef.h"/>
|
||||
<header-file src="src/ios/libs/BeautySDK/include/TXEVideoTypeDef.h"/>
|
||||
<header-file src="src/ios/libs/BeautySDK/include/TXINotifyDelegate.h"/>
|
||||
<header-file src="src/ios/libs/BeautySDK/include/TXIVideoPreprocessorDelegate.h"/>
|
||||
<source-file src="src/ios/libs/BeautySDK/libTXMRenderer.a" framework="true"/>
|
||||
<source-file src="src/ios/libs/BeautySDK/libTXMVideoPreprocessor.a" framework="true"/>
|
||||
<source-file src="src/ios/libs/BeautySDK/libTXMBasic.a" framework="true"/>
|
||||
|
||||
<config-file target="*TrtcConfig.plist" parent="AppId">
|
||||
<string>$APP_ID</string>
|
||||
</config-file>
|
||||
</platform>
|
||||
|
||||
<platform name="android">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
<feature name="Trtc">
|
||||
<param name="android-package" value="com.chuwa.cordova.trtc.Trtc"/>
|
||||
<!-- <param name="onload" value="true" /> -->
|
||||
</feature>
|
||||
</config-file>
|
||||
|
||||
<config-file target="AndroidManifest.xml" parent="/*">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<!-- <uses-feature android:name="android.hardware.camera" />-->
|
||||
<!-- <uses-feature android:name="android.hardware.camera.autofocus" />-->
|
||||
</config-file>
|
||||
|
||||
<config-file target="res/values/strings.xml" parent="/resources">
|
||||
<string name="trtc_app_id">$APP_ID</string>
|
||||
<string name="str_enter_tips">腾讯视频通话</string>
|
||||
<string name="str_room_title">房间名</string>
|
||||
<string name="str_room_id">房间ID</string>
|
||||
<string name="str_create_btn">创建房间</string>
|
||||
<string name="str_switch_camera">翻转</string>
|
||||
<string name="str_chat_tab">聊天</string>
|
||||
<string name="str_wb_tab">白板</string>
|
||||
<string name="msg_title">消息</string>
|
||||
<string name="str_input_tips">输入文字内容</string>
|
||||
<string name="str_beauty">美颜</string>
|
||||
<string name="str_log">日志</string>
|
||||
<string name="str_voice">声音</string>
|
||||
<string name="str_room_id_tips">输入房间号</string>
|
||||
<string name="str_enter_room_btn">进入房间</string>
|
||||
<string name="str_role">配置</string>
|
||||
<string name="str_feedback">反馈</string>
|
||||
<string name="str_set_role">请选择要配置的分辨率</string>
|
||||
<string name="str_chat">聊天</string>
|
||||
<string name="str_set_problem">请选择反馈类型</string>
|
||||
<string name="str_problem_other">请输入具体问题描述</string>
|
||||
<string name="str_feedback_ret">非常感谢您的反馈</string>
|
||||
<string name="str_login_success">登录成功</string>
|
||||
</config-file>
|
||||
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest/application">
|
||||
<activity android:name="com.chuwa.cordova.trtc.CreateActivity" android:launchMode="singleTask"
|
||||
android:screenOrientation="portrait">
|
||||
</activity>
|
||||
<activity android:name="com.chuwa.cordova.trtc.RoomActivity" android:windowSoftInputMode="adjustPan"
|
||||
android:launchMode="singleInstance" android:screenOrientation="portrait">
|
||||
</activity>
|
||||
<activity android:name="android.java.com.example.basic.TRTCBaseActivity" android:windowSoftInputMode="adjustPan"
|
||||
android:launchMode="singleInstance" android:screenOrientation="portrait">
|
||||
</activity>
|
||||
<activity android:name="com.tencent.trtc.videocall.VideoCallingActivity" android:windowSoftInputMode="adjustPan"
|
||||
android:launchMode="singleInstance" android:screenOrientation="portrait" android:theme="@style/AppTheme">
|
||||
</activity>
|
||||
<activity android:name="com.tencent.trtc.videocall.VideoCallingEnterActivity" android:windowSoftInputMode="adjustPan"
|
||||
android:launchMode="singleInstance" android:screenOrientation="portrait" android:theme="@style/AppTheme">
|
||||
</activity>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/Trtc.java" target-dir="src/com/chuwa/cordova/trtc"/>
|
||||
|
||||
<source-file src="src/android/java/com/example/basic/TRTCBaseActivity.java"
|
||||
target-dir="src/android/java/com/example/basic"/>
|
||||
|
||||
<source-file src="src/android/java/com/tencent/trtc/videocall/BuildConfig.java"
|
||||
target-dir="src/com/tencent/trtc/videocall"/>
|
||||
<source-file src="src/android/java/com/tencent/trtc/videocall/FloatingView.java"
|
||||
target-dir="src/com/tencent/trtc/videocall"/>
|
||||
<source-file src="src/android/java/com/tencent/trtc/videocall/VideoCallingActivity.java"
|
||||
target-dir="src/com/tencent/trtc/videocall"/>
|
||||
<source-file src="src/android/java/com/tencent/trtc/videocall/VideoCallingEnterActivity.java"
|
||||
target-dir="src/com/tencent/trtc/videocall"/>
|
||||
|
||||
<source-file src="src/android/java/com/tencent/trtc/debug/Constant.java"
|
||||
target-dir="src/com/tencent/trtc/debug"/>
|
||||
|
||||
<resource-file src="src/android/res/drawable/videocall_background.xml"
|
||||
target="res/drawable/videocall_background.xml"/>
|
||||
<resource-file src="src/android/res/drawable/common_button_bg.xml"
|
||||
target="res/drawable/common_button_bg.xml"/>
|
||||
<resource-file src="src/android/res/drawable/common_edit_bg.xml"
|
||||
target="res/drawable/common_edit_bg.xml"/>
|
||||
<resource-file src="src/android/res/drawable/common_seekbar_style.xml"
|
||||
target="res/drawable/common_seekbar_style.xml"/>
|
||||
<resource-file src="src/android/res/drawable/common_seekbar_thumb.xml"
|
||||
target="res/drawable/common_seekbar_thumb.xml"/>
|
||||
<resource-file src="src/android/res/drawable/common_selector_radio_bg.xml"
|
||||
target="res/drawable/common_selector_radio_bg.xml"/>
|
||||
|
||||
<resource-file src="src/android/res/layout/videocall_activit_enter.xml"
|
||||
target="res/layout/videocall_activit_enter.xml"/>
|
||||
<resource-file src="src/android/res/layout/videocall_activity_calling.xml"
|
||||
target="res/layout/videocall_activity_calling.xml"/>
|
||||
<resource-file src="src/android/res/layout/videocall_popup_layout.xml"
|
||||
target="res/layout/videocall_popup_layout.xml"/>
|
||||
<resource-file src="src/android/res/layout/videocall_view_floating_default.xml"
|
||||
target="res/layout/videocall_view_floating_default.xml"/>
|
||||
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/videocall_float_logo.png"
|
||||
target="res/mipmap-xxhdpi/videocall_float_logo.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/videocall_home.png"
|
||||
target="res/mipmap-xxhdpi/videocall_home.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/common_ic_back.png"
|
||||
target="res/mipmap-xxhdpi/common_ic_back.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/common_user_portrait.png"
|
||||
target="res/mipmap-xxhdpi/common_user_portrait.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/earpiece.png"
|
||||
target="res/mipmap-xxhdpi/earpiece.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/mac_off.png"
|
||||
target="res/mipmap-xxhdpi/mac_off.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/mac_on.png"
|
||||
target="res/mipmap-xxhdpi/mac_on.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/speaker.png"
|
||||
target="res/mipmap-xxhdpi/speaker.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/swap_camera.png"
|
||||
target="res/mipmap-xxhdpi/swap_camera.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/view_close.png"
|
||||
target="res/mipmap-xxhdpi/view_close.png"/>
|
||||
<resource-file src="src/android/res/mipmap-xxhdpi/view_open.png"
|
||||
target="res/mipmap-xxhdpi/view_open.png"/>
|
||||
|
||||
<resource-file src="src/android/res/values/colors.xml"
|
||||
target="res/values/colors.xml"/>
|
||||
<resource-file src="src/android/res/values/strings.xml"
|
||||
target="res/values/strings.xml"/>
|
||||
<resource-file src="src/android/res/values/styles.xml"
|
||||
target="res/values/styles.xml"/>
|
||||
|
||||
<resource-file src="src/android/res/values-en/strings.xml"
|
||||
target="res/values-en/strings.xml"/>
|
||||
<resource-file src="src/android/res/values-en/styles.xml"
|
||||
target="res/values-en/styles.xml"/>
|
||||
|
||||
<!-- <lib-file src="src/android/libs/lib.jar" /> -->
|
||||
|
||||
<framework src="com.tencent.liteav:LiteAVSDK_TRTC:latest.release"/>
|
||||
<framework src="androidx.appcompat:appcompat:1.1.0"/>
|
||||
<framework src="androidx.constraintlayout:constraintlayout:1.1.3"/>
|
||||
<framework src="com.android.support:multidex:1.0.1"/>
|
||||
<framework src="com.tencent.ilivesdk:ilivesdk:1.9.3"/>
|
||||
<framework src="com.tencent.ilivefilter:liteav_normal:1.1.21"/>
|
||||
<framework src="com.google.android.material:material:1.3.0"/>
|
||||
</platform>
|
||||
|
||||
</plugin>
|
55
src/android/Trtc.java
Normal file
@ -0,0 +1,55 @@
|
||||
package com.chuwa.cordova.trtc;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.tencent.trtc.videocall.VideoCallingEnterActivity;
|
||||
import com.tencent.trtc.videocall.VideoCallingActivity;
|
||||
|
||||
import org.apache.cordova.CordovaInterface;
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
public class Trtc extends CordovaPlugin {
|
||||
|
||||
static final String ACTION_SHOW_CREATE_PAGE = "showCreatePage"; // for test
|
||||
static final String ACTION_JOIN_CHANNEL = "joinChannel";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
Activity activity = this.cordova.getActivity();
|
||||
|
||||
// if (ACTION_SHOW_CREATE_PAGE.equals(action)) {
|
||||
// Intent intent = new Intent(activity, VideoCallingEnterActivity.class);
|
||||
// cordova.startActivityForResult(this, intent, 666);
|
||||
// } else
|
||||
if (ACTION_JOIN_CHANNEL.equals(action)) {
|
||||
Intent intent = new Intent(activity, VideoCallingActivity.class);
|
||||
JSONObject arg = args.getJSONObject(0);
|
||||
String ROOM_ID = arg.getString("ROOM_ID");
|
||||
String USER_ID = arg.getString("USER_ID");
|
||||
int SDK_APP_ID = arg.getInt("SDK_APP_ID");
|
||||
String USER_SIG = arg.getString("USER_SIG");
|
||||
intent.putExtra("room_id", ROOM_ID);
|
||||
intent.putExtra("user_id", USER_ID);
|
||||
intent.putExtra("sdk_app_id", SDK_APP_ID);
|
||||
intent.putExtra("user_sig", USER_SIG);
|
||||
cordova.startActivityForResult(this, intent, 666);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
}
|
||||
|
||||
|
||||
}
|
71
src/android/java/com/example/basic/TRTCBaseActivity.java
Normal file
@ -0,0 +1,71 @@
|
||||
package android.java.com.example.basic;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class TRTCBaseActivity extends AppCompatActivity {
|
||||
|
||||
protected static final int REQ_PERMISSION_CODE = 0x1000;
|
||||
protected int mGrantedCount = 0;
|
||||
|
||||
protected abstract void onPermissionGranted();
|
||||
|
||||
private int getId(String idName, String type) {
|
||||
return getResources().getIdentifier(idName, type, getPackageName());
|
||||
}
|
||||
|
||||
protected boolean checkPermission() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
List<String> permissions = new ArrayList<>();
|
||||
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
|
||||
permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||
}
|
||||
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)) {
|
||||
permissions.add(Manifest.permission.CAMERA);
|
||||
}
|
||||
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO)) {
|
||||
permissions.add(Manifest.permission.RECORD_AUDIO);
|
||||
}
|
||||
if (PackageManager.PERMISSION_GRANTED != ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
||||
permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||
}
|
||||
if (permissions.size() != 0) {
|
||||
ActivityCompat.requestPermissions(TRTCBaseActivity.this,
|
||||
permissions.toArray(new String[0]),
|
||||
REQ_PERMISSION_CODE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
switch (requestCode) {
|
||||
case REQ_PERMISSION_CODE:
|
||||
for (int ret : grantResults) {
|
||||
if (PackageManager.PERMISSION_GRANTED == ret) {
|
||||
mGrantedCount ++;
|
||||
}
|
||||
}
|
||||
if (mGrantedCount == permissions.length) {
|
||||
onPermissionGranted();
|
||||
} else {
|
||||
Toast.makeText(this, getString(getId("common_please_input_roomid_and_userid", "string")), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
mGrantedCount = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
27
src/android/java/com/tencent/trtc/debug/Constant.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.tencent.trtc.debug;
|
||||
|
||||
public class Constant {
|
||||
|
||||
public static final String ROOM_ID = "room_id";
|
||||
public static final String USER_ID = "user_id";
|
||||
public static final String SDK_APP_ID = "sdk_app_id";
|
||||
public static final String USER_SIG = "user_sig";
|
||||
public static final String ROLE_TYPE = "role_type";
|
||||
public static final String CUSTOM_VIDEO = "custom_video";
|
||||
|
||||
// 美颜风格.三种美颜风格:0 :光滑 1:自然 2:朦胧
|
||||
public static final int BEAUTY_STYLE_SMOOTH = 0;
|
||||
public static final int BEAUTY_STYLE_NATURE = 1;
|
||||
|
||||
public static final int VIDEO_FPS = 15;
|
||||
// RTC 通话场景:640*360,15fps,550kbps
|
||||
public static final int RTC_VIDEO_BITRATE = 550;
|
||||
|
||||
// 直播场景:连麦小主播:270*480, 15pfs, 400kbps
|
||||
public static final int LIVE_270_480_VIDEO_BITRATE = 400;
|
||||
public static final int LIVE_360_640_VIDEO_BITRATE = 800;
|
||||
// 直播场景:大主播:默认 540*960, 15fps,1200kbps
|
||||
public static final int LIVE_540_960_VIDEO_BITRATE = 900;
|
||||
public static final int LIVE_720_1280_VIDEO_BITRATE = 1500;
|
||||
|
||||
}
|
280
src/android/java/com/tencent/trtc/debug/GenerateTestUserSig.java
Normal file
@ -0,0 +1,280 @@
|
||||
package com.tencent.trtc.debug;
|
||||
|
||||
|
||||
import android.util.Base64;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.Deflater;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Module: GenerateTestUserSig
|
||||
*
|
||||
* Function: 用于生成测试用的 UserSig,UserSig 是腾讯云为其云服务设计的一种安全保护签名。
|
||||
* 其计算方法是对 SDKAppID、UserID 和 EXPIRETIME 进行加密,加密算法为 HMAC-SHA256。
|
||||
*
|
||||
* Attention: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
|
||||
*
|
||||
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
|
||||
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
|
||||
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
|
||||
*
|
||||
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
|
||||
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
|
||||
*
|
||||
* Reference:https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module: GenerateTestUserSig
|
||||
*
|
||||
* Description: generates UserSig for testing. UserSig is a security signature designed by Tencent Cloud for its cloud services.
|
||||
* It is calculated based on `SDKAppID`, `UserID`, and `EXPIRETIME` using the HMAC-SHA256 encryption algorithm.
|
||||
*
|
||||
* Attention: do not use the code below in your commercial app. This is because:
|
||||
*
|
||||
* The code may be able to calculate UserSig correctly, but it is only for quick testing of the SDK’s basic features, not for commercial apps.
|
||||
* `SECRETKEY` in client code can be easily decompiled and reversed, especially on web.
|
||||
* Once your key is disclosed, attackers will be able to steal your Tencent Cloud traffic.
|
||||
*
|
||||
* The correct method is to deploy the `UserSig` calculation code and encryption key on your project server so that your app can request from your server a `UserSig` that is calculated whenever one is needed.
|
||||
* Given that it is more difficult to hack a server than a client app, server-end calculation can better protect your key.
|
||||
*
|
||||
* Reference: https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
public class GenerateTestUserSig {
|
||||
|
||||
/**
|
||||
* 配置为CDN发布、混流的域名
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Domain name for CDN publishing and stream mixing
|
||||
*/
|
||||
public static final String CDN_DOMAIN_NAME = "testuser";
|
||||
|
||||
/**
|
||||
* CDN发布功能 混流bizId
|
||||
*/
|
||||
|
||||
/**
|
||||
* `bizId` for CDN publishing and stream mixing
|
||||
*/
|
||||
public static final int BIZID = 111;
|
||||
|
||||
/**
|
||||
* CDN发布功能 混流appId
|
||||
*/
|
||||
|
||||
/**
|
||||
* `appId` for CDN publishing and stream mixing
|
||||
*/
|
||||
public static final int APPID = 111;
|
||||
|
||||
/**
|
||||
* 腾讯云 SDKAppId,需要替换为您自己账号下的 SDKAppId。
|
||||
*
|
||||
* 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ) 创建应用,即可看到 SDKAppId,
|
||||
* 它是腾讯云用于区分客户的唯一标识。
|
||||
*/
|
||||
|
||||
/**
|
||||
* Tencent Cloud `SDKAppID`. Set it to the `SDKAppID` of your account.
|
||||
*
|
||||
* You can view your `SDKAppID` after creating an application in the [TRTC console](https://console.cloud.tencent.com/rav).
|
||||
* `SDKAppID` uniquely identifies a Tencent Cloud account.
|
||||
*/
|
||||
public static final int SDKAPPID = 1400593044;
|
||||
|
||||
/**
|
||||
* 签名过期时间,建议不要设置的过短
|
||||
* <p>
|
||||
* 时间单位:秒
|
||||
* 默认时间:7 x 24 x 60 x 60 = 604800 = 7 天
|
||||
*/
|
||||
|
||||
/**
|
||||
* Signature validity period, which should not be set too short
|
||||
* <p>
|
||||
* Unit: second
|
||||
* Default value: 604800 (7 days)
|
||||
*/
|
||||
private static final int EXPIRETIME = 604800;
|
||||
|
||||
|
||||
/**
|
||||
* 计算签名用的加密密钥,获取步骤如下:
|
||||
*
|
||||
* step1. 进入腾讯云实时音视频[控制台](https://console.cloud.tencent.com/rav ),如果还没有应用就创建一个,
|
||||
* step2. 单击应用信息,并进一步找到“快速上手”部分。
|
||||
* step3. 点击“复制密钥”按钮,复制密钥,请将其拷贝并复制到如下的变量中
|
||||
*
|
||||
* 注意:该方案仅适用于调试Demo,正式上线前请将 UserSig 计算代码和密钥迁移到您的后台服务器上,以避免加密密钥泄露导致的流量盗用。
|
||||
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
|
||||
/**
|
||||
* Follow the steps below to obtain the key required for UserSig calculation.
|
||||
*
|
||||
* Step 1. Log in to the [TRTC console](https://console.cloud.tencent.com/rav), and create an application if you don’t have one.
|
||||
* Step 2. Find your application, click “Application Info”, and click the “Quick Start” tab.
|
||||
* Step 3. Copy and paste the key to the code, as shown below.
|
||||
*
|
||||
* Note: this method is for testing only. Before commercial launch, please migrate the UserSig calculation code and key to your backend server to prevent key disclosure and traffic stealing.
|
||||
* Reference: https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
public static final String SECRETKEY = "f814a0480cfa95a3ffffd8891844f3cd36888166dbef5347303043744f247c06";
|
||||
|
||||
/**
|
||||
* 计算 UserSig 签名
|
||||
*
|
||||
* 函数内部使用 HMAC-SHA256 非对称加密算法,对 SDKAPPID、userId 和 EXPIRETIME 进行加密。
|
||||
*
|
||||
* @note: 请不要将如下代码发布到您的线上正式版本的 App 中,原因如下:
|
||||
*
|
||||
* 本文件中的代码虽然能够正确计算出 UserSig,但仅适合快速调通 SDK 的基本功能,不适合线上产品,
|
||||
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解,尤其是 Web 端的代码被破解的难度几乎为零。
|
||||
* 一旦您的密钥泄露,攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量。
|
||||
*
|
||||
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上,然后由 App 按需向您的服务器获取实时算出的 UserSig。
|
||||
* 由于破解服务器的成本要高于破解客户端 App,所以服务器计算的方案能够更好地保护您的加密密钥。
|
||||
*
|
||||
* 文档:https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
|
||||
/**
|
||||
* Calculating UserSig
|
||||
*
|
||||
* The asymmetric encryption algorithm HMAC-SHA256 is used in the function to calculate UserSig based on `SDKAppID`, `UserID`, and `EXPIRETIME`.
|
||||
*
|
||||
* @note: do not use the code below in your commercial app. This is because:
|
||||
*
|
||||
* The code may be able to calculate UserSig correctly, but it is only for quick testing of the SDK’s basic features, not for commercial apps.
|
||||
* `SECRETKEY` in client code can be easily decompiled and reversed, especially on web.
|
||||
* Once your key is disclosed, attackers will be able to steal your Tencent Cloud traffic.
|
||||
*
|
||||
* The correct method is to deploy the `UserSig` calculation code on your project server so that your app can request from your server a `UserSig` that is calculated whenever one is needed.
|
||||
* Given that it is more difficult to hack a server than a client app, server-end calculation can better protect your key.
|
||||
*
|
||||
* Reference: https://cloud.tencent.com/document/product/647/17275#Server
|
||||
*/
|
||||
public static String genTestUserSig(String userId) {
|
||||
return GenTLSSignature(SDKAPPID, userId, EXPIRETIME, null, SECRETKEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成 tls 票据
|
||||
*
|
||||
* @param sdkappid 应用的 appid
|
||||
* @param userId 用户 id
|
||||
* @param expire 有效期,单位是秒
|
||||
* @param userbuf 默认填写null
|
||||
* @param priKeyContent 生成 tls 票据使用的私钥内容
|
||||
* @return 如果出错,会返回为空,或者有异常打印,成功返回有效的票据
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generating a TLS Ticket
|
||||
*
|
||||
* @param sdkappid `appid` of your application
|
||||
* @param userId User ID
|
||||
* @param expire Validity period, in seconds
|
||||
* @param userbuf `null` by default
|
||||
* @param priKeyContent Private key required for generating a TLS ticket
|
||||
* @return If an error occurs, an empty string will be returned or exceptions printed. If the operation succeeds, a valid ticket will be returned.
|
||||
*/
|
||||
private static String GenTLSSignature(long sdkappid, String userId, long expire, byte[] userbuf, String priKeyContent) {
|
||||
long currTime = System.currentTimeMillis() / 1000;
|
||||
JSONObject sigDoc = new JSONObject();
|
||||
try {
|
||||
sigDoc.put("TLS.ver", "2.0");
|
||||
sigDoc.put("TLS.identifier", userId);
|
||||
sigDoc.put("TLS.sdkappid", sdkappid);
|
||||
sigDoc.put("TLS.expire", expire);
|
||||
sigDoc.put("TLS.time", currTime);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
String base64UserBuf = null;
|
||||
if (null != userbuf) {
|
||||
base64UserBuf = Base64.encodeToString(userbuf, Base64.NO_WRAP);
|
||||
try {
|
||||
sigDoc.put("TLS.userbuf", base64UserBuf);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
String sig = hmacsha256(sdkappid, userId, currTime, expire, priKeyContent, base64UserBuf);
|
||||
if (sig.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
sigDoc.put("TLS.sig", sig);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Deflater compressor = new Deflater();
|
||||
compressor.setInput(sigDoc.toString().getBytes(Charset.forName("UTF-8")));
|
||||
compressor.finish();
|
||||
byte[] compressedBytes = new byte[2048];
|
||||
int compressedBytesLength = compressor.deflate(compressedBytes);
|
||||
compressor.end();
|
||||
return new String(base64EncodeUrl(Arrays.copyOfRange(compressedBytes, 0, compressedBytesLength)));
|
||||
}
|
||||
|
||||
|
||||
private static String hmacsha256(long sdkappid, String userId, long currTime, long expire, String priKeyContent, String base64Userbuf) {
|
||||
String contentToBeSigned = "TLS.identifier:" + userId + "\n"
|
||||
+ "TLS.sdkappid:" + sdkappid + "\n"
|
||||
+ "TLS.time:" + currTime + "\n"
|
||||
+ "TLS.expire:" + expire + "\n";
|
||||
if (null != base64Userbuf) {
|
||||
contentToBeSigned += "TLS.userbuf:" + base64Userbuf + "\n";
|
||||
}
|
||||
try {
|
||||
byte[] byteKey = priKeyContent.getBytes("UTF-8");
|
||||
Mac hmac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec keySpec = new SecretKeySpec(byteKey, "HmacSHA256");
|
||||
hmac.init(keySpec);
|
||||
byte[] byteSig = hmac.doFinal(contentToBeSigned.getBytes("UTF-8"));
|
||||
return new String(Base64.encode(byteSig, Base64.NO_WRAP));
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
return "";
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
return "";
|
||||
} catch (InvalidKeyException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] base64EncodeUrl(byte[] input) {
|
||||
byte[] base64 = new String(Base64.encode(input, Base64.NO_WRAP)).getBytes();
|
||||
for (int i = 0; i < base64.length; ++i)
|
||||
switch (base64[i]) {
|
||||
case '+':
|
||||
base64[i] = '*';
|
||||
break;
|
||||
case '/':
|
||||
base64[i] = '-';
|
||||
break;
|
||||
case '=':
|
||||
base64[i] = '_';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
}
|
18
src/android/java/com/tencent/trtc/videocall/BuildConfig.java
Normal file
@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Automatically generated file. DO NOT MODIFY
|
||||
*/
|
||||
package com.tencent.trtc.videocall;
|
||||
|
||||
public final class BuildConfig {
|
||||
public static final boolean DEBUG = Boolean.parseBoolean("true");
|
||||
public static final String LIBRARY_PACKAGE_NAME = "com.tencent.trtc.videocall";
|
||||
/**
|
||||
* @deprecated APPLICATION_ID is misleading in libraries. For the library package name use LIBRARY_PACKAGE_NAME
|
||||
*/
|
||||
@Deprecated
|
||||
public static final String APPLICATION_ID = "com.tencent.trtc.videocall";
|
||||
public static final String BUILD_TYPE = "debug";
|
||||
public static final String FLAVOR = "";
|
||||
public static final int VERSION_CODE = 1;
|
||||
public static final String VERSION_NAME = "1.0";
|
||||
}
|
193
src/android/java/com/tencent/trtc/videocall/FloatingView.java
Normal file
@ -0,0 +1,193 @@
|
||||
package com.tencent.trtc.videocall;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
/**
|
||||
* 悬浮球,点击可以弹出菜单
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public class FloatingView extends FrameLayout implements GestureDetector.OnGestureListener {
|
||||
|
||||
private Context mContext;
|
||||
private WindowManager mWindowManager;
|
||||
private GestureDetector mGestureDetector;
|
||||
private WindowManager.LayoutParams mLayoutParams;
|
||||
private float mLastX;
|
||||
private float mLastY;
|
||||
private PopupWindow mPopupWindow;
|
||||
private long mTapOutsideTime;
|
||||
private boolean mIsShowing = false;
|
||||
|
||||
public FloatingView(Context context) {
|
||||
super(context);
|
||||
this.mContext = context;
|
||||
this.mGestureDetector = new GestureDetector(context, this);
|
||||
}
|
||||
|
||||
public FloatingView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.mContext = context;
|
||||
this.mGestureDetector = new GestureDetector(context, this);
|
||||
}
|
||||
|
||||
public FloatingView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.mContext = context;
|
||||
this.mGestureDetector = new GestureDetector(context, this);
|
||||
}
|
||||
|
||||
public FloatingView(Context context, int viewResId) {
|
||||
super(context);
|
||||
this.mContext = context;
|
||||
View.inflate(context, viewResId, this);
|
||||
this.mGestureDetector = new GestureDetector(context, this);
|
||||
}
|
||||
|
||||
public void showView(View view) {
|
||||
showView(view, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
public void showView(View view, int width, int height) {
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
int type = WindowManager.LayoutParams.TYPE_TOAST;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
|
||||
type = WindowManager.LayoutParams.TYPE_PHONE;
|
||||
}
|
||||
mLayoutParams = new WindowManager.LayoutParams(type);
|
||||
mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
mLayoutParams.flags |= WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
|
||||
mLayoutParams.width = width;
|
||||
mLayoutParams.height = height;
|
||||
mLayoutParams.format = PixelFormat.TRANSLUCENT;
|
||||
mWindowManager.addView(view, mLayoutParams);
|
||||
}
|
||||
|
||||
public void hideView() {
|
||||
if (null != mWindowManager) {
|
||||
mWindowManager.removeViewImmediate(this);
|
||||
}
|
||||
mWindowManager = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return mGestureDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
mLastX = e.getRawX();
|
||||
mLastY = e.getRawY();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(MotionEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
|
||||
float nowX, nowY, tranX, tranY;
|
||||
nowX = e2.getRawX();
|
||||
nowY = e2.getRawY();
|
||||
tranX = nowX - mLastX;
|
||||
tranY = nowY - mLastY;
|
||||
mLayoutParams.x += tranX;
|
||||
mLayoutParams.y += tranY;
|
||||
mWindowManager.updateViewLayout(this, mLayoutParams);
|
||||
mLastX = nowX;
|
||||
mLastY = nowY;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(MotionEvent e) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setPopupWindow(int id) {
|
||||
mPopupWindow = new PopupWindow(this);
|
||||
mPopupWindow.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
mPopupWindow.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
mPopupWindow.setTouchable(true);
|
||||
mPopupWindow.setOutsideTouchable(true);
|
||||
mPopupWindow.setFocusable(false);
|
||||
mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
|
||||
mPopupWindow.setContentView(LayoutInflater.from(getContext()).inflate(id, null));
|
||||
mPopupWindow.setTouchInterceptor(new OnTouchListener() {
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
|
||||
mPopupWindow.dismiss();
|
||||
mTapOutsideTime = System.currentTimeMillis();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public View getPopupView() {
|
||||
return mPopupWindow.getContentView();
|
||||
}
|
||||
|
||||
public void setOnPopupItemClickListener(OnClickListener listener) {
|
||||
if (mPopupWindow == null)
|
||||
return;
|
||||
|
||||
ViewGroup layout = (ViewGroup) mPopupWindow.getContentView();
|
||||
for (int i = 0; i < layout.getChildCount(); i++) {
|
||||
layout.getChildAt(i).setOnClickListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!mIsShowing) {
|
||||
showView(this);
|
||||
}
|
||||
mIsShowing = true;
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
if (mIsShowing) {
|
||||
hideView();
|
||||
}
|
||||
mIsShowing = false;
|
||||
ViewGroup layout = (ViewGroup) mPopupWindow.getContentView();
|
||||
for (int i = 0; i < layout.getChildCount(); i++) {
|
||||
layout.getChildAt(i).setOnClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent e) {
|
||||
if (null != mPopupWindow)
|
||||
mPopupWindow.dismiss();
|
||||
if (!(System.currentTimeMillis() - mTapOutsideTime < 80)) {
|
||||
mPopupWindow.showAtLocation(this, Gravity.NO_GRAVITY, 100, 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,397 @@
|
||||
package com.tencent.trtc.videocall;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import android.java.com.example.basic.TRTCBaseActivity;
|
||||
|
||||
import com.tencent.liteav.TXLiteAVCode;
|
||||
import com.tencent.liteav.device.TXDeviceManager;
|
||||
import com.tencent.rtmp.ui.TXCloudVideoView;
|
||||
import com.tencent.trtc.TRTCCloud;
|
||||
import com.tencent.trtc.TRTCCloudDef;
|
||||
import com.tencent.trtc.TRTCCloudListener;
|
||||
import com.tencent.trtc.debug.Constant;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* TRTC视频通话的主页面
|
||||
* <p>
|
||||
* 包含如下简单功能:
|
||||
* - 进入视频通话房间{@link VideoCallingActivity#enterRoom()}
|
||||
* - 退出视频通话房间{@link VideoCallingActivity#exitRoom()}
|
||||
* - 切换前置/后置摄像头{@link VideoCallingActivity#switchCamera()}
|
||||
* - 打开/关闭摄像头{@link VideoCallingActivity#muteVideo()}
|
||||
* - 打开/关闭麦克风{@link VideoCallingActivity#muteAudio()}
|
||||
* - 显示房间内其他用户的视频画面(当前示例最多可显示6个其他用户的视频画面){@link TRTCCloudImplListener#refreshRemoteVideoViews()}
|
||||
* <p>
|
||||
* - 详见接入文档{https://cloud.tencent.com/document/product/647/42045}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Video Call
|
||||
*
|
||||
* Features:
|
||||
* - Enter a video call room: {@link VideoCallingActivity#enterRoom()}
|
||||
* - Exit a video call room: {@link VideoCallingActivity#exitRoom()}
|
||||
* - Switch between the front and rear cameras: {@link VideoCallingActivity#switchCamera()}
|
||||
* - Turn on/off the camera: {@link VideoCallingActivity#muteVideo()}
|
||||
* - Turn on/off the mic: {@link VideoCallingActivity#muteAudio()}
|
||||
* - Display the video of other users (max. 6) in the room: {@link TRTCCloudImplListener#refreshRemoteVideoViews()}
|
||||
*
|
||||
* - For more information, please see the integration document {https://cloud.tencent.com/document/product/647/42045}.
|
||||
*/
|
||||
public class VideoCallingActivity extends TRTCBaseActivity implements View.OnClickListener {
|
||||
|
||||
private static final String TAG = "VideoCallingActivity";
|
||||
private static final int OVERLAY_PERMISSION_REQ_CODE = 1234;
|
||||
|
||||
private TextView mTextTitle;
|
||||
private TXCloudVideoView mTXCVVLocalPreviewView;
|
||||
private ImageView mImageBack;
|
||||
private ImageView mButtonMuteVideo;
|
||||
private ImageView mButtonMuteAudio;
|
||||
private ImageView mButtonSwitchCamera;
|
||||
private ImageView mButtonAudioRoute;
|
||||
|
||||
private TRTCCloud mTRTCCloud;
|
||||
private TXDeviceManager mTXDeviceManager;
|
||||
private boolean mIsFrontCamera = true;
|
||||
private List<String> mRemoteUidList;
|
||||
private List<TXCloudVideoView> mRemoteViewList;
|
||||
private int mUserCount = 0;
|
||||
private String mRoomId;
|
||||
private String mUserId;
|
||||
private int sdkAppId;
|
||||
private String userSig;
|
||||
private boolean mAudioRouteFlag = true;
|
||||
private FloatingView mFloatingView;
|
||||
|
||||
private int getId(String idName, String type) {
|
||||
return getResources().getIdentifier(idName, type, getPackageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// R.layout.videocall_activity_calling
|
||||
setContentView(getId("videocall_activity_calling", "layout"));
|
||||
getSupportActionBar().hide();
|
||||
handleIntent();
|
||||
|
||||
if (checkPermission()) {
|
||||
initView();
|
||||
enterRoom();
|
||||
}
|
||||
}
|
||||
|
||||
private void handleIntent() {
|
||||
Intent intent = getIntent();
|
||||
if (null != intent) {
|
||||
if (intent.getStringExtra(Constant.USER_ID) != null) {
|
||||
mUserId = intent.getStringExtra(Constant.USER_ID);
|
||||
}
|
||||
if (intent.getStringExtra(Constant.ROOM_ID) != null) {
|
||||
mRoomId = intent.getStringExtra(Constant.ROOM_ID);
|
||||
}
|
||||
|
||||
if (intent.getIntExtra(Constant.SDK_APP_ID, 0) != 0) {
|
||||
sdkAppId = intent.getIntExtra(Constant.SDK_APP_ID, 0);
|
||||
}
|
||||
|
||||
if (intent.getStringExtra(Constant.USER_SIG) != null) {
|
||||
userSig = intent.getStringExtra(Constant.USER_SIG);
|
||||
}
|
||||
}
|
||||
System.out.println("sdkAppId = " + sdkAppId);
|
||||
System.out.println("userSig = " + userSig);
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
// R.id.tv_room_number
|
||||
mTextTitle = findViewById(getId("tv_room_number", "id"));
|
||||
// R.id.iv_back
|
||||
mImageBack = findViewById(getId("iv_back", "id"));
|
||||
// R.id.txcvv_main
|
||||
mTXCVVLocalPreviewView = findViewById(getId("txcvv_main", "id"));
|
||||
// R.id.btn_mute_video
|
||||
mButtonMuteVideo = findViewById(getId("btn_mute_video", "id"));
|
||||
// R.id.btn_mute_audio
|
||||
mButtonMuteAudio = findViewById(getId("btn_mute_audio", "id"));
|
||||
// R.id.btn_switch_camera
|
||||
mButtonSwitchCamera = findViewById(getId("btn_switch_camera", "id"));
|
||||
// R.id.btn_audio_route
|
||||
mButtonAudioRoute = findViewById(getId("btn_audio_route", "id"));
|
||||
|
||||
if (!TextUtils.isEmpty(mRoomId)) {
|
||||
mTextTitle.setText(getString(getId("videocall_roomid", "string")) + mRoomId);
|
||||
}
|
||||
mImageBack.setOnClickListener(this);
|
||||
mButtonMuteVideo.setOnClickListener(this);
|
||||
mButtonMuteAudio.setOnClickListener(this);
|
||||
mButtonSwitchCamera.setOnClickListener(this);
|
||||
mButtonAudioRoute.setOnClickListener(this);
|
||||
|
||||
mRemoteUidList = new ArrayList<>();
|
||||
mRemoteViewList = new ArrayList<>();
|
||||
// R.id.trtc_view_1
|
||||
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_1", "id")));
|
||||
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_2", "id")));
|
||||
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_3", "id")));
|
||||
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_4", "id")));
|
||||
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_5", "id")));
|
||||
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_6", "id")));
|
||||
|
||||
// R.layout.videocall_view_floating_default
|
||||
mFloatingView = new FloatingView(getApplicationContext(), getId("videocall_view_floating_default", "layout"));
|
||||
// R.layout.videocall_popup_layout
|
||||
mFloatingView.setPopupWindow(getId("videocall_popup_layout", "layout"));
|
||||
mFloatingView.setOnPopupItemClickListener(this);
|
||||
}
|
||||
|
||||
private void enterRoom() {
|
||||
mTRTCCloud = TRTCCloud.sharedInstance(getApplicationContext());
|
||||
mTRTCCloud.setListener(new TRTCCloudImplListener(VideoCallingActivity.this));
|
||||
mTXDeviceManager = mTRTCCloud.getDeviceManager();
|
||||
|
||||
TRTCCloudDef.TRTCParams trtcParams = new TRTCCloudDef.TRTCParams();
|
||||
trtcParams.sdkAppId = this.sdkAppId;
|
||||
trtcParams.userId = mUserId;
|
||||
trtcParams.roomId = Integer.parseInt(mRoomId);
|
||||
trtcParams.userSig = this.userSig;
|
||||
|
||||
mTRTCCloud.startLocalPreview(mIsFrontCamera, mTXCVVLocalPreviewView);
|
||||
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
|
||||
mTRTCCloud.enterRoom(trtcParams, TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
requestDrawOverLays();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mFloatingView != null && mFloatingView.isShown()) {
|
||||
mFloatingView.dismiss();
|
||||
}
|
||||
exitRoom();
|
||||
}
|
||||
|
||||
private void exitRoom() {
|
||||
if (mTRTCCloud != null) {
|
||||
mTRTCCloud.stopLocalAudio();
|
||||
mTRTCCloud.stopLocalPreview();
|
||||
mTRTCCloud.exitRoom();
|
||||
mTRTCCloud.setListener(null);
|
||||
}
|
||||
mTRTCCloud = null;
|
||||
TRTCCloud.destroySharedInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (mFloatingView != null && mFloatingView.isShown()) {
|
||||
mFloatingView.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPermissionGranted() {
|
||||
initView();
|
||||
enterRoom();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == getId("iv_back", "id")) {
|
||||
finish();
|
||||
} else if (id == getId("btn_mute_video", "id")) {
|
||||
muteVideo();
|
||||
} else if (id == getId("btn_mute_audio", "id")) {
|
||||
muteAudio();
|
||||
} else if (id == getId("btn_switch_camera", "id")) {
|
||||
switchCamera();
|
||||
} else if (id == getId("btn_audio_route", "id")) {
|
||||
audioRoute();
|
||||
} else if (id == getId("iv_return", "id")) {
|
||||
floatViewClick();
|
||||
}
|
||||
}
|
||||
|
||||
private void floatViewClick() {
|
||||
Intent intent = new Intent(this, VideoCallingActivity.class);
|
||||
if (intent != null) {
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
}
|
||||
try {
|
||||
startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void muteVideo() {
|
||||
boolean isSelected = mButtonMuteVideo.isSelected();
|
||||
if (!isSelected) {
|
||||
mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_open", "mipmap")));
|
||||
closeRemoteVideoViews();
|
||||
} else {
|
||||
mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_close", "mipmap")));
|
||||
refreshRemoteVideoViews();
|
||||
}
|
||||
mButtonMuteVideo.setSelected(!isSelected);
|
||||
}
|
||||
|
||||
|
||||
private void closeRemoteVideoViews() {
|
||||
for (int i = 0; i < mRemoteViewList.size(); i++) {
|
||||
if (i < mRemoteUidList.size()) {
|
||||
mRemoteViewList.get(i).setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
mRemoteViewList.get(i).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void refreshRemoteVideoViews() {
|
||||
for (int i = 0; i < mRemoteViewList.size(); i++) {
|
||||
if (i < mRemoteUidList.size()) {
|
||||
String remoteUid = mRemoteUidList.get(i);
|
||||
mRemoteViewList.get(i).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mRemoteViewList.get(i).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void muteAudio() {
|
||||
boolean isSelected = mButtonMuteAudio.isSelected();
|
||||
if (!isSelected) {
|
||||
mTRTCCloud.muteLocalAudio(true);
|
||||
mButtonMuteAudio.setImageDrawable(getDrawable(getId("mac_on", "mipmap")));
|
||||
} else {
|
||||
mTRTCCloud.muteLocalAudio(false);
|
||||
mButtonMuteAudio.setImageDrawable(getDrawable(getId("mac_off", "mipmap")));
|
||||
}
|
||||
mButtonMuteAudio.setSelected(!isSelected);
|
||||
}
|
||||
|
||||
private void switchCamera() {
|
||||
mIsFrontCamera = !mIsFrontCamera;
|
||||
mTXDeviceManager.switchCamera(mIsFrontCamera);
|
||||
}
|
||||
|
||||
private void audioRoute() {
|
||||
if (mAudioRouteFlag) {
|
||||
mAudioRouteFlag = false;
|
||||
mTXDeviceManager.setAudioRoute(TXDeviceManager.TXAudioRoute.TXAudioRouteEarpiece);
|
||||
mButtonAudioRoute.setImageDrawable(getDrawable(getId("earpiece", "mipmap")));
|
||||
} else {
|
||||
mAudioRouteFlag = true;
|
||||
mTXDeviceManager.setAudioRoute(TXDeviceManager.TXAudioRoute.TXAudioRouteSpeakerphone);
|
||||
mButtonAudioRoute.setImageDrawable(getDrawable(getId("speaker", "mipmap")));
|
||||
}
|
||||
}
|
||||
|
||||
private class TRTCCloudImplListener extends TRTCCloudListener {
|
||||
|
||||
private WeakReference<VideoCallingActivity> mContext;
|
||||
|
||||
public TRTCCloudImplListener(VideoCallingActivity activity) {
|
||||
super();
|
||||
mContext = new WeakReference<>(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserVideoAvailable(String userId, boolean available) {
|
||||
Log.d(TAG, "onUserVideoAvailable userId " + userId + ", mUserCount " + mUserCount + ",available " + available);
|
||||
int index = mRemoteUidList.indexOf(userId);
|
||||
if (available) {
|
||||
if (index != -1) {
|
||||
return;
|
||||
}
|
||||
mRemoteUidList.add(userId);
|
||||
refreshRemoteVideoViews();
|
||||
} else {
|
||||
if (index == -1) {
|
||||
return;
|
||||
}
|
||||
mTRTCCloud.stopRemoteView(userId);
|
||||
mRemoteUidList.remove(index);
|
||||
refreshRemoteVideoViews();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void refreshRemoteVideoViews() {
|
||||
for (int i = 0; i < mRemoteViewList.size(); i++) {
|
||||
if (i < mRemoteUidList.size()) {
|
||||
String remoteUid = mRemoteUidList.get(i);
|
||||
mRemoteViewList.get(i).setVisibility(View.VISIBLE);
|
||||
mTRTCCloud.startRemoteView(remoteUid, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SMALL, mRemoteViewList.get(i));
|
||||
} else {
|
||||
mRemoteViewList.get(i).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int errCode, String errMsg, Bundle extraInfo) {
|
||||
Log.d(TAG, "sdk callback onError");
|
||||
VideoCallingActivity activity = mContext.get();
|
||||
if (activity != null) {
|
||||
Toast.makeText(activity, "onError: " + errMsg + "[" + errCode + "]", Toast.LENGTH_SHORT).show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExitRoom(int reason) {
|
||||
super.onExitRoom(reason);
|
||||
if (reason == 2) {
|
||||
VideoCallingActivity activity = mContext.get();
|
||||
Toast.makeText(activity, "远程协助已结束", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void requestDrawOverLays() {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N && !Settings.canDrawOverlays(VideoCallingActivity.this)) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + VideoCallingActivity.this.getPackageName()));
|
||||
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
|
||||
} else {
|
||||
showFloatingView();
|
||||
}
|
||||
}
|
||||
|
||||
private void showFloatingView() {
|
||||
if (mFloatingView != null && !mFloatingView.isShown()) {
|
||||
if ((null != mTRTCCloud)) {
|
||||
mFloatingView.show();
|
||||
mFloatingView.setOnPopupItemClickListener(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package com.tencent.trtc.videocall;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.tencent.trtc.debug.Constant;
|
||||
|
||||
/**
|
||||
* TRTC视频通话的入口页面(可以设置房间id和用户id)
|
||||
*
|
||||
* - 可跳转TRTC视频通话页面{@link VideoCallingActivity}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Video Call Entrance View (set room ID and user ID)
|
||||
*
|
||||
* - Direct to the video call view: {@link VideoCallingActivity}
|
||||
*/
|
||||
public class VideoCallingEnterActivity extends AppCompatActivity {
|
||||
|
||||
private EditText mEditInputUserId;
|
||||
private EditText mEditInputRoomId;
|
||||
|
||||
private int getId(String idName, String type) {
|
||||
return getResources().getIdentifier(idName, type, getPackageName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// setContentView(R.layout.videocall_activit_enter);
|
||||
setContentView(getId("videocall_activit_enter", "layout"));
|
||||
getSupportActionBar().hide();
|
||||
// mEditInputUserId = findViewById(R.id.et_input_username);
|
||||
// mEditInputRoomId = findViewById(R.id.et_input_room_id);
|
||||
mEditInputUserId = findViewById(getId("et_input_username", "id"));
|
||||
mEditInputRoomId = findViewById(getId("et_input_room_id", "id"));
|
||||
// R.id.btn_enter_room
|
||||
findViewById(getId("btn_enter_room", "id")).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
startEnterRoom();
|
||||
}
|
||||
});
|
||||
// R.id.rl_entrance_main
|
||||
findViewById(getId("rl_entrance_main", "id")).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
hideInput();
|
||||
}
|
||||
});
|
||||
// R.id.iv_back
|
||||
findViewById(getId("iv_back", "id")).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
mEditInputRoomId.setText("1256732");
|
||||
String time = String.valueOf(System.currentTimeMillis());
|
||||
String userId = time.substring(time.length() - 8);
|
||||
mEditInputUserId.setText(userId);
|
||||
}
|
||||
|
||||
private void startEnterRoom() {
|
||||
if (TextUtils.isEmpty(mEditInputUserId.getText().toString().trim())
|
||||
|| TextUtils.isEmpty(mEditInputRoomId.getText().toString().trim())) {
|
||||
// R.string.videocall_room_input_error_tip
|
||||
Toast.makeText(VideoCallingEnterActivity.this, getString(getId("videocall_room_input_error_tip", "string")), Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(VideoCallingEnterActivity.this, VideoCallingActivity.class);
|
||||
intent.putExtra(Constant.ROOM_ID, mEditInputRoomId.getText().toString().trim());
|
||||
intent.putExtra(Constant.USER_ID, mEditInputUserId.getText().toString().trim());
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
protected void hideInput() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
View v = getWindow().peekDecorView();
|
||||
if (null != v) {
|
||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
10
src/android/libs/trtc.gradle
Normal file
@ -0,0 +1,10 @@
|
||||
dependencies {
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile 'androidx.appcompat:appcompat:1.1.0'
|
||||
compile 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
compile 'com.android.support:multidex:1.0.1'
|
||||
compile 'com.tencent.ilivesdk:ilivesdk:1.9.3'
|
||||
compile 'com.tencent.ilivefilter:liteav_normal:1.1.21'
|
||||
compile 'com.google.android.material:material:1.3.0'
|
||||
compile 'com.tencent.liteav:LiteAVSDK_TRTC:latest.release'
|
||||
}
|
BIN
src/android/liteavsdk.jar
Normal file
11
src/android/res/drawable/common_button_bg.xml
Normal file
@ -0,0 +1,11 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@color/common_green_bg" />
|
||||
|
||||
<corners
|
||||
android:bottomLeftRadius="5dip"
|
||||
android:bottomRightRadius="5dip"
|
||||
android:topLeftRadius="5dip"
|
||||
android:topRightRadius="5dip" />
|
||||
</shape>
|
16
src/android/res/drawable/common_edit_bg.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="@color/common_edit_solid" />
|
||||
|
||||
<corners
|
||||
android:bottomLeftRadius="2dip"
|
||||
android:bottomRightRadius="2dip"
|
||||
android:topLeftRadius="2dip"
|
||||
android:topRightRadius="2dip" />
|
||||
|
||||
<stroke
|
||||
android:width="5px"
|
||||
android:color="@color/common_edit_stroke" />
|
||||
|
||||
</shape>
|
32
src/android/res/drawable/common_seekbar_style.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@android:id/background">
|
||||
<shape>
|
||||
<corners android:radius="1dp" />
|
||||
<solid android:color="#999" />
|
||||
<size android:height="2dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/secondaryProgress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="1dp" />
|
||||
<solid android:color="#999" />
|
||||
<size android:height="5dp"/>
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/progress">
|
||||
<clip>
|
||||
<shape>
|
||||
<corners android:radius="5dip" />
|
||||
<solid android:color="#999" />
|
||||
</shape>
|
||||
</clip>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
8
src/android/res/drawable/common_seekbar_thumb.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape android:shape="rectangle"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<size android:width="20dp"
|
||||
android:height="20dp"/>
|
||||
<solid android:color="@android:color/white"/>
|
||||
<corners android:radius="10dp"/>
|
||||
</shape>
|
8
src/android/res/drawable/common_selector_radio_bg.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_checked="true"
|
||||
android:color="@color/common_radio_select"/>
|
||||
|
||||
<item android:state_checked="false"
|
||||
android:color="@color/common_raido_select_off"/>
|
||||
</selector>
|
5
src/android/res/drawable/videocall_background.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#6f000000" />
|
||||
<corners android:radius="40dp" />
|
||||
</shape>
|
94
src/android/res/layout/videocall_activit_enter.xml
Normal file
@ -0,0 +1,94 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/rl_entrance_main"
|
||||
android:background="@color/rl_main_bg">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:background="@mipmap/common_ic_back" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="@string/videocall_title"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_room_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="100dp"
|
||||
android:layout_marginRight="60dp"
|
||||
android:layout_marginLeft="60dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:text="@string/videocall_please_input_roomid" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_input_room_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="34dp"
|
||||
android:background="@drawable/common_edit_bg"
|
||||
android:layout_marginTop="5dp"
|
||||
android:inputType="number" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/ll_room_info"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="30dp"
|
||||
android:layout_marginLeft="60dp"
|
||||
android:layout_marginRight="60dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:text="@string/videocall_please_input_userid" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_input_username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="34dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:background="@drawable/common_edit_bg"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_enter_room"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:background="@drawable/common_button_bg"
|
||||
android:textColor="@android:color/white"
|
||||
android:layout_margin="60dp"
|
||||
android:text="@string/videocall_enter_room" />
|
||||
|
||||
</RelativeLayout>
|
148
src/android/res/layout/videocall_activity_calling.xml
Normal file
@ -0,0 +1,148 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/rl_main_bg">
|
||||
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/txcvv_main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="72dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp">
|
||||
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_back"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:background="@mipmap/common_ic_back" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_room_number"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginTop="15dp"
|
||||
android:gravity="center_horizontal"
|
||||
android:text="1111"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/trtc_view_1"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginTop="70dp"
|
||||
android:layout_marginRight="15dp"/>
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/trtc_view_2"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_below="@id/trtc_view_1"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/trtc_view_3"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_below="@id/trtc_view_2"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginTop="10dp"/>
|
||||
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/trtc_view_4"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignTop="@+id/trtc_view_3"
|
||||
android:layout_marginLeft="15dp"/>
|
||||
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/trtc_view_5"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignTop="@+id/trtc_view_2"
|
||||
android:layout_marginLeft="15dp"/>
|
||||
|
||||
|
||||
<com.tencent.rtmp.ui.TXCloudVideoView
|
||||
android:id="@+id/trtc_view_6"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="160dp"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignTop="@+id/trtc_view_1"
|
||||
android:layout_marginLeft="15dp"/>
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginLeft="20dp"
|
||||
android:layout_marginRight="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_controller"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_mute_audio"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@mipmap/mac_off"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_audio_route"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@mipmap/speaker"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_switch_camera"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@mipmap/swap_camera"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_mute_video"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="20dp"
|
||||
android:src="@mipmap/view_close"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
18
src/android/res/layout/videocall_popup_layout.xml
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/videocall_background"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_return"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="10dp"
|
||||
android:padding="2dp"
|
||||
android:src="@mipmap/videocall_home"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp" />
|
||||
</LinearLayout>
|
12
src/android/res/layout/videocall_view_floating_default.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:src="@mipmap/videocall_float_logo"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</LinearLayout>
|
BIN
src/android/res/mipmap-xxhdpi/common_ic_back.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/android/res/mipmap-xxhdpi/common_user_portrait.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
src/android/res/mipmap-xxhdpi/earpiece.png
Normal file
After Width: | Height: | Size: 6.7 KiB |
BIN
src/android/res/mipmap-xxhdpi/mac_off.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
src/android/res/mipmap-xxhdpi/mac_on.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
src/android/res/mipmap-xxhdpi/speaker.png
Normal file
After Width: | Height: | Size: 5.9 KiB |
BIN
src/android/res/mipmap-xxhdpi/swap_camera.png
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
src/android/res/mipmap-xxhdpi/videocall_float_logo.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
src/android/res/mipmap-xxhdpi/videocall_home.png
Normal file
After Width: | Height: | Size: 920 B |
BIN
src/android/res/mipmap-xxhdpi/view_close.png
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
src/android/res/mipmap-xxhdpi/view_open.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
55
src/android/res/values-en/strings.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<resources>
|
||||
|
||||
<string name="app_name">TRTC-API-Example</string>
|
||||
<string name="activity_name">test</string>
|
||||
<string name="launcher_name">testtest</string>
|
||||
<string name="main_trtc_title">TRTC API Example</string>
|
||||
|
||||
<string name="common_please_input_roomid_and_userid">Failed to join call as the user did not grant the required permission.</string>
|
||||
|
||||
<string name="main_trtc_base_funciton">Basic Features</string>
|
||||
<string name="main_item_aduio_call">Audio Call</string>
|
||||
<string name="main_item_aduio_call_desc">One-to-one/Group audio call, which supports muting, hands-free mode, etc.</string>
|
||||
<string name="main_item_video_call">Video Call</string>
|
||||
<string name="main_item_video_call_desc">One-to-one/Group video call, which supports muting, hands-free mode, etc.</string>
|
||||
<string name="main_item_live">Interactive Live Video Streaming</string>
|
||||
<string name="main_item_voice_chat_room">Interactive Live Audio Streaming</string>
|
||||
<string name="main_item_screen_share">Screen Recording Live Streaming</string>
|
||||
<string name="main_item_screen_share_desc">Share the screen during live streaming, designed for online education, game streaming, etc.</string>
|
||||
|
||||
<string name="main_trtc_advanced">Advanced Features</string>
|
||||
<string name="main_item_string_room_id">String-type Room ID</string>
|
||||
<string name="main_rtrc_set_video_quality">Video Quality Setting</string>
|
||||
<string name="main_rtrc_set_audio_quality">Audio Quality Setting</string>
|
||||
<string name="main_rerc_render_params">Render Control</string>
|
||||
<string name="main_item_speed_test">Network Speed Testing</string>
|
||||
<string name="main_item_pushcdn">Publish via CDN</string>
|
||||
<string name="main_trtc_custom_camera">Custom Video Capturing & Rendering</string>
|
||||
<string name="main_rtrc_set_audio_effect">Audio Effect Setting</string>
|
||||
<string name="main_trtc_set_bgm">Background Music Setting</string>
|
||||
<string name="main_trtc_local_video_share">Local Video Sharing</string>
|
||||
<string name="main_item_local_record">Local Recording</string>
|
||||
<string name="main_item_join_multiple_room">Enter Multiple Rooms</string>
|
||||
<string name="main_item_sei_message">Receive/Send SEI Message</string>
|
||||
<string name="main_item_switch_room">Switch Room</string>
|
||||
<string name="main_trtc_connect_other_room_pk">Cross-room Competition</string>
|
||||
<string name="main_item_third_beauty">Third-party Beauty Filter</string>
|
||||
|
||||
|
||||
<string name="videocall_title">Video Call</string>
|
||||
<string name="videocall_room_input_error_tip">Enter a room ID and username</string>
|
||||
<string name="videocall_video_item">Video Settings</string>
|
||||
<string name="videocall_audio_item">Audio Settings</string>
|
||||
<string name="videocall_user_back_camera">Use Rear Camera</string>
|
||||
<string name="videocall_user_front_camera">Use Front Camera</string>
|
||||
<string name="videocall_open_camera">Camera On</string>
|
||||
<string name="videocall_close_camera">Camera Off</string>
|
||||
<string name="videocall_mute_audio">Mic On</string>
|
||||
<string name="videocall_close_mute_audio">Mic Off</string>
|
||||
<string name="videocall_use_speaker">Use Speaker</string>
|
||||
<string name="videocall_use_receiver">Use Receiver</string>
|
||||
<string name="videocall_enter_room">Enter Room</string>
|
||||
<string name="videocall_please_input_userid">User ID (Required)</string>
|
||||
<string name="videocall_please_input_roomid">Room ID (Required)</string>
|
||||
<string name="videocall_roomid">Room ID: </string>
|
||||
</resources>
|
13
src/android/res/values-en/styles.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
14
src/android/res/values/colors.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="rl_main_bg">#1B1B1B</color>
|
||||
<color name="videocall_green_bg">#00A66B</color>
|
||||
<color name="videocall_title_bg">#B7B7B7</color>
|
||||
<color name="common_green_bg">#34c759</color>
|
||||
<color name="common_edit_solid">#EFEFEF</color>
|
||||
<color name="common_edit_stroke">#FFFFFF</color>
|
||||
<color name="common_radio_select">#34c759</color>
|
||||
<color name="common_raido_select_off">#999999</color>
|
||||
<color name="colorPrimary">#008577</color>
|
||||
<color name="colorPrimaryDark">#00574B</color>
|
||||
<color name="colorAccent">#D81B60</color>
|
||||
</resources>
|
55
src/android/res/values/strings.xml
Normal file
@ -0,0 +1,55 @@
|
||||
<resources>
|
||||
<string name="app_name">TRTC-API-Example</string>
|
||||
<string name="activity_name">测试</string>
|
||||
<string name="launcher_name">测试测试</string>
|
||||
<string name="main_trtc_title">腾讯云TRTC API Example</string>
|
||||
|
||||
<string name="common_please_input_roomid_and_userid">用户没有允许需要的权限,加入通话失败</string>
|
||||
|
||||
|
||||
<string name="main_trtc_base_funciton">基础功能</string>
|
||||
<string name="main_item_aduio_call">语音通话</string>
|
||||
<string name="main_item_aduio_call_desc">双人/多人语音通话、包含静音/免提等功能</string>
|
||||
<string name="main_item_video_call">视频通话</string>
|
||||
<string name="main_item_video_call_desc">双人/多人视频通话、包含静音/免提等功能</string>
|
||||
<string name="main_item_live">视频互动直播</string>
|
||||
<string name="main_item_voice_chat_room">语音互动直播</string>
|
||||
<string name="main_item_screen_share">录屏直播</string>
|
||||
<string name="main_item_screen_share_desc">直播过程中分享屏幕,适用于在线教育,游戏直播等场景</string>
|
||||
|
||||
<string name="main_trtc_advanced">进阶功能</string>
|
||||
<string name="main_item_string_room_id">字符串房间号</string>
|
||||
<string name="main_rtrc_set_video_quality">画质设定</string>
|
||||
<string name="main_rtrc_set_audio_quality">音质设定</string>
|
||||
<string name="main_rerc_render_params">渲染控制</string>
|
||||
<string name="main_item_speed_test">网络测速</string>
|
||||
<string name="main_item_pushcdn">CDN发布</string>
|
||||
<string name="main_trtc_custom_camera">自定义视频采集&渲染</string>
|
||||
<string name="main_rtrc_set_audio_effect">设置音效</string>
|
||||
<string name="main_trtc_set_bgm">设置背景音乐</string>
|
||||
<string name="main_trtc_local_video_share">本地视频分享</string>
|
||||
<string name="main_item_local_record">本地媒体录制</string>
|
||||
<string name="main_item_join_multiple_room">加入多个房间</string>
|
||||
<string name="main_item_sei_message">收发SEI消息</string>
|
||||
<string name="main_item_switch_room">快速切换房间</string>
|
||||
<string name="main_trtc_connect_other_room_pk">跨房PK</string>
|
||||
<string name="main_item_third_beauty">第三方美颜</string>
|
||||
|
||||
|
||||
<string name="videocall_title">TRTC 视频通话示例</string>
|
||||
<string name="videocall_room_input_error_tip">"房间号和用户名不能为空"</string>
|
||||
<string name="videocall_video_item">视频选项</string>
|
||||
<string name="videocall_audio_item">音频选项</string>
|
||||
<string name="videocall_user_back_camera">使用后置摄像头</string>
|
||||
<string name="videocall_user_front_camera">使用前置摄像头</string>
|
||||
<string name="videocall_open_camera">打开摄像头</string>
|
||||
<string name="videocall_close_camera">关闭摄像头</string>
|
||||
<string name="videocall_mute_audio">打开麦克风</string>
|
||||
<string name="videocall_close_mute_audio">关闭麦克风</string>
|
||||
<string name="videocall_use_speaker">使用扬声器</string>
|
||||
<string name="videocall_use_receiver">使用听筒</string>
|
||||
<string name="videocall_enter_room">进入房间</string>
|
||||
<string name="videocall_please_input_userid">请输入用户名(必填项)</string>
|
||||
<string name="videocall_please_input_roomid">请输入房间号(必填项)</string>
|
||||
<string name="videocall_roomid">房间号:</string>
|
||||
</resources>
|
13
src/android/res/values/styles.xml
Normal file
@ -0,0 +1,13 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
73
src/ios/Trtc/Info.plist
Normal file
@ -0,0 +1,73 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Editor</string>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>trtcdayisheng</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>trtcdayisheng</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string></string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>请点击“好”以允许访问。
|
||||
</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>请点击“好”以允许访问。
|
||||
</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>audio</string>
|
||||
</array>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
15
src/ios/Trtc/TCLiveConfigDefine.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// TCLiveConfigDefine.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by xiaowei li on 2018/6/22.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef TCLiveConfigDefine_h
|
||||
#define TCLiveConfigDefine_h
|
||||
|
||||
#define Login_Info_Url @"https://xzb.qcloud.com/webrtc/weapp/webrtc_room/get_login_info" //业务后台登录信息拉取,可替换为自己的业务后台
|
||||
#define AuthBuffer_Info_Url @"https://xzb.qcloud.com/webrtc/weapp/webrtc_room/get_privatemapkey"
|
||||
#define Default_Role @"ed640" //用户角色配置画面参数,可在控制台进行配置 https://cloud.tencent.com/document/product/647/17308
|
||||
#endif /* TCLiveConfigDefine_h */
|
17
src/ios/Trtc/TCLiveJoinRoomViewController.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// ViewController.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/5/31.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TCLiveJoinRoomViewController : UIViewController
|
||||
|
||||
@property (nonatomic) NSString *defaultRoomId;
|
||||
|
||||
@end
|
||||
|
||||
|
112
src/ios/Trtc/TCLiveJoinRoomViewController.m
Normal file
@ -0,0 +1,112 @@
|
||||
//
|
||||
// ViewController.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/5/31.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TCLiveJoinRoomViewController.h"
|
||||
#import "UIColorEX.h"
|
||||
#import "TCLiveRoomViewController.h"
|
||||
#import "UIToastView.h"
|
||||
#import "TCLiveConfigDefine.h"
|
||||
|
||||
@interface TCLiveJoinRoomViewController () <UITextFieldDelegate>
|
||||
@property(nonatomic,strong)UITextField *inputTextField;
|
||||
@property(nonatomic,strong)UIButton *joinRoomBtn;
|
||||
@property(nonatomic,strong) UIView *botoomLine;
|
||||
@property(nonatomic,strong) UIImageView *bgImageView;
|
||||
@end
|
||||
|
||||
@implementation TCLiveJoinRoomViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
self.title = @"视频通话";
|
||||
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
|
||||
[self.view addSubview:self.bgImageView];
|
||||
|
||||
[self.view addSubview:self.inputTextField];
|
||||
|
||||
[self.view addSubview:self.joinRoomBtn];
|
||||
|
||||
}
|
||||
-(void)viewWillAppear:(BOOL)animated{
|
||||
if (self.defaultRoomId) {
|
||||
self.inputTextField.text = self.defaultRoomId;
|
||||
}
|
||||
[super viewWillAppear:animated];
|
||||
[self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor]}];
|
||||
}
|
||||
-(UIImageView *)bgImageView{
|
||||
if (!_bgImageView) {
|
||||
_bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
|
||||
[_bgImageView setImage:[UIImage imageNamed:@"bg.png"]];
|
||||
_bgImageView.userInteractionEnabled = YES;
|
||||
}
|
||||
return _bgImageView;
|
||||
}
|
||||
|
||||
- (UIButton *)joinRoomBtn{
|
||||
if (!_joinRoomBtn) {
|
||||
_joinRoomBtn = [[UIButton alloc] initWithFrame:CGRectMake(20, _inputTextField.frame.size.height + _inputTextField.frame.origin.y + 50, self.view.frame.size.width - 40, 50)];
|
||||
_joinRoomBtn.layer.cornerRadius = 25;
|
||||
[_joinRoomBtn setTitle:@"加入房间" forState:UIControlStateNormal];
|
||||
_joinRoomBtn.backgroundColor = [UIColor colorWithRGBHex:0x1472fc];
|
||||
[_joinRoomBtn addTarget:self action:@selector(joinRoomBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[_joinRoomBtn setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
}
|
||||
return _joinRoomBtn;
|
||||
}
|
||||
- (UITextField *)inputTextField{
|
||||
if (!_inputTextField) {
|
||||
_inputTextField = [[UITextField alloc] initWithFrame:CGRectMake(5, 20 , self.view.frame.size.width-10, 40)];
|
||||
_inputTextField.delegate = self;
|
||||
_inputTextField.backgroundColor= [UIColor clearColor];
|
||||
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:@"请输入房间号码" attributes:@{NSForegroundColorAttributeName:[UIColor grayColor]}];
|
||||
_inputTextField.attributedPlaceholder = str;
|
||||
_inputTextField.textColor = [UIColor grayColor];
|
||||
_inputTextField.returnKeyType = UIReturnKeyDone;
|
||||
_inputTextField.keyboardType = UIKeyboardTypeNumberPad;
|
||||
|
||||
_botoomLine = [[UIView alloc] initWithFrame:CGRectMake(0, 40-1, _inputTextField.frame.size.width, 1)];
|
||||
_botoomLine.backgroundColor = [UIColor colorWithRGBHex:0x1472fc];
|
||||
[_inputTextField addSubview:_botoomLine];
|
||||
}
|
||||
return _inputTextField;
|
||||
}
|
||||
|
||||
- (void)joinRoomBtnClick:(UIButton *)sender{
|
||||
if (self.inputTextField.text.length > 0) {
|
||||
TCLiveRoomViewController *vc = [[TCLiveRoomViewController alloc] initWithRoomID:self.inputTextField.text role:Default_Role];
|
||||
[self.navigationController pushViewController:vc animated:YES];
|
||||
}
|
||||
else{
|
||||
[[UIToastView getInstance] showToastWithMessage:@"请输入房间号" toastMode:UIToastShowMode_fail];
|
||||
}
|
||||
}
|
||||
|
||||
// 自动跳转
|
||||
//- (void)autoNav:(NSString *)roomId {
|
||||
// TCLiveRoomViewController *vc = [[TCLiveRoomViewController alloc] initWithRoomID:roomId role:Default_Role];
|
||||
// [self.navigationController pushViewController:vc animated:YES];
|
||||
//}
|
||||
|
||||
|
||||
#pragma mark - UITextFieldDelegate
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
|
||||
[textField resignFirstResponder];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
|
||||
{
|
||||
NSCharacterSet *cs = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789"] invertedSet];
|
||||
NSString *filtered = [[string componentsSeparatedByCharactersInSet:cs] componentsJoinedByString:@""];
|
||||
return [string isEqualToString:filtered];
|
||||
}
|
||||
@end
|
21
src/ios/Trtc/TCLiveRequestManager.h
Normal file
@ -0,0 +1,21 @@
|
||||
//
|
||||
// TCLiveRequestManager.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/5/31.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^LiveLoginInfoBlock)(int code);
|
||||
typedef void (^LiveAuthBufferBlock)(NSDictionary *info);
|
||||
@interface TCLiveRequestManager : NSObject
|
||||
@property(nonatomic,assign)int sdkAppID; //app标识,可在实时音视频控制台(https://console.cloud.tencent.com/rav)创建自己的应用生成
|
||||
@property(nonatomic,assign)int accountType; //登录实时音视频应用的帐号类型,在控制台创建应用后分配
|
||||
@property(nonatomic,strong)NSString *userID; //用户id标识(可由业务后台自己管理)
|
||||
@property(nonatomic,strong)NSString *userSig; //用于用户鉴权,生成方法https://cloud.tencent.com/document/product/647/17275 (可由业务后台自己管理)
|
||||
+ (TCLiveRequestManager *)getInstance;
|
||||
- (void)requestLoginInfo:(LiveLoginInfoBlock)block;
|
||||
- (void)reqGetAuthBufferInfoWithParams:(NSDictionary *)params block:(LiveAuthBufferBlock)block;
|
||||
@end
|
98
src/ios/Trtc/TCLiveRequestManager.m
Normal file
@ -0,0 +1,98 @@
|
||||
//
|
||||
// TCLiveRequestManager.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/5/31.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TCLiveRequestManager.h"
|
||||
#import "UIToastView.h"
|
||||
#import "TCLiveConfigDefine.h"
|
||||
@implementation TCLiveRequestManager
|
||||
+ (TCLiveRequestManager *)getInstance{
|
||||
static TCLiveRequestManager *singleTon = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
singleTon = [TCLiveRequestManager new];
|
||||
});
|
||||
return singleTon;
|
||||
}
|
||||
|
||||
- (void)requestLoginInfo:(LiveLoginInfoBlock)block{
|
||||
NSString *user = [[NSUserDefaults standardUserDefaults] objectForKey:@"TCLIVE_USER"];
|
||||
if (user.length == 0) {
|
||||
user = @"";
|
||||
}
|
||||
NSDictionary *params = @{@"userID":user};
|
||||
NSMutableURLRequest *request = [self getSendPostRequest:Login_Info_Url body:params];//加备注
|
||||
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
[sessionConfig setTimeoutIntervalForRequest:30];
|
||||
|
||||
__weak TCLiveRequestManager *weakself = self;
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
if (error|| data == nil) {
|
||||
block(-1);
|
||||
[[UIToastView getInstance] showToastWithMessage:@"登录请求失败" toastMode:UIToastShowMode_fail];
|
||||
}
|
||||
else{
|
||||
//无error data解不出
|
||||
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
if (info) {
|
||||
weakself.sdkAppID = [info[@"sdkAppID"] intValue];
|
||||
weakself.accountType = [info[@"accountType"]intValue];
|
||||
weakself.userID = info[@"userID"];
|
||||
weakself.userSig = info[@"userSig"];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:info[@"userID"] forKey:@"TCLIVE_USER"];
|
||||
block(0);
|
||||
}
|
||||
else{
|
||||
block(-1);
|
||||
[[UIToastView getInstance] showToastWithMessage:@"登录信息解包失败" toastMode:UIToastShowMode_fail];
|
||||
}
|
||||
}
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
-(void)reqGetAuthBufferInfoWithParams:(NSDictionary *)params block:(LiveAuthBufferBlock)block{
|
||||
NSMutableURLRequest *request = [self getSendPostRequest:AuthBuffer_Info_Url body:params];
|
||||
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
[sessionConfig setTimeoutIntervalForRequest:30];
|
||||
|
||||
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
if (error|| data == nil) {
|
||||
[[UIToastView getInstance] showToastWithMessage:@"获取authBuffer请求失败" toastMode:UIToastShowMode_fail];
|
||||
}
|
||||
else{
|
||||
//无error data解不出
|
||||
NSDictionary *info = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
||||
if (info) {
|
||||
block(info);
|
||||
}
|
||||
else{
|
||||
[[UIToastView getInstance] showToastWithMessage:@"获取authBuffer解包失败" toastMode:UIToastShowMode_fail];
|
||||
}
|
||||
}
|
||||
}];
|
||||
[task resume];
|
||||
}
|
||||
|
||||
- (NSMutableURLRequest *)getSendPostRequest:(NSString *)url body:(NSDictionary *)body{
|
||||
|
||||
NSData *dataBody = [NSJSONSerialization dataWithJSONObject:body options:NSJSONWritingPrettyPrinted error:nil];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:url] cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:10];
|
||||
[request setValue:[NSString stringWithFormat:@"%ld", (long) [dataBody length]] forHTTPHeaderField:@"Content-Length"];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
[request setValue:@"application/json; charset=UTF-8" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
|
||||
[request setHTTPBody:dataBody];
|
||||
|
||||
return request;
|
||||
}
|
||||
@end
|
||||
|
17
src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.h
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// TCLiveChatTableView.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/3.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ImSDK/ImSDK.h>
|
||||
|
||||
@interface TCLiveChatTableView : UITableView
|
||||
//添加信息到聊天列表
|
||||
- (void)addChatMessage:(NSArray *)msgList withContentColor:(UIColor *)contentColor nickColor:(UIColor *)nickColor;
|
||||
//发送信息
|
||||
- (void)sendMessage:(NSString *)message;
|
||||
@end
|
177
src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableView.m
Normal file
@ -0,0 +1,177 @@
|
||||
//
|
||||
// TCLiveChatTableView.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/3.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <ImSDK/ImSDK.h>
|
||||
#import <ILiveSDK/ILiveSDK.h>
|
||||
#import <ILiveSDK/ILiveRoomManager.h>
|
||||
#import "TCLiveChatTableView.h"
|
||||
#import "TCLiveChatTableViewCell.h"
|
||||
#import "UIColorEX.h"
|
||||
#import "UIToastView.h"
|
||||
#import "TCLiveRequestManager.h"
|
||||
|
||||
@interface TCLiveChatTableView () <UITableViewDelegate,UITableViewDataSource,TIMMessageListener>
|
||||
@property(nonatomic,strong)NSMutableArray *chatMessageList;
|
||||
@property(nonatomic,strong)UIColor *contentColor;
|
||||
@property(nonatomic,strong)UIColor *nickColor;
|
||||
@end
|
||||
|
||||
@implementation TCLiveChatTableView
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame style:(UITableViewStyle)style{
|
||||
if (self = [super initWithFrame:frame style:style]) {
|
||||
_chatMessageList = [[NSMutableArray alloc] initWithCapacity:1];
|
||||
self.delegate = self;
|
||||
self.dataSource = self;
|
||||
self.separatorStyle = UITableViewCellSeparatorStyleNone;
|
||||
self.transform = CGAffineTransformMakeScale(1, -1);
|
||||
self.layer.cornerRadius = 4;
|
||||
self.layer.masksToBounds = YES;
|
||||
self.scrollEnabled = NO;
|
||||
//设置消息监听
|
||||
[[[ILiveSDK getInstance] getTIMManager] setMessageListener:self];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//接收消息
|
||||
-(void)onNewMessage:(NSArray *)msgs{
|
||||
[self addChatMessage:msgs withContentColor:nil nickColor:nil];
|
||||
}
|
||||
//消息处理
|
||||
- (void)addChatMessage:(NSArray *)msgList withContentColor:(UIColor *)contentColor nickColor:(UIColor *)nickColor{
|
||||
self.contentColor = contentColor;
|
||||
self.nickColor = nickColor;
|
||||
for (id item in msgList) {
|
||||
[self.chatMessageList insertObject:item atIndex:0];
|
||||
}
|
||||
//过滤非文本消息
|
||||
NSMutableArray *tempArr = [NSMutableArray array];
|
||||
for (int i = 0; i < msgList.count;i++) {
|
||||
TIMMessage *msg = msgList[i];
|
||||
if (![self isTextMsg:msg]) {
|
||||
[tempArr addObject:msg];
|
||||
}
|
||||
}
|
||||
[self.chatMessageList removeObjectsInArray:tempArr];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self reloadData];
|
||||
});
|
||||
}
|
||||
- (BOOL)isTextMsg:(TIMMessage *)msg {
|
||||
TIMOfflinePushInfo *info = msg.getOfflinePushInfo;
|
||||
if ([info.ext hasPrefix:@"TEXT"]) {
|
||||
return YES;
|
||||
}
|
||||
int count = [msg elemCount];
|
||||
for(int i = 0; i < count; i++) {
|
||||
TIMElem *elem = [msg getElem:i];
|
||||
if ([elem isKindOfClass:[TIMCustomElem class]]){
|
||||
if ([((TIMCustomElem*)elem).ext hasPrefix:@"TEXT"]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
else if ([elem isKindOfClass:[TIMTextElem class]]){
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
//发送消息
|
||||
- (void)sendMessage:(NSString *)message{
|
||||
//消息组装
|
||||
TIMMessage *msge = [[TIMMessage alloc] init];
|
||||
TIMCustomElem *textElem = [[TIMCustomElem alloc] init];
|
||||
textElem.data = [message dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary *descDic = @{@"nickName":[TCLiveRequestManager getInstance].userID};
|
||||
NSString *desc = [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:descDic options:NSJSONReadingAllowFragments error:nil] encoding:NSUTF8StringEncoding];
|
||||
textElem.desc = desc;
|
||||
textElem.ext = @"TEXT";
|
||||
[msge addElem:textElem];
|
||||
//调用发送接口
|
||||
[[ILiveRoomManager getInstance] sendGroupMessage:msge succ:^{
|
||||
NSLog(@"send message succ");
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"send message fail");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"发送消息失败" toastMode:UIToastShowMode_fail];
|
||||
}];
|
||||
[self addChatMessage:@[msge] withContentColor:nil nickColor:nil];
|
||||
}
|
||||
#pragma mark - UITableViewDelegate
|
||||
|
||||
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
|
||||
TCLiveChatTableViewCell *cell = [self createChatTableViewCell:tableView withIndexPath:indexPath];
|
||||
return cell.cellHeight;
|
||||
}
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
|
||||
return self.chatMessageList.count;
|
||||
}
|
||||
|
||||
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
|
||||
TCLiveChatTableViewCell *cell = [self createChatTableViewCell:tableView withIndexPath:indexPath];
|
||||
return cell;
|
||||
}
|
||||
//创建消息cell
|
||||
- (TCLiveChatTableViewCell *)createChatTableViewCell:(UITableView *)tableView withIndexPath:(NSIndexPath *)indexPath{
|
||||
TCLiveChatTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ChatTableviewCell"];
|
||||
if (!cell) {
|
||||
cell = [[TCLiveChatTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ChatTableviewCell"];
|
||||
cell.selectionStyle = UITableViewCellSelectionStyleNone;
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
cell.contentView.transform = CGAffineTransformMakeScale (1,-1);
|
||||
}
|
||||
TIMMessage *msg = self.chatMessageList[indexPath.row];
|
||||
int count = [msg elemCount];
|
||||
for(int i = 0; i < count; i++) {
|
||||
TIMElem *elem = [msg getElem:i];
|
||||
//收到消息展示
|
||||
NSMutableAttributedString *msgInfo = [[NSMutableAttributedString alloc] initWithString:@""];
|
||||
if([elem isKindOfClass:[TIMTextElem class]]){
|
||||
msgInfo = [self getContentWithNick:msg.sender andContentTex:((TIMTextElem *)elem).text];
|
||||
[cell setModel:msgInfo];
|
||||
break;
|
||||
}
|
||||
else if ([elem isKindOfClass:[TIMCustomElem class]]){
|
||||
NSString *nick = msg.sender;
|
||||
NSString *dataStr = [[NSString alloc] initWithData:((TIMCustomElem *)elem).data encoding:NSUTF8StringEncoding];
|
||||
NSDictionary *descDic = [NSJSONSerialization JSONObjectWithData:[((TIMCustomElem *)elem).desc dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:nil];
|
||||
NSString *nickNmae = descDic[@"nickName"];
|
||||
if (nickNmae.length > 0) {
|
||||
nick = nickNmae;
|
||||
}
|
||||
msgInfo = [self getContentWithNick:nick andContentTex:dataStr];
|
||||
[cell setModel:msgInfo];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
- (NSMutableAttributedString *)getContentWithNick:(NSString *)nick andContentTex:(NSString *)contentText{
|
||||
NSString *content = [NSString stringWithFormat:@"%@:%@",nick, contentText];
|
||||
NSMutableAttributedString *msgInfo = [[NSMutableAttributedString alloc] initWithString:content];
|
||||
UIColor *contentColor = [UIColor whiteColor];
|
||||
UIColor *nickColor = [UIColor colorWithRGBHex:0xFF4081];
|
||||
if (self.contentColor) {
|
||||
contentColor = self.contentColor;
|
||||
}
|
||||
if(self.nickColor){
|
||||
nickColor = self.nickColor;
|
||||
}
|
||||
[msgInfo addAttribute:NSForegroundColorAttributeName value:contentColor range:[content rangeOfString:contentText]];
|
||||
[msgInfo addAttribute:NSForegroundColorAttributeName value:nickColor range:[content rangeOfString:nick]];
|
||||
|
||||
return msgInfo;
|
||||
}
|
||||
//点击事件透传
|
||||
-(UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
|
||||
return nil;
|
||||
}
|
||||
@end
|
14
src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.h
Normal file
@ -0,0 +1,14 @@
|
||||
//
|
||||
// TCLiveChatTableViewCell.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/8.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TCLiveChatTableViewCell : UITableViewCell
|
||||
@property(nonatomic,assign)CGFloat cellHeight;
|
||||
-(void)setModel:(NSMutableAttributedString *)model;
|
||||
@end
|
59
src/ios/Trtc/TCLiveRoomVC/TCLiveChatTableViewCell.m
Normal file
@ -0,0 +1,59 @@
|
||||
//
|
||||
// TCLiveChatTableViewCell.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/8.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
#import "TCLiveChatTableViewCell.h"
|
||||
|
||||
@interface TCLiveChatTableViewCell ()
|
||||
@property(nonatomic,strong) UILabel *contentLabel;
|
||||
@property(nonatomic,strong) UIView *backMaskView;
|
||||
@end
|
||||
|
||||
@implementation TCLiveChatTableViewCell
|
||||
|
||||
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
|
||||
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
|
||||
|
||||
[self.contentView addSubview:self.backMaskView];
|
||||
[_backMaskView addSubview:self.contentLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(UILabel *)contentLabel{
|
||||
if (!_contentLabel) {
|
||||
_contentLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_contentLabel.backgroundColor = [UIColor clearColor];
|
||||
_contentLabel.textAlignment = NSTextAlignmentLeft;
|
||||
_contentLabel.font = [UIFont systemFontOfSize:13];
|
||||
_contentLabel.textColor = [UIColor whiteColor];
|
||||
_contentLabel.numberOfLines = 0;
|
||||
}
|
||||
return _contentLabel;
|
||||
}
|
||||
|
||||
-(UIView *)backMaskView{
|
||||
if (!_backMaskView) {
|
||||
_backMaskView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
_backMaskView.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
|
||||
_backMaskView.layer.cornerRadius = 10;
|
||||
_backMaskView.layer.masksToBounds = YES;
|
||||
}
|
||||
return _backMaskView;
|
||||
}
|
||||
|
||||
-(void)setModel:(NSMutableAttributedString *)model{
|
||||
_contentLabel.attributedText = model;
|
||||
CGSize size = [_contentLabel sizeThatFits:CGSizeMake(230, 10000)];
|
||||
|
||||
_contentLabel.frame = CGRectMake(5, 5, size.width, size.height );
|
||||
_backMaskView.frame = CGRectMake(5, 5, size.width + 10, size.height + 10);
|
||||
self.cellHeight = size.height+20;
|
||||
}
|
||||
|
||||
@end
|
15
src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.h
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// TCLiveRoomViewController.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/5/31.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "TCLiveVideoLayoutView.h"
|
||||
|
||||
@interface TCLiveRoomViewController : UIViewController
|
||||
//传入roomid(房间号)进入指定房间 设置role配置画面参数
|
||||
-(instancetype)initWithRoomID:(NSString *)roomid role:(NSString *)role;
|
||||
@end
|
275
src/ios/Trtc/TCLiveRoomVC/TCLiveRoomViewController.m
Normal file
@ -0,0 +1,275 @@
|
||||
//
|
||||
// TCLiveRoomViewController.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/5/31.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TCLiveRoomViewController.h"
|
||||
#import <ILiveSDK/ILiveRoomOption.h>
|
||||
#import <ILiveSDK/ILiveRoomManager.h>
|
||||
#import <ILiveSDK/ILiveRenderView.h>
|
||||
#import <ILiveSDK/ILiveFrameDispatcher.h>
|
||||
#import <TILLiveSDK/TILLiveManager.h>
|
||||
#import <QAVSDK/QAVCommon.h>
|
||||
#import <ImSDK/ImSDK.h>
|
||||
#import "TCLiveRequestManager.h"
|
||||
#import "TCLiveVideoControlBar.h"
|
||||
#import "TCLiveChatTableView.h"
|
||||
#import "UIToastView.h"
|
||||
|
||||
#define LIVE_VIEW_HEIGHT 370
|
||||
#define LIVE_CONTROL_BAR_HEIGHT 70
|
||||
#define LIVE_INPUTTEXTFIELD_HEIGHT 40
|
||||
|
||||
@interface TCLiveRoomViewController ()<UITextFieldDelegate,TCLiveVideoControlBarDelegate>
|
||||
@property(nonatomic,strong) TCLiveVideoLayoutView *videoLayoutView;
|
||||
@property(nonatomic,strong) TCLiveVideoControlBar *controlBar;
|
||||
@property(nonatomic,strong) TCLiveChatTableView *chatTableView;
|
||||
@property(nonatomic,strong) UITextField *inputTextField;
|
||||
@property(nonatomic,strong) NSString *roomID;
|
||||
@property(nonatomic,strong) UIImageView *bgImageView;
|
||||
@property(nonatomic,strong) NSTimer *logTimer;
|
||||
@property(nonatomic,strong) NSTimer *heartBeatTimer;
|
||||
@property(nonatomic,assign) CGRect origInputTextFieldFrame;
|
||||
@property(nonatomic,assign) CGRect origChatTableViewFrame;
|
||||
@property(nonatomic,strong) NSString *role;
|
||||
@end
|
||||
|
||||
@implementation TCLiveRoomViewController
|
||||
|
||||
-(instancetype)initWithRoomID:(NSString *)roomid role:(NSString *)role{
|
||||
if (self = [super init]) {
|
||||
self.roomID = roomid;
|
||||
self.role = role;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
self.title = self.roomID;
|
||||
[self.navigationController.navigationBar setTitleTextAttributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];
|
||||
|
||||
[self enterRoom];
|
||||
|
||||
[self customLeftButton];
|
||||
|
||||
[self.view addSubview:self.bgImageView];
|
||||
|
||||
[_bgImageView addSubview:self.videoLayoutView];
|
||||
|
||||
[_bgImageView addSubview:self.chatTableView];
|
||||
|
||||
[_bgImageView addSubview:self.controlBar];
|
||||
|
||||
[_bgImageView addSubview:self.inputTextField];
|
||||
|
||||
//监听键盘变化
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nil];
|
||||
}
|
||||
-(void)viewWillAppear:(BOOL)animated{
|
||||
[super viewWillAppear:animated];
|
||||
//导航栏透明化
|
||||
[self setNavigationBarTransparent];
|
||||
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated{
|
||||
[super viewWillDisappear:animated];
|
||||
[self.navigationController.navigationBar setTranslucent:NO];
|
||||
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
|
||||
}
|
||||
- (void)setNavigationBarTransparent{
|
||||
[self.navigationController.navigationBar setTranslucent:YES];
|
||||
[self.navigationController.navigationBar setBackgroundImage:[[UIImage alloc]init] forBarMetrics:UIBarMetricsDefault];
|
||||
[self.navigationController.navigationBar setShadowImage:[[UIImage alloc]init]];
|
||||
[self.navigationController.navigationBar setBackgroundColor:[UIColor clearColor]];
|
||||
}
|
||||
|
||||
- (void)enterRoom{
|
||||
[[TCLiveRequestManager getInstance] reqGetAuthBufferInfoWithParams:@{@"roomID":self.roomID,@"userID":[TCLiveRequestManager getInstance].userID} block:^(NSDictionary *info) {
|
||||
ILiveRoomOption *option = [ILiveRoomOption defaultHostLiveOption];
|
||||
option.imOption.imSupport = YES;
|
||||
option.memberStatusListener = self.videoLayoutView;
|
||||
option.roomDisconnectListener = self;
|
||||
option.controlRole = self.role;
|
||||
option.avOption.privateMapKey = [info[@"privateMapKey"] dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
[[ILiveRoomManager getInstance] createRoom:[self.roomID intValue] option:option succ:^{
|
||||
NSLog(@"-----> create room succ");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"创建房间成功" toastMode:UIToastShowMode_Succ];
|
||||
[self.controlBar enableBeauty:YES];//进入房间默认开启美颜
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
if(errId == 10021){
|
||||
//表示房间已存在直接加入房间
|
||||
[[ILiveRoomManager getInstance] joinRoom:[self.roomID intValue] option:option succ:^{
|
||||
NSLog(@"-----> join room succ");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"加入房间成功" toastMode:UIToastShowMode_Succ];
|
||||
[self.controlBar enableBeauty:YES];//进入房间默认开启美颜
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"-----> join room fail,%@ %d %@",module, errId, errMsg);
|
||||
[[UIToastView getInstance] showToastWithMessage:errMsg toastMode:UIToastShowMode_fail];
|
||||
}];
|
||||
}
|
||||
else{
|
||||
NSLog(@"-----> create room fail,%@ %d %@",module, errId, errMsg);
|
||||
[[UIToastView getInstance] showToastWithMessage:errMsg toastMode:UIToastShowMode_fail];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)customLeftButton{
|
||||
UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
backBtn.frame = CGRectMake(0, 0, 44, 44);
|
||||
[backBtn setImage:[UIImage imageNamed:@"ui_title_arrow_left.png"] forState:UIControlStateNormal];
|
||||
[backBtn addTarget:self action:@selector(backBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
|
||||
backBtn.frame = CGRectMake(0, 0, 44, 44);
|
||||
[backBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
|
||||
backBtn.imageEdgeInsets = UIEdgeInsetsMake(10, 0, 10, 20);
|
||||
UIBarButtonItem *item = [[UIBarButtonItem alloc]initWithCustomView:backBtn];
|
||||
self.navigationItem.leftBarButtonItem = item;
|
||||
}
|
||||
|
||||
- (UIImageView *)bgImageView{
|
||||
if (!_bgImageView) {
|
||||
_bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
|
||||
[_bgImageView setImage:[UIImage imageNamed:@"bg.png"]];
|
||||
_bgImageView.userInteractionEnabled = YES;
|
||||
}
|
||||
return _bgImageView;
|
||||
}
|
||||
|
||||
//视频区域
|
||||
-(TCLiveVideoLayoutView *)videoLayoutView{
|
||||
if (!_videoLayoutView) {
|
||||
_videoLayoutView = [[TCLiveVideoLayoutView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
|
||||
}
|
||||
return _videoLayoutView;
|
||||
}
|
||||
//控制bar
|
||||
- (TCLiveVideoControlBar *)controlBar{
|
||||
if (!_controlBar) {
|
||||
_controlBar = [[TCLiveVideoControlBar alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - LIVE_CONTROL_BAR_HEIGHT, self.view.frame.size.width, LIVE_CONTROL_BAR_HEIGHT)];
|
||||
_controlBar.delegate = self;
|
||||
}
|
||||
return _controlBar;
|
||||
}
|
||||
//消息列表
|
||||
- (UITableView *)chatTableView{
|
||||
if (!_chatTableView) {
|
||||
_chatTableView = [[TCLiveChatTableView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - LIVE_CONTROL_BAR_HEIGHT - 400, 250, 400) style:UITableViewStylePlain];
|
||||
_chatTableView.backgroundColor = [UIColor clearColor];
|
||||
self.origChatTableViewFrame = self.chatTableView.frame;
|
||||
}
|
||||
return _chatTableView;
|
||||
}
|
||||
//输入框
|
||||
- (UITextField *)inputTextField{
|
||||
if (!_inputTextField) {
|
||||
_inputTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height , self.view.frame.size.width, LIVE_INPUTTEXTFIELD_HEIGHT)];
|
||||
_inputTextField.delegate = self;
|
||||
_inputTextField.backgroundColor= [[UIColor whiteColor] colorWithAlphaComponent:0.9];
|
||||
_inputTextField.placeholder = @"请输入内容";
|
||||
_inputTextField.returnKeyType = UIReturnKeySend;
|
||||
self.origInputTextFieldFrame = self.inputTextField.frame;
|
||||
}
|
||||
return _inputTextField;
|
||||
}
|
||||
//关闭界面退出房间
|
||||
- (void)backBtnClicked:(UIButton *)sender{
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
[[ILiveRoomManager getInstance] quitRoom:^{
|
||||
NSLog(@"-----> quit room succ");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"退出房间成功" toastMode:UIToastShowMode_Succ];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"-----> quit room fail,%@ %d %@",module, errId, errMsg);
|
||||
[[UIToastView getInstance] showToastWithMessage:@"退出房间失败" toastMode:UIToastShowMode_fail];
|
||||
}];
|
||||
[_logTimer invalidate];
|
||||
_logTimer = nil;
|
||||
|
||||
}
|
||||
#pragma mark - ILiveRoomDisconnectListener
|
||||
- (BOOL)onRoomDisconnect:(int)reason;{
|
||||
[self backBtnClicked:nil];
|
||||
return YES;
|
||||
}
|
||||
#pragma mark - UITextFieldDelegate
|
||||
|
||||
- (BOOL)textFieldShouldReturn:(UITextField *)textField;{
|
||||
[textField resignFirstResponder];
|
||||
NSString *text = textField.text;
|
||||
if (text.length > 0) {
|
||||
[self.chatTableView sendMessage:text];
|
||||
textField.text = nil;
|
||||
}
|
||||
else{
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
#pragma mark - TCLiveVideoControlBarDelegate
|
||||
-(void)logBtnClick:(UIButton *)sender{
|
||||
if (sender.selected) {
|
||||
if (!_logTimer) {
|
||||
[self logUpdate];
|
||||
_logTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(logUpdate) userInfo:nil repeats:YES];
|
||||
}
|
||||
}
|
||||
else{
|
||||
[_logTimer invalidate];
|
||||
_logTimer = nil;
|
||||
[_videoLayoutView closeLogView];
|
||||
}
|
||||
}
|
||||
- (void)chatBtnClick:(UIButton *)sender{
|
||||
[self.inputTextField becomeFirstResponder];
|
||||
}
|
||||
- (void)logUpdate{
|
||||
QAVContext *avContext = [[ILiveSDK getInstance] getAVContext];
|
||||
NSString *qualityStr = [avContext.room getQualityTips];
|
||||
[_videoLayoutView showLogView:qualityStr];
|
||||
}
|
||||
|
||||
- (void)beautyBtnClick:(UIButton *)sender{
|
||||
if (sender.selected) {
|
||||
[self.videoLayoutView setBeautyLevel:9];
|
||||
}
|
||||
else{
|
||||
[self.videoLayoutView setBeautyLevel:0];
|
||||
}
|
||||
}
|
||||
#pragma mark - 键盘事件
|
||||
- (void)keybaordAnimationWithDuration:(CGFloat)duration keyboardOriginY:(CGFloat)keyboardOriginY{
|
||||
|
||||
__block TCLiveRoomViewController *blockSelf = self;
|
||||
//作为视图的键盘,弹出动画也是UIViewAnimationOptionCurveEaseIn的方式
|
||||
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
|
||||
//text field
|
||||
if(keyboardOriginY == blockSelf.view.frame.size.height){
|
||||
blockSelf.inputTextField.frame = blockSelf.origInputTextFieldFrame;
|
||||
blockSelf.chatTableView.frame = blockSelf.origChatTableViewFrame;
|
||||
}
|
||||
else{
|
||||
blockSelf.inputTextField.frame = CGRectMake(blockSelf.inputTextField.frame.origin.x,keyboardOriginY - blockSelf.inputTextField.frame.size.height, blockSelf.inputTextField.frame.size.width, blockSelf.inputTextField.frame.size.height);
|
||||
blockSelf.chatTableView.frame = CGRectMake(blockSelf.chatTableView.frame.origin.x,keyboardOriginY - blockSelf.chatTableView.frame.size.height - LIVE_INPUTTEXTFIELD_HEIGHT, blockSelf.chatTableView.frame.size.width, blockSelf.chatTableView.frame.size.height);
|
||||
}
|
||||
|
||||
} completion:nil];
|
||||
}
|
||||
- (void)keyboardWillChangeFrame:(NSNotification *)notify{
|
||||
NSDictionary * info = notify.userInfo;
|
||||
//动画时间
|
||||
CGFloat animationDuration = [info[UIKeyboardAnimationDurationUserInfoKey] floatValue];
|
||||
//键盘目标位置
|
||||
CGRect keyboardAimFrame = [info[UIKeyboardFrameEndUserInfoKey] CGRectValue];
|
||||
if ([self.inputTextField isFirstResponder]) {
|
||||
[self keybaordAnimationWithDuration:animationDuration keyboardOriginY:keyboardAimFrame.origin.y];
|
||||
}
|
||||
}
|
||||
@end
|
13
src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// UIColorEX.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/5.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIColor (EX)
|
||||
+ (UIColor *)colorWithRGBHex: (unsigned int)hex;
|
||||
@end
|
25
src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIColorEX.m
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// UIColorEX.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/5.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIColorEX.h"
|
||||
|
||||
@implementation UIColor (EX)
|
||||
|
||||
+ (UIColor *)colorWithRGBHex: (unsigned int)hex
|
||||
{
|
||||
int r = (hex >> 16) & 0xFF;
|
||||
int g = (hex >> 8) & 0xFF;
|
||||
int b = (hex) & 0xFF;
|
||||
|
||||
return [UIColor colorWithRed:r / 255.0f
|
||||
green:g / 255.0f
|
||||
blue:b / 255.0f
|
||||
alpha:1.0f];
|
||||
}
|
||||
|
||||
@end
|
20
src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.h
Normal file
@ -0,0 +1,20 @@
|
||||
//
|
||||
// UIToastView.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/9.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
typedef enum {
|
||||
UIToastShowMode_Default = 1,
|
||||
UIToastShowMode_Succ,
|
||||
UIToastShowMode_fail,
|
||||
}UIToastShowMode;
|
||||
|
||||
@interface UIToastView : UIView
|
||||
+ (UIToastView *)getInstance;
|
||||
- (void)showToastWithMessage:(NSString *)text toastMode:(UIToastShowMode )mode;
|
||||
@end
|
96
src/ios/Trtc/TCLiveRoomVC/TCLiveUITools/UIToastView.m
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// UIToastView.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/9.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "UIToastView.h"
|
||||
|
||||
#define UITOAST_HEIGHT 30
|
||||
#define UITOAST_IMAGE_HEIGHT 15
|
||||
@interface UIToastView ()
|
||||
@property(nonatomic,strong)UILabel *toastLabel;
|
||||
@property(nonatomic,strong)UIImageView *toastImageView;
|
||||
@end
|
||||
|
||||
@implementation UIToastView
|
||||
|
||||
+ (UIToastView *)getInstance{
|
||||
static UIToastView *singleTon = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
singleTon = [UIToastView new];
|
||||
});
|
||||
return singleTon;
|
||||
}
|
||||
|
||||
-(instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5];
|
||||
self.layer.cornerRadius = 4;
|
||||
self.layer.masksToBounds = YES;
|
||||
|
||||
[self addSubview:self.toastImageView];
|
||||
[self addSubview:self.toastLabel];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (UILabel *)toastLabel{
|
||||
if (!_toastLabel) {
|
||||
_toastLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
||||
_toastLabel.numberOfLines = 1;
|
||||
_toastLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_toastLabel.textColor = [UIColor whiteColor];
|
||||
_toastLabel.font = [UIFont systemFontOfSize:16];
|
||||
}
|
||||
return _toastLabel;
|
||||
}
|
||||
|
||||
-(UIImageView *)toastImageView{
|
||||
if (!_toastImageView) {
|
||||
_toastImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
|
||||
|
||||
}
|
||||
return _toastImageView;
|
||||
}
|
||||
|
||||
- (void)showToastWithMessage:(NSString *)text toastMode:(UIToastShowMode )mode{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIToastView *view = [UIToastView new];
|
||||
CGRect rect = [text boundingRectWithSize:CGSizeMake([UIScreen mainScreen].bounds.size.width - 80, 20) options:NSStringDrawingUsesLineFragmentOrigin attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:16]} context:nil];
|
||||
view.frame = CGRectMake(0, 0, rect.size.width + 10 + 20, UITOAST_HEIGHT);
|
||||
view.toastImageView.frame = CGRectMake(5, 0, UITOAST_IMAGE_HEIGHT, UITOAST_IMAGE_HEIGHT);
|
||||
view.toastImageView.center = CGPointMake(view.toastImageView.center.x, view.frame.size.height/2);
|
||||
view.toastLabel.frame = CGRectMake(25, 0, rect.size.width, UITOAST_HEIGHT);
|
||||
view.toastLabel.text = text;
|
||||
if (UIToastShowMode_Succ == mode){
|
||||
view.toastImageView.image = [UIImage imageNamed:@"ic_toast_success@2x"];
|
||||
}
|
||||
else if(UIToastShowMode_fail == mode){
|
||||
view.toastImageView.image = [UIImage imageNamed:@"icon_sign@2x"];
|
||||
}
|
||||
else{
|
||||
view.toastImageView.frame = CGRectZero;
|
||||
view.frame = CGRectMake(0, 0, rect.size.width + 10 , UITOAST_HEIGHT);
|
||||
view.toastLabel.frame = CGRectMake(5, 0, rect.size.width, UITOAST_HEIGHT);
|
||||
}
|
||||
view.center = [[UIApplication sharedApplication] keyWindow].center;
|
||||
[[[UIApplication sharedApplication] keyWindow] addSubview:view];
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
view.alpha = 1;
|
||||
} completion:^(BOOL finished) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
view.alpha = 0;
|
||||
} completion:^(BOOL finished) {
|
||||
[view removeFromSuperview];
|
||||
}];
|
||||
});
|
||||
}];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@end
|
34
src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.h
Normal file
@ -0,0 +1,34 @@
|
||||
//
|
||||
// TCLiveVideoControlBar.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/3.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol TCLiveVideoControlBarDelegate <NSObject>
|
||||
@optional
|
||||
//切换摄像头
|
||||
- (void)switchCameraBtnClick:(UIButton *)sender;
|
||||
//开关美颜
|
||||
- (void)beautyBtnClick:(UIButton *)sender;
|
||||
//开关麦克风
|
||||
- (void)voiceBtnClick:(UIButton *)sender;
|
||||
//展示日志
|
||||
- (void)logBtnClick:(UIButton *)sender;
|
||||
//反馈
|
||||
- (void)feedBackBtnClick:(UIButton *)sender;
|
||||
//切换配置
|
||||
- (void)changeRoleBtnClick:(UIButton *)sender;
|
||||
//聊天
|
||||
- (void)chatBtnClick:(UIButton *)sender;
|
||||
@end
|
||||
|
||||
@interface TCLiveVideoControlBar : UIView
|
||||
@property(nonatomic,weak)id<TCLiveVideoControlBarDelegate> delegate;
|
||||
@property(nonatomic,strong) UIButton *logBtn;
|
||||
- (void)enableLog:(BOOL)endable;
|
||||
- (void)enableBeauty:(BOOL)enable;
|
||||
@end
|
298
src/ios/Trtc/TCLiveRoomVC/TCLiveVideoControlBar.m
Normal file
@ -0,0 +1,298 @@
|
||||
//
|
||||
// TCLiveVideoControlBar.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/3.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
|
||||
#import "TCLiveVideoControlBar.h"
|
||||
#import <ILiveSDK/ILiveRoomManager.h>
|
||||
#import <ILiveSDK/ILiveRenderView.h>
|
||||
#import <ILiveSDK/ILiveFrameDispatcher.h>
|
||||
#import "TCLiveRequestManager.h"
|
||||
#import "UIToastView.h"
|
||||
#import "TCLiveRoomViewController.h"
|
||||
|
||||
#define CONTROLBAR_BUTTON_WIDTH 50
|
||||
|
||||
@interface TCLiveVideoControlBar ()
|
||||
@property(nonatomic,strong) UIButton *chatBtn;
|
||||
@property(nonatomic,strong) UIButton *switchCamera;
|
||||
@property(nonatomic,strong) UIButton *beautyBtn;
|
||||
@property(nonatomic,strong) UIButton *voiceBtn;
|
||||
@property(nonatomic,strong) UIButton *changeRoleBtn;
|
||||
@property(nonatomic,strong) UIButton *feedBackBtn;
|
||||
@end
|
||||
|
||||
@implementation TCLiveVideoControlBar
|
||||
|
||||
-(instancetype)initWithFrame:(CGRect)frame
|
||||
{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
// [self addSubview:self.chatBtn];
|
||||
// [self addSubview:self.beautyBtn];
|
||||
[self addSubview:self.voiceBtn];
|
||||
// [self addSubview:self.logBtn];
|
||||
[self addSubview:self.switchCamera];
|
||||
// [self addSubview:self.feedBackBtn];
|
||||
// [self addSubview:self.changeRoleBtn];
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(UIButton *)chatBtn{
|
||||
if(!_chatBtn){
|
||||
_chatBtn = [self createCustomControlBtn:@"聊天" withImage:[UIImage imageNamed:@"chat.png"] selectedImage:nil];
|
||||
[_chatBtn addTarget:self action:@selector(chatBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
_chatBtn.frame = CGRectMake(self.frame.size.width / 2 - CONTROLBAR_BUTTON_WIDTH * 3.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
return _chatBtn;
|
||||
}
|
||||
-(UIButton *)switchCamera{
|
||||
if (!_switchCamera) {
|
||||
_switchCamera = [self createCustomControlBtn:@"翻转" withImage:[UIImage imageNamed:@"camera.png"] selectedImage:[UIImage imageNamed:@"camera-gray.png"]];
|
||||
[_switchCamera addTarget:self action:@selector(switchCameraClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
// _switchCamera.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH * 2.5,0,CONTROLBAR_BUTTON_WIDTH,self.frame.size.height);
|
||||
_switchCamera.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH * 1.5,0,CONTROLBAR_BUTTON_WIDTH,self.frame.size.height);
|
||||
return _switchCamera;
|
||||
}
|
||||
|
||||
- (UIButton *)beautyBtn{
|
||||
if (!_beautyBtn) {
|
||||
_beautyBtn = [self createCustomControlBtn:@"美颜" withImage:[UIImage imageNamed:@"beauty.png"] selectedImage:[UIImage imageNamed:@"beauty-dis.png"]];
|
||||
[_beautyBtn addTarget:self action:@selector(beautyBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
_beautyBtn.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH *1.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
return _beautyBtn;
|
||||
}
|
||||
|
||||
- (UIButton *)voiceBtn{
|
||||
if (!_voiceBtn) {
|
||||
_voiceBtn = [self createCustomControlBtn:@"声音" withImage:[UIImage imageNamed:@"mic-dis.png"] selectedImage:[UIImage imageNamed:@"mic.png"]];
|
||||
[_voiceBtn addTarget:self action:@selector(voiceBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
// _voiceBtn.frame = CGRectMake(self.frame.size.width/2 - CONTROLBAR_BUTTON_WIDTH * 0.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
_voiceBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 0.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
return _voiceBtn;
|
||||
}
|
||||
|
||||
-(UIButton *)changeRoleBtn{
|
||||
if (!_changeRoleBtn) {
|
||||
_changeRoleBtn = [self createCustomControlBtn:@"配置" withImage:[UIImage imageNamed:@"role.png"] selectedImage:[UIImage imageNamed:@"role.png"]];
|
||||
[_changeRoleBtn addTarget:self action:@selector(changeRoleBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
_changeRoleBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 0.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
return _changeRoleBtn;
|
||||
}
|
||||
|
||||
- (UIButton *)feedBackBtn{
|
||||
if (!_feedBackBtn) {
|
||||
_feedBackBtn = [self createCustomControlBtn:@"反馈" withImage:[UIImage imageNamed:@"feedback.png"] selectedImage:[UIImage imageNamed:@"feedback.png"]];
|
||||
[_feedBackBtn addTarget:self action:@selector(feedBackBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
_feedBackBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 1.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
return _feedBackBtn;
|
||||
}
|
||||
|
||||
- (UIButton *)logBtn{
|
||||
if (!_logBtn) {
|
||||
_logBtn = [self createCustomControlBtn:@"信息" withImage:[UIImage imageNamed:@"log.png"] selectedImage:[UIImage imageNamed:@"log2.png"]];
|
||||
[_logBtn addTarget:self action:@selector(logBtnClick:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
_logBtn.frame = CGRectMake(self.frame.size.width/2 + CONTROLBAR_BUTTON_WIDTH * 2.5, 0, CONTROLBAR_BUTTON_WIDTH, self.frame.size.height);
|
||||
return _logBtn;
|
||||
}
|
||||
|
||||
- (UIButton *)createCustomControlBtn:(NSString *)wording withImage:(UIImage *)image selectedImage:(UIImage *)highlightImage{
|
||||
UIButton *customButton = [[UIButton alloc] initWithFrame:CGRectZero];
|
||||
[customButton setTitle:wording forState:UIControlStateNormal];
|
||||
customButton.titleLabel.font = [UIFont systemFontOfSize:11];
|
||||
[customButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
[customButton setImage:image forState:UIControlStateNormal];
|
||||
[customButton setImage:highlightImage forState:UIControlStateSelected];
|
||||
CGFloat imageHeight = image.size.height;
|
||||
CGFloat imageWidth = image.size.width;
|
||||
[customButton setTitleEdgeInsets:UIEdgeInsetsMake(15, -60, -15, 0)];
|
||||
[customButton setImageEdgeInsets:UIEdgeInsetsMake(10, 10, self.frame.size.height - (CONTROLBAR_BUTTON_WIDTH - 20)/imageWidth *imageHeight -10, 10)];
|
||||
return customButton;
|
||||
}
|
||||
#pragma mark - Handle Event
|
||||
//聊天按钮
|
||||
- (void)chatBtnClick:(UIButton *)sender{
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(chatBtnClick:)]) {
|
||||
[_delegate chatBtnClick:sender];
|
||||
}
|
||||
}
|
||||
//切换摄像头
|
||||
- (void)switchCameraClick:(UIButton *)sender{
|
||||
ILiveFrameDispatcher *frameDispatcher = [[ILiveRoomManager getInstance] getFrameDispatcher];
|
||||
ILiveRenderView *renderView = [frameDispatcher getRenderView:[TCLiveRequestManager getInstance].userID srcType:QAVVIDEO_SRC_TYPE_CAMERA];
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
renderView.isMirror = NO;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
renderView.isMirror = YES;
|
||||
}
|
||||
[[ILiveRoomManager getInstance] switchCamera:^{
|
||||
NSLog(@"switch camera succ");
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"switch camera fail");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"切换摄像头失败" toastMode:UIToastShowMode_fail];
|
||||
}];
|
||||
if ([_delegate respondsToSelector:@selector(switchCameraClick:)]) {
|
||||
[_delegate switchCameraBtnClick:sender];
|
||||
}
|
||||
}
|
||||
//美颜
|
||||
- (void)beautyBtnClick:(UIButton *)sender{
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(beautyBtnClick:)]) {
|
||||
[_delegate beautyBtnClick:sender];
|
||||
}
|
||||
}
|
||||
- (void)enableBeauty:(BOOL)enable{
|
||||
if (enable) {
|
||||
_beautyBtn.selected = YES;
|
||||
}
|
||||
else{
|
||||
_beautyBtn.selected = NO;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(beautyBtnClick:)]) {
|
||||
[_delegate beautyBtnClick:_beautyBtn];
|
||||
}
|
||||
}
|
||||
//语音开关
|
||||
- (void)voiceBtnClick:(UIButton *)sender{
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
}
|
||||
[[ILiveRoomManager getInstance] enableMic:!sender.selected succ:^{
|
||||
NSLog(@"enable mic succ");
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"enable mic fail");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"关麦失败" toastMode:UIToastShowMode_fail];
|
||||
}];
|
||||
if ([_delegate respondsToSelector:@selector(voiceBtnClick:)]) {
|
||||
[_delegate voiceBtnClick:sender];
|
||||
}
|
||||
|
||||
}
|
||||
//配置角色
|
||||
- (void)changeRoleBtnClick:(UIButton *)sender{
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(changeRoleBtnClick:)]) {
|
||||
[_delegate changeRoleBtnClick:sender];
|
||||
}
|
||||
if ([_delegate isKindOfClass:[UIViewController class]]) {
|
||||
[self showChangeRoleMenuOnVC:(UIViewController *)_delegate];
|
||||
}
|
||||
}
|
||||
//日志
|
||||
- (void)logBtnClick:(UIButton *)sender{
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(logBtnClick:)]) {
|
||||
[_delegate logBtnClick:sender];
|
||||
}
|
||||
}
|
||||
//问题反馈
|
||||
- (void)feedBackBtnClick:(UIButton *)sender{
|
||||
if (!sender.selected) {
|
||||
sender.selected = YES;
|
||||
}
|
||||
else{
|
||||
sender.selected = NO;
|
||||
}
|
||||
if ([_delegate respondsToSelector:@selector(feedBackBtnClick:)]) {
|
||||
[_delegate feedBackBtnClick:sender];
|
||||
}
|
||||
if ([_delegate isKindOfClass:[UIViewController class]]) {
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"若您在接入过程中有疑问可直接反馈给我们" message:@"邮箱联系地址:trtcfb@qq.com" preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:nil]];
|
||||
[((UIViewController *)self.delegate) presentViewController:alert animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
//log展示开关
|
||||
- (void)enableLog:(BOOL)endable{
|
||||
self.logBtn.selected = endable;
|
||||
if ([_delegate respondsToSelector:@selector(logBtnClick:)]) {
|
||||
[_delegate logBtnClick:self.logBtn];
|
||||
}
|
||||
}
|
||||
//弹出配置菜单
|
||||
-(void)showChangeRoleMenuOnVC:(UIViewController *)vc{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"请选择配置分辨率" message:nil preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"1280x720 1000-1800kbps 20fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[ILiveRoomManager getInstance] changeRole:@"ed1280" succ:^{
|
||||
NSLog(@"更换ed1280 succ");
|
||||
[self enableLog:YES];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"更换ed1280 fail");
|
||||
}];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"960x540 500-800kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[ILiveRoomManager getInstance] changeRole:@"ed960" succ:^{
|
||||
NSLog(@"更换ed960 succ");
|
||||
[self enableLog:YES];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"更换ed960 fail");
|
||||
}];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"640x480 400-800kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[ILiveRoomManager getInstance] changeRole:@"ed640" succ:^{
|
||||
NSLog(@"更换ed640 succ");
|
||||
[self enableLog:YES];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"更换ed640 fail");
|
||||
}];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"480x360 300-600kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[ILiveRoomManager getInstance] changeRole:@"ed480" succ:^{
|
||||
NSLog(@"更换ed480 succ");
|
||||
[self enableLog:YES];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"更换ed480 fail");
|
||||
}];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"320x240 200-400kbps 15fps" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
|
||||
[[ILiveRoomManager getInstance] changeRole:@"ed320" succ:^{
|
||||
NSLog(@"更换ed320 succ");
|
||||
[self enableLog:YES];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"更换ed320 fail");
|
||||
}];
|
||||
}]];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil]];
|
||||
[vc presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
@end
|
42
src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.h
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// TCLiveVideoLayoutView.h
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/3.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <ILiveSDK/ILiveRenderView.h>
|
||||
|
||||
@class TCLiveVideoElementView;
|
||||
|
||||
typedef enum {
|
||||
TCLiveRoomVideoLayoutStyle_1v3,
|
||||
TCLiveRoomVideoLayoutStyle_4_geizi,
|
||||
}TCLiveRoomVideoLayoutStyle;
|
||||
|
||||
@protocol TCLiveVideoElementViewDelegate
|
||||
- (void)tapHandle:(TCLiveVideoElementView *)view;
|
||||
@end
|
||||
|
||||
@interface TCLiveVideoLayoutView : UIView
|
||||
//画面布局样式
|
||||
@property(nonatomic,assign) TCLiveRoomVideoLayoutStyle layoutStyle;
|
||||
//显示日志信息
|
||||
- (void)showLogView:(NSString *)qualityParams;
|
||||
//关闭日志信息
|
||||
- (void)closeLogView;
|
||||
//设置美颜
|
||||
- (void)setBeautyLevel:(NSInteger)level;
|
||||
@end
|
||||
|
||||
@interface TCLiveVideoElementView: UIView
|
||||
//展示userID
|
||||
@property(nonatomic,strong)UILabel *userIdLabel;
|
||||
//展示视频分辨率、帧率等信息
|
||||
@property(nonatomic,strong)UILabel *videoInfoLable;
|
||||
@property(nonatomic,weak)TCLiveVideoLayoutView<TCLiveVideoElementViewDelegate> *delegate;
|
||||
//可拖动开关
|
||||
- (void)ennableDraggable:(BOOL)draggable;
|
||||
@end
|
403
src/ios/Trtc/TCLiveRoomVC/TCLiveVideoLayoutView.m
Normal file
@ -0,0 +1,403 @@
|
||||
//
|
||||
// TCLiveVideoLayoutView.m
|
||||
// TRTC
|
||||
//
|
||||
// Created by Tencent on 2018/6/3.
|
||||
// Copyright © 2018年 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TCLiveVideoLayoutView.h"
|
||||
#import <ILiveSDK/ILiveRoomManager.h>
|
||||
#import <ILiveSDK/ILiveQualityData.h>
|
||||
#import <TILLiveSDK/TILLiveManager.h>
|
||||
#import "TCLiveRequestManager.h"
|
||||
#import "UIColorEX.h"
|
||||
#import "TXCVideoPreprocessor.h"
|
||||
#define LIVE_VIDEO_NUM 4
|
||||
|
||||
@interface TCLiveVideoLayoutView () <QAVRemoteVideoDelegate,QAVLocalVideoDelegate,ILiveScreenVideoDelegate,ILVLiveAVListener,ILiveMemStatusListener,TCLiveVideoElementViewDelegate,TXIVideoPreprocessorDelegate>
|
||||
@property(nonatomic,strong)NSMutableArray *liveVideos;
|
||||
@property(nonatomic,strong)NSMutableArray *liveRnederView;
|
||||
@property(nonatomic,strong)UITextView *logView;
|
||||
@property(nonatomic,assign) BOOL isShowLogInfo;
|
||||
@property(nonatomic,assign) NSDate *startTime;
|
||||
@property (nonatomic, strong) TXCVideoPreprocessor *preProcessor;
|
||||
@property (nonatomic, assign) Byte *processorBytes;
|
||||
@end
|
||||
|
||||
@implementation TCLiveVideoLayoutView
|
||||
|
||||
-(instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
self.backgroundColor = [UIColor clearColor];
|
||||
self.layoutStyle = TCLiveRoomVideoLayoutStyle_1v3;
|
||||
[self initialVideoViews];
|
||||
[[TILLiveManager getInstance] setAVListener:self];
|
||||
[[ILiveRoomManager getInstance] setLocalVideoDelegate:self];
|
||||
[[ILiveRoomManager getInstance] setRemoteVideoDelegate:self];
|
||||
[[ILiveRoomManager getInstance] setScreenVideoDelegate:self];
|
||||
//美颜处理器
|
||||
self.preProcessor = [[TXCVideoPreprocessor alloc] init];
|
||||
[self.preProcessor setDelegate:self];
|
||||
//记录进入时间
|
||||
self.startTime = [NSDate date];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
//初始化每路视频view
|
||||
- (void)initialVideoViews{
|
||||
_liveVideos = [NSMutableArray array];
|
||||
//自定义视窗数组
|
||||
NSArray *frames = [self customLayoutFrames];
|
||||
for (int i = 0; i < frames.count; i++) {
|
||||
TCLiveVideoElementView *view = [[TCLiveVideoElementView alloc] initWithFrame:CGRectZero];
|
||||
view.delegate = self;
|
||||
[self addSubview:view];
|
||||
[_liveVideos addObject:view];
|
||||
}
|
||||
}
|
||||
-(UITextView *)logView{
|
||||
if(!_logView){
|
||||
_logView = [[UITextView alloc] initWithFrame:CGRectMake(0, self.frame.size.height/5, self.frame.size.width/3*2, self.frame.size.height/2)];
|
||||
_logView.textColor = [UIColor colorWithRGBHex:0xFF4081];
|
||||
_logView.font = [UIFont systemFontOfSize:14];
|
||||
_logView.backgroundColor = [UIColor clearColor];
|
||||
_logView.editable = NO;
|
||||
}
|
||||
return _logView;
|
||||
}
|
||||
|
||||
//自定义布局(开发者可自定义每个视窗的frame来实现,视窗个数即为frames数组个数)
|
||||
- (NSArray *)customLayoutFrames{
|
||||
NSMutableArray *frames = [NSMutableArray array];
|
||||
if (TCLiveRoomVideoLayoutStyle_1v3 == _layoutStyle) {
|
||||
[frames removeAllObjects];
|
||||
int smallViewWidth = ((self.frame.size.height - 20 - 150 - 10 *2)/3)*(3.0/4.0);
|
||||
int smallViewHeight = (self.frame.size.height - 20 - 150 - 10 *2)/3;
|
||||
CGRect frame1 = self.bounds;
|
||||
CGRect frame2 = CGRectMake(self.bounds.size.width - 10 - smallViewWidth, 84 , smallViewWidth, smallViewHeight);
|
||||
CGRect frame3 = CGRectMake(self.bounds.size.width - 10 - smallViewWidth, frame2.origin.y + frame2.size.height + 10, smallViewWidth, smallViewHeight);
|
||||
CGRect frame4 = CGRectMake(self.bounds.size.width - 10 - smallViewWidth, frame3.origin.y + frame3.size.height + 10, smallViewWidth, smallViewHeight);
|
||||
|
||||
[frames addObject:[NSValue valueWithCGRect:frame1]];
|
||||
[frames addObject:[NSValue valueWithCGRect:frame2]];
|
||||
[frames addObject:[NSValue valueWithCGRect:frame3]];
|
||||
[frames addObject:[NSValue valueWithCGRect:frame4]];
|
||||
}
|
||||
else if (TCLiveRoomVideoLayoutStyle_4_geizi == _layoutStyle) {
|
||||
[frames removeAllObjects];
|
||||
CGRect frame1 = CGRectMake(20, 50, (self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
|
||||
CGRect frame2 = CGRectMake(20 + (self.frame.size.width -40)/2 +1, 50, (self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
|
||||
CGRect frame3 = CGRectMake(20, 50 + (self.frame.size.height - 100)/2 +1, (self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
|
||||
CGRect frame4 = CGRectMake(20 + (self.frame.size.width -40)/2 +1, 50 + (self.frame.size.height - 100)/2 +1,(self.frame.size.width -40)/2, (self.frame.size.height - 100)/2);
|
||||
[frames addObject:[NSValue valueWithCGRect:frame1]];
|
||||
[frames addObject:[NSValue valueWithCGRect:frame2]];
|
||||
[frames addObject:[NSValue valueWithCGRect:frame3]];
|
||||
[frames addObject:[NSValue valueWithCGRect:frame4]];
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
//添加视频画面
|
||||
-(void)addLiveRenderView:(ILiveRenderView *)renderView{
|
||||
NSArray *frames = [self customLayoutFrames];
|
||||
for (int i = 0; i < _liveVideos.count;i++) {
|
||||
TCLiveVideoElementView *videoView = _liveVideos[i];
|
||||
videoView.frame = [frames[i] CGRectValue];
|
||||
if (![self getLiveRenderViewOnElementView:videoView]) {
|
||||
renderView.frame = videoView.bounds;
|
||||
[videoView insertSubview:renderView atIndex:0];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//移除视频画面
|
||||
- (void)removeLiverRenderVeiw:(ILiveRenderView *)renderView{
|
||||
if ([[renderView superview] isKindOfClass:[TCLiveVideoElementView class]]) {
|
||||
TCLiveVideoElementView *videoView = (TCLiveVideoElementView *)[renderView superview];
|
||||
videoView.frame = CGRectZero;
|
||||
[renderView removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
//画面切换
|
||||
- (void)tapHandle:(TCLiveVideoElementView *)view{
|
||||
NSUInteger index = [_liveVideos indexOfObject:view];
|
||||
TCLiveVideoElementView *bigView = _liveVideos[0];
|
||||
ILiveRenderView *bigRenderView = [self getLiveRenderViewOnElementView:bigView];
|
||||
ILiveRenderView *renderView = [self getLiveRenderViewOnElementView:view];
|
||||
if (index > 0) {
|
||||
[UIView animateWithDuration:0.5 animations:^{
|
||||
bigView.frame = view.frame;
|
||||
view.frame = [[self customLayoutFrames][0] CGRectValue];
|
||||
bigRenderView.frame = bigView.bounds;
|
||||
renderView.frame = view.bounds;
|
||||
[self.liveVideos exchangeObjectAtIndex:0 withObjectAtIndex:index];
|
||||
[self exchangeSubviewAtIndex:0 withSubviewAtIndex:index];
|
||||
} completion:^(BOOL finished) {
|
||||
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
//获取指定视窗的渲染view
|
||||
- (ILiveRenderView *)getLiveRenderViewOnElementView:(TCLiveVideoElementView *)elementView{
|
||||
ILiveRenderView *renderView = nil;
|
||||
for (id view in [elementView subviews]) {
|
||||
if ([view isKindOfClass:[ILiveRenderView class]]) {
|
||||
renderView = view;
|
||||
}
|
||||
}
|
||||
return renderView;
|
||||
}
|
||||
-(void)setBeautyLevel:(NSInteger)level{
|
||||
[self.preProcessor setBeautyLevel:level];
|
||||
}
|
||||
#pragma mark - ILiveMemStatusListener
|
||||
//房间成员状态变化事件
|
||||
- (BOOL)onEndpointsUpdateInfo:(QAVUpdateEvent)event updateList:(NSArray *)endpoints{
|
||||
if (endpoints.count <= 0) {
|
||||
return NO;
|
||||
}
|
||||
for (QAVEndpoint *endoption in endpoints) {
|
||||
switch (event) {
|
||||
case QAV_EVENT_ID_ENDPOINT_HAS_CAMERA_VIDEO:
|
||||
{
|
||||
ILiveFrameDispatcher *frameDispatcher = [[ILiveRoomManager getInstance] getFrameDispatcher];
|
||||
ILiveRenderView *renderView = [frameDispatcher addRenderAt:CGRectZero forIdentifier:endoption.identifier srcType:QAVVIDEO_SRC_TYPE_CAMERA];
|
||||
renderView.isRotate = NO;
|
||||
renderView.autoRotate = NO;
|
||||
renderView.isMirror = YES;
|
||||
renderView.identifier = endoption.identifier;
|
||||
renderView.diffDirectionRenderMode = ILIVERENDERMODE_SCALEASPECTFILL;
|
||||
if ([[TCLiveRequestManager getInstance].userID isEqualToString:endoption.identifier]) {
|
||||
renderView.rotateAngle = ILIVEROTATION_90;
|
||||
}
|
||||
[self addLiveRenderView:renderView];
|
||||
}
|
||||
break;
|
||||
case QAV_EVENT_ID_ENDPOINT_NO_CAMERA_VIDEO:
|
||||
{
|
||||
ILiveFrameDispatcher *frameDispatcher = [[ILiveRoomManager getInstance] getFrameDispatcher];
|
||||
ILiveRenderView *renderView = [frameDispatcher removeRenderViewFor:endoption.identifier srcType:QAVVIDEO_SRC_TYPE_CAMERA];
|
||||
[self removeLiverRenderVeiw:renderView];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
/***本地画面帧数据回调***/
|
||||
#pragma mark - QAVLocalVideoDelegate
|
||||
- (void)OnLocalVideoPreview:(QAVVideoFrame *)frameData{
|
||||
[self showElementVideoInfoWithVideoFrame:frameData];
|
||||
}
|
||||
|
||||
- (void)OnLocalVideoPreProcess:(QAVVideoFrame *)frameData{
|
||||
|
||||
|
||||
[self.preProcessor setOutputSize:CGSizeMake(frameData.frameDesc.width, frameData.frameDesc.height)];
|
||||
[self.preProcessor setCropRect:CGRectMake(0, 0,frameData.frameDesc.width, frameData.frameDesc.height)];
|
||||
[self.preProcessor processFrame:frameData.data width:frameData.frameDesc.width height:frameData.frameDesc.height orientation:TXE_ROTATION_0 inputFormat:TXE_FRAME_FORMAT_NV12 outputFormat:TXE_FRAME_FORMAT_NV12];
|
||||
//将处理完的数据拷贝到原来的地址空间,如果是同步处理,此时会先执行didProcessFrame回调
|
||||
if(self.processorBytes){
|
||||
memcpy(frameData.data, self.processorBytes, frameData.frameDesc.width * frameData.frameDesc.height * 3 / 2);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)OnLocalVideoRawSampleBuf:(CMSampleBufferRef)buf result:(CMSampleBufferRef *)ret{
|
||||
|
||||
}
|
||||
|
||||
/***远端视频帧数据回调***/
|
||||
#pragma mark - QAVRemoteVideoDelegate
|
||||
- (void)OnVideoPreview:(QAVVideoFrame *)frameData{
|
||||
[self showElementVideoInfoWithVideoFrame:frameData];
|
||||
}
|
||||
/***远端屏幕分享帧数据回调***/
|
||||
#pragma mark - ILiveScreenVideoDelegate
|
||||
|
||||
-(void)onScreenVideoPreview:(QAVVideoFrame *)frameData{
|
||||
[self showElementVideoInfoWithVideoFrame:frameData];
|
||||
}
|
||||
/***首帧回调***/
|
||||
#pragma mark - ILVLiveAVListener
|
||||
- (void)onFirstFrameRecved:(int)width height:(int)height identifier:(NSString *)identifier srcType:(avVideoSrcType)srcType;{
|
||||
|
||||
}
|
||||
/***美颜相关处理***/
|
||||
#pragma mark - TXIVideoPreprocessorDelegate
|
||||
- (void)didProcessFrame:(Byte *)bytes width:(NSInteger)width height:(NSInteger)height format:(TXEFrameFormat)format timeStamp:(UInt64)timeStamp
|
||||
{
|
||||
self.processorBytes = bytes;
|
||||
}
|
||||
#pragma mark - LOG相关
|
||||
- (void)showLogView:(NSString *)qualityParams{
|
||||
NSString *role = [[[[qualityParams componentsSeparatedByString:@"ControlRole="] lastObject] componentsSeparatedByString:@","] firstObject];
|
||||
|
||||
self.logView.text = [NSString stringWithFormat:@"发送速率:%ldkbps 丢包率:%.1f%%\n接收速率:%ldkbps 丢包率:%.1f%%\n应用CPU:%.1f%% 系统CPU:%.1f%%\n角色:%@\nSDKAPPID:%d\nSDKVersion:%@",(long)[[ILiveRoomManager getInstance] getQualityData].sendRate,[[ILiveRoomManager getInstance] getQualityData].sendLossRate/100.0,(long)[[ILiveRoomManager getInstance] getQualityData].recvRate,[[ILiveRoomManager getInstance] getQualityData].recvLossRate/100.0,[[ILiveRoomManager getInstance] getQualityData].appCPURate/100.0,[[ILiveRoomManager getInstance] getQualityData].sysCPURate/100.0,role,[TCLiveRequestManager getInstance].sdkAppID,[[ILiveSDK getInstance] getVersion]];
|
||||
if(![_logView superview]){
|
||||
[self addSubview:_logView];
|
||||
}
|
||||
self.isShowLogInfo = YES;
|
||||
}
|
||||
|
||||
- (void)showElementVideoInfoWithVideoFrame:(QAVVideoFrame *)frame{
|
||||
if(!self.isShowLogInfo){
|
||||
return;
|
||||
}
|
||||
NSString *userId = frame.identifier;
|
||||
NSString *fps = @"";
|
||||
if (userId.length == 0){
|
||||
userId = [TCLiveRequestManager getInstance].userID;
|
||||
}
|
||||
else{
|
||||
NSString *qualityParams = [[[ILiveSDK getInstance] getAVContext].room getQualityTips];
|
||||
NSString *decode = [[qualityParams componentsSeparatedByString:@"音频部分:========"] firstObject];
|
||||
NSString *itemDecode = [[[[decode componentsSeparatedByString:[NSString stringWithFormat:@"成员:%@",userId]] lastObject] componentsSeparatedByString:@"接收参数"] firstObject];
|
||||
fps = [[[[itemDecode componentsSeparatedByString:@"FPS="] lastObject] componentsSeparatedByString:@","] firstObject];
|
||||
}
|
||||
int width = frame.frameDesc.width;
|
||||
int height = frame.frameDesc.height;
|
||||
for (int i = 0; i < _liveVideos.count;i++) {
|
||||
TCLiveVideoElementView *videoView = _liveVideos[i];
|
||||
ILiveRenderView *renderView = [self getLiveRenderViewOnElementView:videoView];
|
||||
if ([renderView.identifier isEqualToString:userId]) {
|
||||
if ([userId isEqualToString:[TCLiveRequestManager getInstance].userID]) {
|
||||
videoView.videoInfoLable.text = [NSString stringWithFormat:@"%dx%d",width,height];
|
||||
}
|
||||
else{
|
||||
videoView.videoInfoLable.text = [NSString stringWithFormat:@"%dx%d fps:%d",width,height,[fps intValue]/10];
|
||||
}
|
||||
videoView.userIdLabel.text = [NSString stringWithFormat:@"%@",userId];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeLogView{
|
||||
[_logView removeFromSuperview];
|
||||
_logView = nil;
|
||||
for (int i = 0; i < _liveVideos.count;i++) {
|
||||
TCLiveVideoElementView *videoView = _liveVideos[i];
|
||||
[videoView.userIdLabel removeFromSuperview];
|
||||
videoView.userIdLabel = nil;
|
||||
[videoView.videoInfoLable removeFromSuperview];
|
||||
videoView.videoInfoLable = nil;
|
||||
}
|
||||
self.isShowLogInfo = NO;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface TCLiveVideoElementView() <UIGestureRecognizerDelegate>
|
||||
@property(nonatomic,strong) UIPanGestureRecognizer *panGesture;
|
||||
@end
|
||||
|
||||
@implementation TCLiveVideoElementView
|
||||
|
||||
-(instancetype)initWithFrame:(CGRect)frame{
|
||||
if (self = [super initWithFrame:frame]) {
|
||||
[self ennableDraggable:YES];
|
||||
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGestuer:)];
|
||||
[self addGestureRecognizer:tap];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (UILabel *)userIdLabel{
|
||||
if (!_userIdLabel) {
|
||||
_userIdLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 12, self.bounds.size.width, 8)];
|
||||
_userIdLabel.textColor = [UIColor colorWithRGBHex:0xFF4081];
|
||||
_userIdLabel.textAlignment = NSTextAlignmentLeft;
|
||||
_userIdLabel.font = [UIFont systemFontOfSize:9];
|
||||
[self addSubview:_userIdLabel];
|
||||
}
|
||||
return _userIdLabel;
|
||||
}
|
||||
-(UILabel *)videoInfoLable{
|
||||
if (!_videoInfoLable) {
|
||||
_videoInfoLable = [[UILabel alloc] initWithFrame:CGRectMake(0, 0 , self.bounds.size.width, 12)];
|
||||
_videoInfoLable.textColor = [UIColor colorWithRGBHex:0xFF4081];
|
||||
_videoInfoLable.textAlignment = NSTextAlignmentLeft;
|
||||
_videoInfoLable.font = [UIFont systemFontOfSize:13];
|
||||
[self addSubview:_videoInfoLable];
|
||||
}
|
||||
return _videoInfoLable;
|
||||
}
|
||||
- (void)layoutSubviews{
|
||||
if (self.bounds.size.width == [UIScreen mainScreen].bounds.size.width) {
|
||||
_videoInfoLable.frame = CGRectMake(0, 84 , self.bounds.size.width, 12);
|
||||
_userIdLabel.frame = CGRectMake(0, 12 + 84, self.bounds.size.width, 8);
|
||||
}
|
||||
else{
|
||||
_videoInfoLable.frame = CGRectMake(0, 0 , self.bounds.size.width, 12);
|
||||
_userIdLabel.frame = CGRectMake(0, 12, self.bounds.size.width, 8);
|
||||
}
|
||||
}
|
||||
//可拖动能力
|
||||
-(void)ennableDraggable:(BOOL)draggable {
|
||||
[self setUserInteractionEnabled:YES];
|
||||
[self removeConstraints:self.constraints];
|
||||
for (NSLayoutConstraint *constraint in self.superview.constraints) {
|
||||
if ([constraint.firstItem isEqual:self]) {
|
||||
[self.superview removeConstraint:constraint];
|
||||
}
|
||||
}
|
||||
[self setTranslatesAutoresizingMaskIntoConstraints:YES];
|
||||
if (draggable) {
|
||||
if (!_panGesture) {
|
||||
_panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
|
||||
_panGesture.delegate = self;
|
||||
[self addGestureRecognizer:_panGesture];
|
||||
}
|
||||
}else{
|
||||
if (_panGesture) {
|
||||
_panGesture = nil;
|
||||
[self removeGestureRecognizer:_panGesture];
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void)tapGestuer:(UITapGestureRecognizer *)gesture{
|
||||
if ([_delegate respondsToSelector:@selector(tapHandle:)]) {
|
||||
[_delegate tapHandle:self];
|
||||
}
|
||||
}
|
||||
- (void)pan:(UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
switch (panGestureRecognizer.state) {
|
||||
case UIGestureRecognizerStateBegan: {
|
||||
[self dragging:panGestureRecognizer];
|
||||
}
|
||||
break;
|
||||
case UIGestureRecognizerStateChanged: {
|
||||
[self dragging:panGestureRecognizer];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
-(void)dragging:(UIPanGestureRecognizer *)panGestureRecognizer {
|
||||
UIView *view = panGestureRecognizer.view;
|
||||
CGPoint translation = [panGestureRecognizer translationInView:view.superview];
|
||||
CGPoint center = CGPointMake(view.center.x + translation.x, view.center.y + translation.y);
|
||||
//不能拖过边界
|
||||
CGSize size = view.frame.size;
|
||||
CGSize superSize = view.superview.frame.size;
|
||||
CGFloat width = size.width;
|
||||
CGFloat height = size.height;
|
||||
CGFloat superWidth = superSize.width;
|
||||
CGFloat superHeight = superSize.height;
|
||||
center.x = (center.x<width/2)?width/2:center.x;
|
||||
center.x = (center.x+width/2>superWidth)?superWidth-width/2:center.x;
|
||||
center.y = (center.y<height/2)?height/2:center.y;
|
||||
center.y = (center.y+height/2>superHeight)?superHeight-height/2:center.y;
|
||||
|
||||
[view setCenter:center];
|
||||
[panGestureRecognizer setTranslation:CGPointZero inView:view.superview];
|
||||
}
|
||||
@end
|
||||
|
10
src/ios/TrtcConfig.plist
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>AppId</key>
|
||||
<string></string>
|
||||
<key>IsDebug</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
13
src/ios/TrtcPlugin.h
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// TrtcPlugin.h
|
||||
//
|
||||
// Created by 布丁丸子酱 on 2018/12/26.
|
||||
//
|
||||
|
||||
#import <Cordova/CDVPlugin.h>
|
||||
|
||||
@interface TrtcPlugin : CDVPlugin
|
||||
|
||||
- (void) showCreatePage:(CDVInvokedUrlCommand*)command;
|
||||
|
||||
@end
|
49
src/ios/TrtcPlugin.m
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// TrtcPlugin.m
|
||||
//
|
||||
// Created by 布丁丸子酱 on 2018/12/26.
|
||||
//
|
||||
|
||||
#import "TrtcPlugin.h"
|
||||
#import "TCLiveRequestManager.h"
|
||||
#import <ILiveSDK/ILiveSDK.h>
|
||||
#import "TCLiveJoinRoomViewController.h"
|
||||
#import <ILiveSDK/ILiveLoginManager.h>
|
||||
#import "UIToastView.h"
|
||||
#import <ILiveLogReport/ILiveLogReport.h>
|
||||
|
||||
@interface TrtcPlugin()
|
||||
{}
|
||||
@end
|
||||
|
||||
@implementation TrtcPlugin
|
||||
|
||||
- (void) showCreatePage:(CDVInvokedUrlCommand*)command {
|
||||
NSLog(@"showCreatePage");
|
||||
[[TCLiveRequestManager getInstance] requestLoginInfo:^(int code) {
|
||||
if (code == 0) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
int retCode = [[ILiveSDK getInstance] initSdk:[TCLiveRequestManager getInstance].sdkAppID accountType:[TCLiveRequestManager getInstance].accountType];
|
||||
NSLog(@"initSdk success %d",retCode);
|
||||
if (retCode == 0) {
|
||||
NSLog(@"userId & sig:");
|
||||
NSLog(@"%@", [TCLiveRequestManager getInstance].userID);
|
||||
NSLog(@"%@", [TCLiveRequestManager getInstance].userSig);
|
||||
[[ILiveLoginManager getInstance] iLiveLogin:[TCLiveRequestManager getInstance].userID sig:[TCLiveRequestManager getInstance].userSig succ:^{
|
||||
NSLog(@"-----> login succ");
|
||||
[[UIToastView getInstance] showToastWithMessage:@"登录成功" toastMode:UIToastShowMode_Succ];
|
||||
} failed:^(NSString *module, int errId, NSString *errMsg) {
|
||||
NSLog(@"-----> login fail,%@ %d %@",module, errId, errMsg);
|
||||
[[UIToastView getInstance] showToastWithMessage:@"登录失败" toastMode:UIToastShowMode_fail];
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
}];
|
||||
TCLiveJoinRoomViewController *vc = [TCLiveJoinRoomViewController new];
|
||||
// vc.defaultRoomId = self.defaultRoomId;
|
||||
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
|
||||
[self.viewController presentViewController:nav animated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
4
src/ios/libs/.gitkeep
Normal file
@ -0,0 +1,4 @@
|
||||
# AVSDK
|
||||
# BeautySDK
|
||||
# ILiveSDK
|
||||
# IMSDK
|
BIN
src/ios/res/beauty-dis.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
src/ios/res/beauty.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/ios/res/bg.png
Normal file
After Width: | Height: | Size: 224 KiB |
BIN
src/ios/res/camera-gray.png
Normal file
After Width: | Height: | Size: 681 B |
BIN
src/ios/res/camera.png
Normal file
After Width: | Height: | Size: 894 B |
BIN
src/ios/res/chat.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/ios/res/doubleroom.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
src/ios/res/feedback.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
src/ios/res/ic_toast_success@2x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/ios/res/icon_sign@2x.png
Normal file
After Width: | Height: | Size: 598 B |
BIN
src/ios/res/log.png
Normal file
After Width: | Height: | Size: 832 B |
BIN
src/ios/res/log2.png
Normal file
After Width: | Height: | Size: 654 B |
BIN
src/ios/res/mic-dis.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
src/ios/res/mic.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
src/ios/res/muti_room_bg.png
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
src/ios/res/role.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/ios/res/ui_title_arrow_left.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
13
www/trtc.js
Normal file
@ -0,0 +1,13 @@
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
function Trtc() {}
|
||||
|
||||
Trtc.prototype.joinChannel = function(arg, success, error) {
|
||||
exec(success, error, 'Trtc', 'joinChannel', [arg]);
|
||||
};
|
||||
|
||||
Trtc.prototype.showCreatePage = function(success, error) {
|
||||
exec(success, error, 'Trtc', 'showCreatePage', []);
|
||||
}
|
||||
|
||||
module.exports = new Trtc();
|