完成android端基本功能升级

This commit is contained in:
zher52 2021-12-26 19:04:17 +08:00
parent 062ceea091
commit d9554f7571
17 changed files with 584 additions and 706 deletions

View File

@ -1,55 +0,0 @@
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);
}
}

View File

@ -0,0 +1,45 @@
package com.tencent.trtc;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONObject;
public class CordovaEventKit {
public static CordovaEventKit kit = null;
private CordovaPlugin plugin;
private CordovaEventKit(CordovaPlugin plugin) {
this.plugin = plugin;
}
public static void init(CordovaPlugin plugin){
if(CordovaEventKit.kit == null){
CordovaEventKit.kit = new CordovaEventKit(plugin);
}
}
public void fireEvent(String event, JSONArray obj){
this.fireEvent(event,obj == null ? null obj.toString());
}
public void fireEvent(String event, JSONObject obj){
this.fireEvent(event,obj == null ? null :obj.toString());
}
public void fireEvent(String event, String obj){
if (plugin == null || event == null || obj == null) {
return;
}
event = event.replace('\'','_');
String format = "window.cordova.plugin.trtc.fireEvent('%s',%s);";
final String js = String.format(format,event, obj);
plugin.cordova.getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
plugin.webView.loadUrl("javascript:" + js);
}
});
}
}

View File

@ -0,0 +1,132 @@
package com.tencent.trtc;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tencent.rtmp.ui.TXCloudVideoView;
import com.tencent.trtc.event.Events;
import com.tencent.trtc.videocall.UserInfo;
public class CustomVideoView extends RelativeLayout {
private Context mContext;
private View mView;
private TXCloudVideoView videoView;
private TextView titleView;
private UserInfo userInfo;
private boolean alwaysHide = false;
private boolean mainView;
public CustomVideoView(Context context) {
this(context,null);
}
public CustomVideoView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CustomVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr,0);
}
public CustomVideoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mContext = context;
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mView = inflater.inflate(getId("custom_video_view_layout", "layout"), this, true);
videoView = (TXCloudVideoView) findViewById(getId("video", "id"));
titleView = (TextView) findViewById(getId("title","id"));
}
private int getId(String idName, String type) {
return getResources().getIdentifier(idName, type, mContext.getPackageName());
}
public void changeUser(TRTCCloud mTRTCCloud, UserInfo userInfo) {
if(userInfo == null){
setVisibility(View.GONE);
}else {
setVisibility(alwaysHide?INVISIBLE:VISIBLE);
}
// if(this.userInfo != null){
// if(this.userInfo.isLocal()){
// mTRTCCloud.stopLocalPreview();
// mTRTCCloud.stopLocalAudio();
//// }else{
//// mTRTCCloud.stopRemoteView(this.userInfo.getPersonid());
// }
// }
if(isChanged(userInfo)){
this.userInfo = userInfo;
if(this.userInfo != null){
if(this.userInfo.isLocal()){
mTRTCCloud.startLocalPreview(userInfo.isFrontCamera(), videoView);
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
} else {
mTRTCCloud.startRemoteView(this.userInfo.getPersonid(), TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SMALL, videoView);
}
if(this.userInfo.getDisplayName() == null || this.userInfo.getDisplayName().length() == 0){
this.titleView.setText(this.userInfo.getPersonid());
}else {
this.titleView.setText(this.userInfo.getDisplayName());
}
this.titleView.setVisibility(VISIBLE);
}else {
this.titleView.setVisibility(INVISIBLE);
}
}
}
private boolean isChanged(UserInfo userInfo){
return (this.userInfo == null && userInfo !=null) ||
(this.userInfo != null && !this.userInfo.equals(userInfo));
}
public boolean isAlwaysHide() {
return alwaysHide;
}
public CustomVideoView setAlwaysHide(boolean alwaysHide) {
this.alwaysHide = alwaysHide;
return this;
}
public boolean isMainView() {
return mainView;
}
public CustomVideoView setMainView(boolean mainView) {
this.mainView = mainView;
if(!mainView){
Events.addListener("subview.always.hide",(extra)->{
this.alwaysHide = extra.getBoolean("alwaysHide",false);
this.setVisibility(alwaysHide?INVISIBLE:VISIBLE);
});
}
Events.addListener("userinfo.update",(extra -> {
if(this.userInfo !=null && this.userInfo.getDisplayName() == null
&& this.userInfo.getDisplayName().length() == 0
&& extra.getString("personid").equals(this.userInfo.getPersonid())){
this.titleView.setText(extra.getString("displayName",this.userInfo.getDisplayName()));
}
}));
return this;
}
public UserInfo getUserInfo() {
return userInfo;
}
}

View File

@ -0,0 +1,10 @@
package com.tencent.trtc;
import org.json.JSONObject;
public interface MessageObject {
String getEvent();
JSONObject toJson();
}

View File

@ -1,4 +1,4 @@
package android.java.com.example.basic; package com.tencent.trtc;
import android.Manifest; import android.Manifest;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;

View File

@ -0,0 +1,86 @@
package com.tencent.trtc;
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import com.tencent.trtc.CordovaEventKit;
import com.tencent.trtc.event.Events;
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;
import java.lang.reflect.Method;
public class TrtcPlugin extends CordovaPlugin {
private static final String TAG = TrtcPlugin.class.getSimpleName();
/**
* 加入会议
* @param args
* @param callbackContext
* @throws JSONException
*/
public void joinChannel(JSONArray args, CallbackContext callbackContext) throws JSONException {
Intent intent = new Intent(this.cordova.getActivity(), 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);
}
public void userInfoChange(JSONArray args,CallbackContext callbackContext) throws JSONException{
JSONObject object = args.getJSONObject(0);
if(object != null){
Events.fireEvent("userinfo.update",extra -> {
try {
extra.putString("personid",object.getString("personid"));
extra.putString("displayName",object.getString("displayName"));
} catch (JSONException e) {
e.printStackTrace();
}
});
}
}
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
try {
Method method = TrtcPlugin.class.getDeclaredMethod(action, JSONArray.class, CallbackContext.class);
method.invoke(TrtcPlugin.this, args, callbackContext);
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
});
return true;
}
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
CordovaEventKit.init(this);
}
}

View File

@ -1,280 +0,0 @@
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: 用于生成测试用的 UserSigUserSig 是腾讯云为其云服务设计的一种安全保护签名
* 其计算方法是对 SDKAppIDUserID EXPIRETIME 进行加密加密算法为 HMAC-SHA256
*
* Attention: 请不要将如下代码发布到您的线上正式版本的 App 原因如下
*
* 本文件中的代码虽然能够正确计算出 UserSig但仅适合快速调通 SDK 的基本功能不适合线上产品
* 这是因为客户端代码中的 SECRETKEY 很容易被反编译逆向破解尤其是 Web 端的代码被破解的难度几乎为零
* 一旦您的密钥泄露攻击者就可以计算出正确的 UserSig 来盗用您的腾讯云流量
*
* 正确的做法是将 UserSig 的计算代码和加密密钥放在您的业务服务器上然后由 App 按需向您的服务器获取实时算出的 UserSig
* 由于破解服务器的成本要高于破解客户端 App所以服务器计算的方案能够更好地保护您的加密密钥
*
* Referencehttps://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 SDKs 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 dont 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 非对称加密算法 SDKAPPIDuserId 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 SDKs 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;
}
}

View File

@ -0,0 +1,54 @@
package com.tencent.trtc.event;
import android.os.Bundle;
import org.json.JSONException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Events {
private static final String PREFIX = "com.tencent.trtc.event";
private static Map<String, List<Listener>> events = new HashMap();
public static void fireEvent(String event){
fireEvent(event,null);
}
public static void fireEvent(String event, Extra extra){
if(events.containsKey(PREFIX+event)){
for(Listener listener : events.get(PREFIX+event)){
Bundle bundle = extra == null ? Bundle.EMPTY: new Bundle();
if(extra != null){
extra.extra(bundle);
}
listener.on(extra == null ? Bundle.EMPTY : bundle);
}
}
}
public static void addListener(String eventName,Listener listener){
if(!events.containsKey(PREFIX+eventName)){
events.put(PREFIX+eventName,new ArrayList<>());
}
List<Listener> list = events.get(PREFIX+eventName);
if(!list.contains(listener)){
list.add(listener);
}
}
public static void removeListener(String eventName,Listener listener){
if(!events.containsKey(PREFIX+eventName)){
events.put(PREFIX+eventName,new ArrayList<>());
}
List<Listener> list = events.get(PREFIX+eventName);
if(list.contains(listener)){
list.remove(listener);
}
}
public interface Extra{
public void extra(Bundle extra);
}
}

View File

@ -0,0 +1,7 @@
package com.tencent.trtc.event;
import android.os.Bundle;
public interface Listener {
void on(Bundle extra);
}

View File

@ -0,0 +1,28 @@
package com.tencent.trtc.message;
import com.tencent.trtc.MessageObject;
import org.json.JSONObject;
public class LayoutChangeMessage implements MessageObject {
private String room;
private String personid;
public LayoutChangeMessage(String room, String personid) {
this.room = room;
this.personid = personid;
}
@Override
public String getEvent() {
return "LayoutChangeMessage";
}
@Override
public JSONObject getJson() {
JSONObject obj = new JSONObject();
return obj;
}
}

View File

@ -1,193 +0,0 @@
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;
}
}

View File

@ -0,0 +1,67 @@
package com.tencent.trtc.videocall;
import androidx.annotation.Nullable;
public class UserInfo {
String personid;
String displayName;
boolean isLocal;
boolean frontCamera = true;
public String getPersonid() {
return personid;
}
public UserInfo setPersonid(String personid) {
this.personid = personid;
return this;
}
public String getDisplayName() {
return displayName;
}
public UserInfo setDisplayName(String displayName) {
this.displayName = displayName;
return this;
}
public boolean isLocal() {
return isLocal;
}
public UserInfo setLocal(boolean local) {
isLocal = local;
return this;
}
public boolean isFrontCamera() {
return frontCamera;
}
public UserInfo setFrontCamera(boolean frontCamera) {
this.frontCamera = frontCamera;
return this;
}
@Override
public int hashCode() {
return getPersonid().hashCode();
}
@Override
public boolean equals(@Nullable Object obj) {
if(obj == null){
return false;
}
if(obj instanceof UserInfo){
return getPersonid() == null ? ((UserInfo) obj).getPersonid() == null
: getPersonid().equals(((UserInfo) obj).getPersonid());
}else if(obj instanceof String){
return getPersonid() == null ? obj == null : getPersonid().equals(obj);
}
return false;
}
}

View File

@ -1,31 +1,31 @@
package com.tencent.trtc.videocall; package com.tencent.trtc.videocall;
import android.content.Intent; import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.java.com.example.basic.TRTCBaseActivity; import com.tencent.trtc.CordovaEventKit;
import com.tencent.trtc.CustomVideoView;
import com.tencent.trtc.TRTCBaseActivity;
import com.tencent.liteav.TXLiteAVCode;
import com.tencent.liteav.device.TXDeviceManager; import com.tencent.liteav.device.TXDeviceManager;
import com.tencent.rtmp.ui.TXCloudVideoView;
import com.tencent.trtc.TRTCCloud; import com.tencent.trtc.TRTCCloud;
import com.tencent.trtc.TRTCCloudDef; import com.tencent.trtc.TRTCCloudDef;
import com.tencent.trtc.TRTCCloudListener; import com.tencent.trtc.TRTCCloudListener;
import com.tencent.trtc.debug.Constant; import com.tencent.trtc.debug.Constant;
import com.tencent.trtc.event.Events;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
/** /**
@ -61,7 +61,7 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
private static final int OVERLAY_PERMISSION_REQ_CODE = 1234; private static final int OVERLAY_PERMISSION_REQ_CODE = 1234;
private TextView mTextTitle; private TextView mTextTitle;
private TXCloudVideoView mTXCVVLocalPreviewView; // private CustomVideoView mTXCVVLocalPreviewView;
private ImageView mImageBack; private ImageView mImageBack;
private ImageView mButtonMuteVideo; private ImageView mButtonMuteVideo;
private ImageView mButtonMuteAudio; private ImageView mButtonMuteAudio;
@ -71,15 +71,17 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
private TRTCCloud mTRTCCloud; private TRTCCloud mTRTCCloud;
private TXDeviceManager mTXDeviceManager; private TXDeviceManager mTXDeviceManager;
private boolean mIsFrontCamera = true; private boolean mIsFrontCamera = true;
private List<String> mRemoteUidList; private List<UserInfo> mUserList;
private List<TXCloudVideoView> mRemoteViewList; private List<CustomVideoView> mRemoteViewList;
private int mUserCount = 0; private int mUserCount = 0;
private String mRoomId; private String mRoomId;
private String mUserId; private String mUserId;
private int sdkAppId; private int sdkAppId;
private String userSig; private String userSig;
private boolean hideSub;
private boolean mAudioRouteFlag = true; private boolean mAudioRouteFlag = true;
private FloatingView mFloatingView;
private UserInfo localUserInfo;
private int getId(String idName, String type) { private int getId(String idName, String type) {
return getResources().getIdentifier(idName, type, getPackageName()); return getResources().getIdentifier(idName, type, getPackageName());
@ -127,7 +129,6 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
// R.id.iv_back // R.id.iv_back
mImageBack = findViewById(getId("iv_back", "id")); mImageBack = findViewById(getId("iv_back", "id"));
// R.id.txcvv_main // R.id.txcvv_main
mTXCVVLocalPreviewView = findViewById(getId("txcvv_main", "id"));
// R.id.btn_mute_video // R.id.btn_mute_video
mButtonMuteVideo = findViewById(getId("btn_mute_video", "id")); mButtonMuteVideo = findViewById(getId("btn_mute_video", "id"));
// R.id.btn_mute_audio // R.id.btn_mute_audio
@ -146,21 +147,45 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
mButtonSwitchCamera.setOnClickListener(this); mButtonSwitchCamera.setOnClickListener(this);
mButtonAudioRoute.setOnClickListener(this); mButtonAudioRoute.setOnClickListener(this);
mRemoteUidList = new ArrayList<>(); mUserList = new ArrayList<>();
localUserInfo = new UserInfo().setPersonid(mUserId).setLocal(true)
.setDisplayName("").setFrontCamera(mIsFrontCamera);
mUserList.add(localUserInfo);
mRemoteViewList = new ArrayList<>(); mRemoteViewList = new ArrayList<>();
// R.id.trtc_view_1 for(int i=0;i<7;i++){
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_1", "id"))); CustomVideoView videoView = ((CustomVideoView) findViewById(getId("trtc_view_"+i, "id"))).setMainView(i == 0);
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_2", "id"))); if(i > 0){
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_3", "id"))); videoView.setOnClickListener((view)->{
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_4", "id"))); if(view instanceof CustomVideoView && !((CustomVideoView) view).isMainView()){
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_5", "id"))); UserInfo mainUser = this.mUserList.get(0);
mRemoteViewList.add((TXCloudVideoView) findViewById(getId("trtc_view_6", "id"))); UserInfo viewUser = ((CustomVideoView) view).getUserInfo();
Collections.swap(this.mUserList,0,this.mUserList.indexOf(viewUser));
mTRTCCloud.stopLocalPreview();
mTRTCCloud.stopLocalAudio();
mRemoteViewList.get(0).changeUser(mTRTCCloud,viewUser);
((CustomVideoView) view).changeUser(mTRTCCloud,mainUser);
try {
JSONObject object = new JSONObject();
object.put("room",mRoomId);
object.put("personid",viewUser.getPersonid());
CordovaEventKit.kit.fireEvent("LayoutChangeMessage",object);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
mRemoteViewList.add(videoView);
}
Events.fireEvent("subview.always.hide",(extra)->extra.putBoolean("alwaysHide",false));
// R.layout.videocall_view_floating_default Events.addListener("userinfo.update",(extra -> {
mFloatingView = new FloatingView(getApplicationContext(), getId("videocall_view_floating_default", "layout")); String user = extra.getString("personid");
// R.layout.videocall_popup_layout int index = this.mUserList.indexOf(user);
mFloatingView.setPopupWindow(getId("videocall_popup_layout", "layout")); if(index > -1){
mFloatingView.setOnPopupItemClickListener(this); this.mUserList.get(index).setDisplayName(extra.getString("displayName"));
}
}));
} }
private void enterRoom() { private void enterRoom() {
@ -174,23 +199,26 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
trtcParams.roomId = Integer.parseInt(mRoomId); trtcParams.roomId = Integer.parseInt(mRoomId);
trtcParams.userSig = this.userSig; trtcParams.userSig = this.userSig;
mTRTCCloud.startLocalPreview(mIsFrontCamera, mTXCVVLocalPreviewView); mRemoteViewList.get(0).changeUser(mTRTCCloud, mUserList.get(0));
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
mTRTCCloud.enterRoom(trtcParams, TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL); mTRTCCloud.enterRoom(trtcParams, TRTCCloudDef.TRTC_APP_SCENE_VIDEOCALL);
JSONObject object = new JSONObject();
try {
object.put("room",mRoomId);
object.put("personid",mUserList.get(0).getPersonid());
CordovaEventKit.kit.fireEvent("LayoutChangeMessage",object);
} catch (JSONException e) {
e.printStackTrace();
}
} }
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
requestDrawOverLays();
} }
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (mFloatingView != null && mFloatingView.isShown()) {
mFloatingView.dismiss();
}
exitRoom(); exitRoom();
} }
@ -208,9 +236,6 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
@Override @Override
protected void onResume() { protected void onResume() {
super.onResume(); super.onResume();
if (mFloatingView != null && mFloatingView.isShown()) {
mFloatingView.dismiss();
}
} }
@Override @Override
@ -232,58 +257,21 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
switchCamera(); switchCamera();
} else if (id == getId("btn_audio_route", "id")) { } else if (id == getId("btn_audio_route", "id")) {
audioRoute(); 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() { private void muteVideo() {
boolean isSelected = mButtonMuteVideo.isSelected(); boolean isSelected = mButtonMuteVideo.isSelected();
if (!isSelected) { if (!isSelected) {
mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_open", "mipmap"))); mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_open", "mipmap")));
closeRemoteVideoViews();
} else { } else {
mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_close", "mipmap"))); mButtonMuteVideo.setImageDrawable(getDrawable(getId("view_close", "mipmap")));
refreshRemoteVideoViews();
} }
Events.fireEvent("subview.always.hide",(extra)->extra.putBoolean("alwaysHide",!isSelected));
mButtonMuteVideo.setSelected(!isSelected); 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() { private void muteAudio() {
boolean isSelected = mButtonMuteAudio.isSelected(); boolean isSelected = mButtonMuteAudio.isSelected();
if (!isSelected) { if (!isSelected) {
@ -298,6 +286,7 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
private void switchCamera() { private void switchCamera() {
mIsFrontCamera = !mIsFrontCamera; mIsFrontCamera = !mIsFrontCamera;
localUserInfo.setFrontCamera(mIsFrontCamera);
mTXDeviceManager.switchCamera(mIsFrontCamera); mTXDeviceManager.switchCamera(mIsFrontCamera);
} }
@ -313,6 +302,17 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
} }
} }
private void refreshRemoteVideoViews() {
for (int i = 0; i < mRemoteViewList.size(); i++) {
if (i < mUserList.size()) {
UserInfo user = mUserList.get(i);
mRemoteViewList.get(i).changeUser(mTRTCCloud,user);
} else {
mRemoteViewList.get(i).changeUser(mTRTCCloud,null);
}
}
}
private class TRTCCloudImplListener extends TRTCCloudListener { private class TRTCCloudImplListener extends TRTCCloudListener {
private WeakReference<VideoCallingActivity> mContext; private WeakReference<VideoCallingActivity> mContext;
@ -325,19 +325,20 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
@Override @Override
public void onUserVideoAvailable(String userId, boolean available) { public void onUserVideoAvailable(String userId, boolean available) {
Log.d(TAG, "onUserVideoAvailable userId " + userId + ", mUserCount " + mUserCount + ",available " + available); Log.d(TAG, "onUserVideoAvailable userId " + userId + ", mUserCount " + mUserCount + ",available " + available);
int index = mRemoteUidList.indexOf(userId); UserInfo info = new UserInfo().setPersonid(userId);
int index = mUserList.indexOf(info);
if (available) { if (available) {
if (index != -1) { if (index != -1) {
return; return;
} }
mRemoteUidList.add(userId); mUserList.add(info);
refreshRemoteVideoViews(); refreshRemoteVideoViews();
} else { } else {
if (index == -1) { if (index == -1) {
return; return;
} }
mTRTCCloud.stopRemoteView(userId); mTRTCCloud.stopRemoteView(userId);
mRemoteUidList.remove(index); mUserList.remove(index);
refreshRemoteVideoViews(); refreshRemoteVideoViews();
} }
@ -345,12 +346,11 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
private void refreshRemoteVideoViews() { private void refreshRemoteVideoViews() {
for (int i = 0; i < mRemoteViewList.size(); i++) { for (int i = 0; i < mRemoteViewList.size(); i++) {
if (i < mRemoteUidList.size()) { if (i < mUserList.size()) {
String remoteUid = mRemoteUidList.get(i); UserInfo user = mUserList.get(i);
mRemoteViewList.get(i).setVisibility(View.VISIBLE); mRemoteViewList.get(i).changeUser(mTRTCCloud,user);
mTRTCCloud.startRemoteView(remoteUid, TRTCCloudDef.TRTC_VIDEO_STREAM_TYPE_SMALL, mRemoteViewList.get(i));
} else { } else {
mRemoteViewList.get(i).setVisibility(View.GONE); mRemoteViewList.get(i).changeUser(mTRTCCloud,null);
} }
} }
} }
@ -377,21 +377,4 @@ public class VideoCallingActivity extends TRTCBaseActivity implements View.OnCli
} }
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);
}
}
}
} }

View File

@ -0,0 +1,24 @@
<?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">
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@+id/video"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/video"
android:textColor="#ffffff"
android:background="@color/colorPrimaryDark"
android:paddingTop="1dp"
android:paddingBottom="1dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textAlignment="center"
android:visibility="gone"
/>
</RelativeLayout>

View File

@ -5,11 +5,11 @@
android:background="@color/rl_main_bg"> android:background="@color/rl_main_bg">
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/txcvv_main" android:id="@+id/trtc_view_0"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginBottom="72dp" /> android:layout_marginBottom="72dp" />
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -38,58 +38,58 @@
</LinearLayout> </LinearLayout>
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/trtc_view_1" android:id="@+id/trtc_view_1"
android:layout_width="90dp" android:layout_width="90dp"
android:layout_height="160dp" android:layout_height="160dp"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_marginTop="70dp" android:layout_marginTop="70dp"
android:layout_marginRight="15dp"/> android:layout_marginRight="15dp" />
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/trtc_view_2" android:id="@+id/trtc_view_2"
android:layout_width="90dp" android:layout_width="90dp"
android:layout_height="160dp" android:layout_height="160dp"
android:layout_below="@id/trtc_view_1" android:layout_below="@id/trtc_view_1"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_marginRight="15dp" android:layout_marginRight="15dp"
android:layout_marginTop="10dp"/> android:layout_marginTop="10dp"/>
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/trtc_view_3" android:id="@+id/trtc_view_3"
android:layout_width="90dp" android:layout_width="90dp"
android:layout_height="160dp" android:layout_height="160dp"
android:layout_below="@id/trtc_view_2" android:layout_below="@id/trtc_view_2"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_marginRight="15dp" android:layout_marginTop="10dp"
android:layout_marginTop="10dp"/> android:layout_marginRight="15dp" />
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/trtc_view_4" android:id="@+id/trtc_view_4"
android:layout_width="90dp" android:layout_width="90dp"
android:layout_height="160dp" android:layout_height="160dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/trtc_view_3" android:layout_alignTop="@+id/trtc_view_3"
android:layout_marginLeft="15dp"/> android:layout_marginLeft="15dp"/>
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/trtc_view_5" android:id="@+id/trtc_view_5"
android:layout_width="90dp" android:layout_width="90dp"
android:layout_height="160dp" android:layout_height="160dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/trtc_view_2" android:layout_alignTop="@+id/trtc_view_2"
android:layout_marginLeft="15dp"/> android:layout_marginLeft="15dp"/>
<com.tencent.rtmp.ui.TXCloudVideoView <com.tencent.trtc.CustomVideoView
android:id="@+id/trtc_view_6" android:id="@+id/trtc_view_6"
android:layout_width="90dp" android:layout_width="90dp"
android:layout_height="160dp" android:layout_height="160dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignTop="@+id/trtc_view_1" android:layout_alignTop="@+id/trtc_view_1"
android:layout_marginLeft="15dp"/> android:layout_marginLeft="15dp"/>
<LinearLayout <LinearLayout

View File

@ -1,18 +0,0 @@
<?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>

View File

@ -1,12 +0,0 @@
<?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>