添加项目

This commit is contained in:
zhanglei
2025-06-30 14:12:14 +08:00
commit 8a60755b60
2374 changed files with 198527 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 31
defaultConfig {
versionName "1.0"
versionCode 1
minSdkVersion 19
targetSdkVersion 26
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
api 'androidx.appcompat:appcompat:1.3.1'
api 'com.google.android.material:material:1.4.0'
api 'androidx.recyclerview:recyclerview:1.2.1'
api 'androidx.constraintlayout:constraintlayout:1.1.3'
api "com.github.bumptech.glide:glide:4.12.0"
def projects = this.rootProject.getAllprojects().stream().map { project -> project.name }.collect()
println "all projects : {$projects}"
if (projects.contains("tuicore")) {
api project(':system:tuicore-7.3.4358')
} else {
api "com.tencent.imsdk:imsdk-plus:7.3.4358"
api "com.tencent.liteav.tuikit:tuicore:1.1.24"
}
if (projects.contains("tuicallengine")) {
api project(':tuicallengine')
} else {
api "com.google.code.gson:gson:2.9.1"
api "com.tencent.liteav:LiteAVSDK_TRTC:latest.release"
api 'com.tencent.liteav.tuikit:tuicallengine:latest.release'
}
}
+21
View File
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tencent.qcloud.tuikit.tuicallkit">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
<!-- Support Android S(31) Bluetooth-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application>
<activity
android:name="com.tencent.qcloud.tuikit.tuicallkit.base.BaseCallActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.NoActionBar" />
<activity
android:name=".extensions.inviteuser.SelectGroupMemberActivity"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.NoActionBar" />
<provider
android:name="com.tencent.qcloud.tuikit.tuicallkit.internal.ServiceInitializer"
android:authorities="${applicationId}.ServiceInitializer"
android:enabled="true"
android:exported="false" />
<service
android:name="com.tencent.qcloud.tuikit.tuicallkit.service.TUICallService"
android:enabled="true"
android:exported="false" />
<service
android:name="com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow.FloatWindowService"
android:enabled="true"
android:exported="false" />
</application>
</manifest>
@@ -0,0 +1,98 @@
package com.tencent.qcloud.tuikit.tuicallkit;
import android.content.Context;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import java.util.List;
public abstract class TUICallKit {
public static TUICallKit createInstance(Context context) {
return TUICallKitImpl.createInstance(context);
}
/**
* Set user profile
*
* @param nickname User name, which can contain up to 500 bytes
* @param avatar User profile photo URL, which can contain up to 500 bytes
* For example: https://liteav.sdk.qcloud.com/app/res/picture/voiceroom/avatar/user_avatar1.png
* @param callback Set the result callback
*/
public void setSelfInfo(String nickname, String avatar, TUICommonDefine.Callback callback) {
}
/**
* Make a call
*
* @param userId callees
* @param callMediaType Call type
*/
public void call(String userId, TUICallDefine.MediaType callMediaType) {
}
/**
* Make a call
*
* @param userId callees
* @param callMediaType Call type
* @param params Extension param: eg: offlinePushInfo
*/
public void call(String userId, TUICallDefine.MediaType callMediaType,
TUICallDefine.CallParams params, TUICommonDefine.Callback callback) {
}
/**
* Make a group call
*
* @param groupId GroupId
* @param userIdList List of userId
* @param callMediaType Call type
*/
public void groupCall(String groupId, List<String> userIdList, TUICallDefine.MediaType callMediaType) {
}
/**
* Make a group call
*
* @param groupId GroupId
* @param userIdList List of userId
* @param callMediaType Call type
* @param params Extension param: eg: offlinePushInfo
*/
public void groupCall(String groupId, List<String> userIdList,
TUICallDefine.MediaType callMediaType, TUICallDefine.CallParams params,
TUICommonDefine.Callback callback) {
}
/**
* Join a current call
*
* @param roomId current call room ID
* @param callMediaType call type
*/
public void joinInGroupCall(TUICommonDefine.RoomId roomId, String groupId, TUICallDefine.MediaType callMediaType) {
}
/**
* Set the ringtone (preferably shorter than 30s)
*
* @param filePath Callee ringtone path
*/
public void setCallingBell(String filePath) {
}
/**
* Enable the mute mode (the callee doesn't ring)
*/
public void enableMuteMode(boolean enable) {
}
/**
* Enable the floating window
*/
public void enableFloatWindow(boolean enable) {
}
}
@@ -0,0 +1,840 @@
package com.tencent.qcloud.tuikit.tuicallkit;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.text.TextUtils;
import com.tencent.liteav.beauty.TXBeautyManager;
import com.tencent.qcloud.tuicore.TUIConstants;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuicore.interfaces.ITUINotification;
import com.tencent.qcloud.tuicore.permission.PermissionCallback;
import com.tencent.qcloud.tuicore.util.SPUtils;
import com.tencent.qcloud.tuicore.util.ToastUtil;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.Scene;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.Status;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallEngine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallObserver;
import com.tencent.qcloud.tuikit.tuicallengine.impl.base.TUILog;
import com.tencent.qcloud.tuikit.tuicallengine.utils.PermissionUtils;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.Constants;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
import com.tencent.qcloud.tuikit.tuicallkit.config.OfflinePushInfoConfig;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.CallingBellFeature;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.CallingKeepAliveFeature;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.CallingScreenSensorFeature;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DeviceUtils;
import com.tencent.qcloud.tuikit.tuicallkit.utils.PermissionRequest;
import com.tencent.qcloud.tuikit.tuicallkit.utils.UserInfoUtils;
import com.tencent.qcloud.tuikit.tuicallkit.view.TUICallingViewManager;
import com.tencent.trtc.TRTCCloud;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public final class TUICallKitImpl extends TUICallKit implements ITUINotification {
private static final String TAG = "TUICallKit";
private static TUICallKitImpl sInstance;
private final Context mContext;
private final UserInfoUtils mUserInfoUtils;
private final CallingKeepAliveFeature mCallingKeepAliveFeature;
private final CallingScreenSensorFeature mCallingScreenSensorFeature;
private final CallingBellFeature mCallingBellFeature;
private final TUICallingViewManager mCallingViewManager;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private Runnable mTimeRunnable;
private int mTimeCount;
private Handler mTimeHandler;
private HandlerThread mTimeHandlerThread;
private long mSelfLowQualityTime;
private long mOtherUserLowQualityTime;
private List<CallingUserModel> mInviteeList = new ArrayList<>();
private CallingUserModel mInviter = new CallingUserModel();
public static TUICallKitImpl createInstance(Context context) {
if (null == sInstance) {
synchronized (TUICallKitImpl.class) {
if (null == sInstance) {
sInstance = new TUICallKitImpl(context);
}
}
}
return sInstance;
}
private TUICallKitImpl(Context context) {
mContext = context.getApplicationContext();
TUICallEngine.createInstance(mContext).addObserver(mTUICallObserver);
mCallingKeepAliveFeature = new CallingKeepAliveFeature(mContext);
mCallingScreenSensorFeature = new CallingScreenSensorFeature(mContext);
mCallingBellFeature = new CallingBellFeature(mContext);
mCallingViewManager = new TUICallingViewManager(mContext);
mUserInfoUtils = new UserInfoUtils();
createTimeHandler();
registerCallingEvent();
}
private void registerCallingEvent() {
//Login Event
TUICore.registerEvent(TUIConstants.TUILogin.EVENT_LOGIN_STATE_CHANGED,
TUIConstants.TUILogin.EVENT_SUB_KEY_USER_LOGIN_SUCCESS, this);
TUICore.registerEvent(TUIConstants.TUILogin.EVENT_LOGIN_STATE_CHANGED,
TUIConstants.TUILogin.EVENT_SUB_KEY_USER_LOGOUT_SUCCESS, this);
//TUICallkit Event
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CALL_STATUS_CHANGED, this);
}
@Override
public void setSelfInfo(String nickname, String avatar, TUICommonDefine.Callback callback) {
TUILog.i(TAG, "setSelfInfo, nickname: " + nickname + " ,avatar: " + avatar);
TUICallEngine.createInstance(mContext).setSelfInfo(nickname, avatar, callback);
}
@Override
public void call(String userId, TUICallDefine.MediaType callMediaType) {
TUICallDefine.CallParams params = new TUICallDefine.CallParams();
params.offlinePushInfo = OfflinePushInfoConfig.createOfflinePushInfo(mContext);
params.timeout = Constants.SIGNALING_MAX_TIME;
call(userId, callMediaType, params, null);
}
@Override
public void call(String userId, TUICallDefine.MediaType callMediaType, TUICallDefine.CallParams params,
TUICommonDefine.Callback callback) {
TUILog.i(TAG, "call, userId: " + userId + " ,callMediaType: " + callMediaType + " ,params: " + params);
if (TextUtils.isEmpty(userId)) {
TUILog.e(TAG, "call failed, userId is empty");
callbackError(callback, TUICallDefine.ERROR_PARAM_INVALID, "call failed, userId is empty");
return;
}
if (TUICallDefine.MediaType.Unknown.equals(callMediaType)) {
TUILog.e(TAG, "call failed, callMediaType is Unknown");
callbackError(callback, TUICallDefine.ERROR_PARAM_INVALID, "call failed, callMediaType is Unknown");
return;
}
PermissionRequest.requestPermissions(mContext, callMediaType, new PermissionCallback() {
@Override
public void onGranted() {
TUICallEngine.createInstance(mContext).call(userId, callMediaType, params,
new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
CallingUserModel model = new CallingUserModel();
model.userId = userId;
mInviteeList.add(model);
TUICallingStatusManager.sharedInstance(mContext).setMediaType(callMediaType);
TUICallingStatusManager.sharedInstance(mContext).setCallRole(TUICallDefine.Role.Caller);
TUICallingStatusManager.sharedInstance(mContext).setCallScene(Scene.SINGLE_CALL);
showCallingView();
mCallingBellFeature.startDialingMusic();
callbackSuccess(callback);
}
@Override
public void onError(int errCode, String errMsg) {
if (errCode == TUICallDefine.ERROR_PACKAGE_NOT_PURCHASED) {
errMsg = mContext.getString(R.string.tuicalling_package_not_purchased);
}
ToastUtil.toastLongMessage(errMsg);
callbackError(callback, errCode, errMsg);
}
});
}
@Override
public void onDenied() {
callbackError(callback, TUICallDefine.ERROR_PERMISSION_DENIED, "permission denied");
resetCall();
}
});
}
@Override
public void groupCall(String groupId, List<String> userIdList, TUICallDefine.MediaType callMediaType) {
TUICallDefine.CallParams params = new TUICallDefine.CallParams();
params.offlinePushInfo = OfflinePushInfoConfig.createOfflinePushInfo(mContext);
params.timeout = Constants.SIGNALING_MAX_TIME;
groupCall(groupId, userIdList, callMediaType, params, null);
}
@Override
public void groupCall(String groupId, List<String> userIdList, TUICallDefine.MediaType callMediaType,
TUICallDefine.CallParams params, TUICommonDefine.Callback callback) {
TUILog.i(TAG, "groupCall, groupId: " + groupId + " ,userIdList: " + userIdList
+ " ,callMediaType: " + callMediaType + " ,params: " + params);
if (TextUtils.isEmpty(groupId)) {
TUILog.e(TAG, "groupCall failed, groupId is empty");
callbackError(callback, TUICallDefine.ERROR_PARAM_INVALID, "groupCall failed, groupId is empty");
return;
}
if (TUICallDefine.MediaType.Unknown.equals(callMediaType)) {
TUILog.e(TAG, "groupCall failed, callMediaType is Unknown");
callbackError(callback, TUICallDefine.ERROR_PARAM_INVALID, "groupCall failed, callMediaType is Unknown");
return;
}
if (userIdList == null || userIdList.isEmpty()) {
TUILog.e(TAG, "groupCall failed, userIdList is empty");
callbackError(callback, TUICallDefine.ERROR_PARAM_INVALID, "groupCall failed, userIdList is empty");
return;
}
if (userIdList.size() >= Constants.MAX_USER) {
ToastUtil.toastLongMessage(mContext.getString(R.string.tuicalling_user_exceed_limit));
TUILog.e(TAG, "groupCall failed, exceeding max user number: 9");
callbackError(callback, TUICallDefine.ERROR_PARAM_INVALID, "groupCall failed, exceeding max user number");
return;
}
PermissionRequest.requestPermissions(mContext, callMediaType, new PermissionCallback() {
@Override
public void onGranted() {
TUICallEngine.createInstance(mContext).groupCall(groupId, userIdList, callMediaType, params,
new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
for (String userId : userIdList) {
if (!TextUtils.isEmpty(userId)) {
CallingUserModel model = new CallingUserModel();
model.userId = userId;
mInviteeList.add(model);
}
}
TUICallingStatusManager.sharedInstance(mContext).setMediaType(callMediaType);
TUICallingStatusManager.sharedInstance(mContext).setCallRole(TUICallDefine.Role.Caller);
TUICallingStatusManager.sharedInstance(mContext).setCallScene(Scene.GROUP_CALL);
TUICallingStatusManager.sharedInstance(mContext).setGroupId(groupId);
showCallingView();
mCallingBellFeature.startDialingMusic();
callbackSuccess(callback);
}
@Override
public void onError(int errCode, String errMsg) {
if (errCode == TUICallDefine.ERROR_PACKAGE_NOT_SUPPORTED) {
errMsg = mContext.getString(R.string.tuicalling_package_not_support);
}
ToastUtil.toastLongMessage(errMsg);
callbackError(callback, errCode, errMsg);
}
});
}
@Override
public void onDenied() {
callbackError(callback, TUICallDefine.ERROR_PERMISSION_DENIED, "permission denied");
resetCall();
}
});
}
private void showCallingView() {
mCallingViewManager.createCallingView(mInviteeList, mInviter);
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.Waiting);
queryUserInfo();
mCallingKeepAliveFeature.startKeepAlive();
mCallingScreenSensorFeature.registerSensorEventListener();
mCallingViewManager.showCallingView();
}
@Override
public void joinInGroupCall(TUICommonDefine.RoomId roomId, String groupId, TUICallDefine.MediaType mediaType) {
TUILog.i(TAG, "joinInGroupCall, roomId: " + roomId + " ,groupId: " + groupId + " ,mediaType: " + mediaType);
int intRoomId = (roomId != null) ? roomId.intRoomId : 0;
String strRoomId = (roomId != null) ? roomId.strRoomId : "";
if (intRoomId <= 0 && TextUtils.isEmpty(strRoomId)) {
TUILog.e(TAG, "joinInGroupCall failed, roomId is invalid");
return;
}
if (TextUtils.isEmpty(groupId)) {
TUILog.e(TAG, "joinInGroupCall failed, groupId is empty");
return;
}
if (TUICallDefine.MediaType.Unknown.equals(mediaType)) {
TUILog.e(TAG, "joinInGroupCall failed, mediaType is unknown");
return;
}
PermissionRequest.requestPermissions(mContext, mediaType, new PermissionCallback() {
@Override
public void onGranted() {
TUICallEngine.createInstance(mContext).joinInGroupCall(roomId, groupId, mediaType,
new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(Status.Accept);
TUICallingStatusManager.sharedInstance(mContext).setMediaType(mediaType);
TUICallingStatusManager.sharedInstance(mContext).setCallScene(Scene.GROUP_CALL);
TUICallingStatusManager.sharedInstance(mContext).setCallRole(TUICallDefine.Role.Called);
TUICallingStatusManager.sharedInstance(mContext).setGroupId(groupId);
mCallingViewManager.createGroupCallingAcceptView();
mCallingViewManager.showCallingView();
showTimeCount();
}
@Override
public void onError(int errCode, String errMsg) {
if (errCode == TUICallDefine.ERROR_PACKAGE_NOT_SUPPORTED) {
errMsg = mContext.getString(R.string.tuicalling_package_not_support);
}
ToastUtil.toastLongMessage(errMsg);
}
});
}
@Override
public void onDenied() {
resetCall();
}
});
}
@Override
public void setCallingBell(String filePath) {
TUILog.i(TAG, "setCallingBell, filePath: " + filePath);
SPUtils.getInstance(CallingBellFeature.PROFILE_TUICALLKIT).put(CallingBellFeature.PROFILE_CALL_BELL, filePath);
}
@Override
public void enableMuteMode(boolean enable) {
TUILog.i(TAG, "enableMuteMode, enable: " + enable);
SPUtils.getInstance(CallingBellFeature.PROFILE_TUICALLKIT).put(CallingBellFeature.PROFILE_MUTE_MODE, enable);
}
@Override
public void enableFloatWindow(boolean enable) {
TUILog.i(TAG, "enableFloatWindow, enable: " + enable);
mCallingViewManager.enableFloatWindow(enable);
}
public void queryOfflineCall() {
if (Status.None.equals(TUICallingStatusManager.sharedInstance(mContext).getCallStatus())) {
TUICallDefine.Role role = TUICallingStatusManager.sharedInstance(mContext).getCallRole();
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
if (TUICallDefine.Role.None.equals(role) || TUICallDefine.MediaType.Unknown.equals(mediaType)) {
return;
}
PermissionRequest.requestPermissions(mContext, mediaType, new PermissionCallback() {
@Override
public void onGranted() {
showCallingView();
}
@Override
public void onDenied() {
TUICallEngine.createInstance(mContext).reject(null);
resetCall();
}
});
}
}
private final TUICallObserver mTUICallObserver = new TUICallObserver() {
@Override
public void onError(int code, String msg) {
super.onError(code, msg);
ToastUtil.toastLongMessage(mContext.getString(R.string.tuicalling_toast_call_error_msg, code, msg));
}
@Override
public void onCallReceived(String callerId, List<String> calleeIdList, String groupId,
TUICallDefine.MediaType callMediaType, String userData) {
super.onCallReceived(callerId, calleeIdList, groupId, callMediaType, userData);
if (TUICallDefine.MediaType.Unknown.equals(callMediaType)) {
return;
}
if (calleeIdList == null || calleeIdList.isEmpty()) {
return;
}
if (calleeIdList.size() >= Constants.MAX_USER) {
ToastUtil.toastLongMessage(mContext.getString(R.string.tuicalling_user_exceed_limit));
return;
}
mInviter.userId = callerId;
for (String userId : calleeIdList) {
if (!TextUtils.isEmpty(userId)) {
CallingUserModel model = new CallingUserModel();
model.userId = userId;
mInviteeList.add(model);
}
}
Scene scene;
if (!TextUtils.isEmpty(groupId)) {
scene = Scene.GROUP_CALL;
} else {
scene = (calleeIdList.size() > 1) ? Scene.MULTI_CALL : Scene.SINGLE_CALL;
}
TUICallingStatusManager.sharedInstance(mContext).setMediaType(callMediaType);
TUICallingStatusManager.sharedInstance(mContext).setCallRole(TUICallDefine.Role.Called);
TUICallingStatusManager.sharedInstance(mContext).setCallScene(scene);
TUICallingStatusManager.sharedInstance(mContext).setGroupId(groupId);
//when app comes back to foreground, start the call
if (!DeviceUtils.isAppRunningForeground(mContext) && !PermissionUtils.hasPermission(mContext)) {
TUILog.w(TAG, "App is in background");
mCallingBellFeature.startRing();
return;
}
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.Waiting);
PermissionRequest.requestPermissions(mContext, callMediaType, new PermissionCallback() {
@Override
public void onGranted() {
showCallingView();
mCallingBellFeature.startRing();
}
@Override
public void onDenied() {
TUICallEngine.createInstance(mContext).reject(null);
resetCall();
}
});
}
@Override
public void onCallCancelled(String callerId) {
super.onCallCancelled(callerId);
resetCall();
}
@Override
public void onCallBegin(TUICommonDefine.RoomId roomId, TUICallDefine.MediaType callMediaType,
TUICallDefine.Role callRole) {
super.onCallBegin(roomId, callMediaType, callRole);
mCallingBellFeature.stopMusic();
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.Accept);
showTimeCount();
}
@Override
public void onCallEnd(TUICommonDefine.RoomId roomId, TUICallDefine.MediaType callMediaType,
TUICallDefine.Role callRole, long totalTime) {
super.onCallEnd(roomId, callMediaType, callRole, totalTime);
resetCall();
}
@Override
public void onCallMediaTypeChanged(TUICallDefine.MediaType oldCallMediaType,
TUICallDefine.MediaType newCallMediaType) {
super.onCallMediaTypeChanged(oldCallMediaType, newCallMediaType);
if (!oldCallMediaType.equals(newCallMediaType)) {
TUICallingStatusManager.sharedInstance(mContext).setMediaType(newCallMediaType);
}
}
@Override
public void onUserReject(String userId) {
super.onUserReject(userId);
CallingUserModel userModel = findCallingUserModel(userId);
mCallingViewManager.userLeave(userModel);
showUserToast(userModel, R.string.tuicalling_toast_user_reject_call);
mInviteeList.remove(userModel);
}
@Override
public void onUserNoResponse(String userId) {
super.onUserNoResponse(userId);
CallingUserModel userModel = findCallingUserModel(userId);
mCallingViewManager.userLeave(userModel);
showUserToast(userModel, R.string.tuicalling_toast_user_not_response);
mInviteeList.remove(userModel);
}
@Override
public void onUserLineBusy(String userId) {
super.onUserLineBusy(userId);
CallingUserModel userModel = findCallingUserModel(userId);
mCallingViewManager.userLeave(userModel);
showUserToast(userModel, R.string.tuicalling_toast_user_busy);
mInviteeList.remove(userModel);
}
@Override
public void onUserJoin(String userId) {
super.onUserJoin(userId);
CallingUserModel userModel = findCallingUserModel(userId);
if (userModel == null) {
userModel = new CallingUserModel();
userModel.userId = userId;
mInviteeList.add(userModel);
}
mCallingViewManager.userEnter(userModel);
}
@Override
public void onUserLeave(String userId) {
super.onUserLeave(userId);
CallingUserModel userModel = findCallingUserModel(userId);
mCallingViewManager.userLeave(userModel);
showUserToast(userModel, R.string.tuicalling_toast_user_end);
mInviteeList.remove(userModel);
}
@Override
public void onUserVideoAvailable(String userId, boolean isVideoAvailable) {
super.onUserVideoAvailable(userId, isVideoAvailable);
CallingUserModel model = findCallingUserModel(userId);
if (model != null && model.isVideoAvailable != isVideoAvailable) {
model.isVideoAvailable = isVideoAvailable;
mCallingViewManager.updateUser(model);
}
}
@Override
public void onUserAudioAvailable(String userId, boolean isAudioAvailable) {
super.onUserAudioAvailable(userId, isAudioAvailable);
CallingUserModel model = findCallingUserModel(userId);
if (model != null && model.isAudioAvailable != isAudioAvailable) {
model.isAudioAvailable = isAudioAvailable;
mCallingViewManager.updateUser(model);
}
}
@Override
public void onUserVoiceVolumeChanged(Map<String, Integer> volumeMap) {
super.onUserVoiceVolumeChanged(volumeMap);
if (Scene.SINGLE_CALL.equals(TUICallingStatusManager.sharedInstance(mContext).getCallScene())) {
return;
}
for (Map.Entry<String, Integer> entry : volumeMap.entrySet()) {
if (null != entry && !TextUtils.isEmpty(entry.getKey())) {
CallingUserModel userModel = findCallingUserModel(entry.getKey());
if (userModel != null && userModel.volume != entry.getValue()) {
userModel.volume = entry.getValue();
mCallingViewManager.updateUser(userModel);
}
}
}
}
@Override
public void onUserNetworkQualityChanged(List<TUICommonDefine.NetworkQualityInfo> networkQualityList) {
super.onUserNetworkQualityChanged(networkQualityList);
updateNetworkQuality(networkQualityList);
}
@Override
public void onKickedOffline() {
super.onKickedOffline();
TUICallEngine.createInstance(mContext).hangup(null);
resetCall();
}
@Override
public void onUserSigExpired() {
super.onUserSigExpired();
TUICallEngine.createInstance(mContext).hangup(null);
resetCall();
}
};
private void resetCall() {
runOnMainThread(new Runnable() {
@Override
public void run() {
stopTimeCount();
mCallingBellFeature.stopMusic();
mCallingKeepAliveFeature.stopKeepAlive();
mCallingScreenSensorFeature.unregisterSensorEventListener();
mCallingViewManager.closeCallingView();
mInviter = new CallingUserModel();
mInviteeList.clear();
TUICallingStatusManager.sharedInstance(mContext).clear();
}
});
}
private void runOnMainThread(Runnable task) {
if (null != task) {
mMainHandler.post(task);
}
}
private void queryUserInfo() {
ArrayList<CallingUserModel> list = new ArrayList<>();
list.add(mInviter);
list.addAll(mInviteeList);
mUserInfoUtils.getUserInfo(list, new UserInfoUtils.UserCallback() {
@Override
public void onSuccess(List<CallingUserModel> list) {
if (null == list || list.isEmpty()) {
return;
}
for (CallingUserModel model : list) {
if (null != model && !TextUtils.isEmpty(model.userId)) {
CallingUserModel userModel = findCallingUserModel(model.userId);
if (null != userModel) {
userModel.userName = model.userName;
userModel.userAvatar = model.userAvatar;
}
}
}
mCallingViewManager.updateCallingUserView(mInviteeList, mInviter);
}
@Override
public void onFailed(int errorCode, String errorMsg) {
}
});
}
private void showUserToast(CallingUserModel model, int msgId) {
if (null == model || TextUtils.isEmpty(model.userId)) {
return;
}
if (!TextUtils.isEmpty(model.userName)) {
ToastUtil.toastLongMessage(mContext.getString(msgId, model.userName));
return;
}
UserInfoUtils userInfoUtils = new UserInfoUtils();
userInfoUtils.getUserInfo(model.userId, new UserInfoUtils.UserCallback() {
@Override
public void onSuccess(List<CallingUserModel> list) {
if (null == list || list.isEmpty() || null == list.get(0) || TextUtils.isEmpty(list.get(0).userId)) {
ToastUtil.toastLongMessage(mContext.getString(msgId, model.userId));
return;
}
model.userName = list.get(0).userName;
model.userAvatar = list.get(0).userAvatar;
ToastUtil.toastLongMessage(mContext.getString(msgId, model.userName));
}
@Override
public void onFailed(int errorCode, String errorMsg) {
ToastUtil.toastLongMessage(mContext.getString(msgId, model.userId));
}
});
}
private CallingUserModel findCallingUserModel(String userId) {
if (TextUtils.isEmpty(userId)) {
return null;
}
if (userId.equals(mInviter.userId)) {
return mInviter;
} else {
for (CallingUserModel model : mInviteeList) {
if (null != model && !TextUtils.isEmpty(model.userId) && userId.equals(model.userId)) {
return model;
}
}
}
return null;
}
private void createTimeHandler() {
mTimeHandlerThread = new HandlerThread("time-count-thread");
mTimeHandlerThread.start();
mTimeHandler = new Handler(mTimeHandlerThread.getLooper());
}
private void showTimeCount() {
if (mTimeRunnable != null) {
return;
}
mTimeCount = 0;
mTimeRunnable = new Runnable() {
@Override
public void run() {
mTimeCount++;
mCallingViewManager.userCallingTimeStr(getShowTime(mTimeCount));
mTimeHandler.postDelayed(mTimeRunnable, 1000);
}
};
mTimeHandler.post(mTimeRunnable);
}
private String getShowTime(int count) {
return mContext.getString(R.string.tuicalling_called_time_format, count / 60, count % 60);
}
private void stopTimeCount() {
mTimeHandler.removeCallbacks(mTimeRunnable);
mTimeRunnable = null;
mTimeCount = 0;
}
private void updateNetworkQuality(List<TUICommonDefine.NetworkQualityInfo> networkQualityList) {
if (networkQualityList.isEmpty()) {
return;
}
TUICommonDefine.NetworkQualityInfo localInfo = null;
Iterator<TUICommonDefine.NetworkQualityInfo> iterator = networkQualityList.iterator();
while (iterator.hasNext()) {
TUICommonDefine.NetworkQualityInfo tempInfo = iterator.next();
if (TUILogin.getLoginUser().equals(tempInfo.userId)) {
localInfo = tempInfo;
break;
}
}
boolean isLocalLowQuality = false;
if (localInfo != null) {
isLocalLowQuality = isLowQuality(localInfo.quality);
}
if (isLocalLowQuality) {
updateLowQualityTip(true);
} else if (iterator.hasNext()) {
TUICommonDefine.NetworkQualityInfo remoteInfo = iterator.next();
if (isLowQuality(remoteInfo.quality)) {
updateLowQualityTip(false);
}
}
}
private boolean isLowQuality(TUICommonDefine.NetworkQuality quality) {
if (null == quality) {
return false;
}
boolean lowQuality;
switch (quality) {
case Vbad:
case Down:
lowQuality = true;
break;
default:
lowQuality = false;
}
return lowQuality;
}
private void updateLowQualityTip(boolean isSelf) {
long currentTime = System.currentTimeMillis();
if (isSelf) {
if (currentTime - mSelfLowQualityTime > Constants.MIN_DURATION_SHOW_LOW_QUALITY) {
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_self_network_low_quality));
mSelfLowQualityTime = currentTime;
}
} else {
if (currentTime - mOtherUserLowQualityTime > Constants.MIN_DURATION_SHOW_LOW_QUALITY) {
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_other_party_network_low_quality));
mOtherUserLowQualityTime = currentTime;
}
}
}
private void initCallEngine() {
TUICallEngine.createInstance(mContext).init(TUILogin.getSdkAppId(), TUILogin.getLoginUser(),
TUILogin.getUserSig(), new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
initCallVideoParams();
initCallBeautyParams();
}
private void initCallVideoParams() {
//set video render params of loginUser
TUICommonDefine.VideoRenderParams renderParams = new TUICommonDefine.VideoRenderParams();
renderParams.fillMode = TUICommonDefine.VideoRenderParams.FillMode.Fill;
renderParams.rotation = TUICommonDefine.VideoRenderParams.Rotation.Rotation_0;
String user = TUILogin.getLoginUser();
TUICallEngine.createInstance(mContext).setVideoRenderParams(user, renderParams, new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
//set video encoder params
TUICommonDefine.VideoEncoderParams encoderParams = new TUICommonDefine.VideoEncoderParams();
encoderParams.resolution = TUICommonDefine.VideoEncoderParams.Resolution.Resolution_640_360;
encoderParams.resolutionMode = TUICommonDefine.VideoEncoderParams.ResolutionMode.Portrait;
TUICallEngine.createInstance(mContext).setVideoEncoderParams(encoderParams, new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
}
private void initCallBeautyParams() {
TRTCCloud trtcCloud = TUICallEngine.createInstance(mContext).getTRTCCloudInstance();
TXBeautyManager txBeautyManager = trtcCloud.getBeautyManager();
txBeautyManager.setBeautyStyle(TXBeautyManager.TXBeautyStyleNature);
txBeautyManager.setBeautyLevel(6);
}
@Override
public void onNotifyEvent(String key, String subKey, Map<String, Object> param) {
//Login Event
if (TUIConstants.TUILogin.EVENT_LOGIN_STATE_CHANGED.equals(key)) {
if (TUIConstants.TUILogin.EVENT_SUB_KEY_USER_LOGOUT_SUCCESS.equals(subKey)) {
//logout succeed: if user is in the call, end the call first and then clean data
TUICallEngine.createInstance(mContext).hangup(null);
TUICallEngine.destroyInstance();
resetCall();
} else if (TUIConstants.TUILogin.EVENT_SUB_KEY_USER_LOGIN_SUCCESS.equals(subKey)) {
TUILog.i(TAG, "login success");
//login succeed: add "TUICallObserver" again
TUICallEngine.createInstance(mContext).addObserver(mTUICallObserver);
//login succeed: initialize "TUICallEngine"
initCallEngine();
}
}
//TUICallkit Event
if (Constants.EVENT_TUICALLING_CHANGED.equals(key)
&& Constants.EVENT_SUB_CALL_STATUS_CHANGED.equals(subKey) && param != null) {
if (TUICallDefine.Status.None.equals(param.get(Constants.CALL_STATUS))) {
resetCall();
}
}
}
public void callbackSuccess(TUICommonDefine.Callback callback) {
if (callback != null) {
callback.onSuccess();
}
}
public void callbackError(TUICommonDefine.Callback callback, int errCode, String errMsg) {
if (callback != null) {
callback.onError(errCode, errMsg);
}
}
}
@@ -0,0 +1,100 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.RelativeLayout;
import androidx.appcompat.app.AppCompatActivity;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DeviceUtils;
import com.tencent.qcloud.tuikit.tuicallkit.view.root.BaseCallView;
public class BaseCallActivity extends AppCompatActivity {
private static AppCompatActivity mActivity;
private static BaseCallView mBaseCallView;
private static RelativeLayout mLayoutContainer;
public static void updateBaseView(BaseCallView view) {
mBaseCallView = view;
if (null != mLayoutContainer && null != mBaseCallView) {
mLayoutContainer.removeAllViews();
if (null != mBaseCallView.getParent()) {
((ViewGroup) mBaseCallView.getParent()).removeView(mBaseCallView);
}
mLayoutContainer.addView(mBaseCallView);
}
}
public static void finishActivity() {
if (null != mActivity) {
mActivity.finish();
}
mActivity = null;
if (null != mBaseCallView && null != mBaseCallView.getParent()) {
((ViewGroup) mBaseCallView.getParent()).removeView(mBaseCallView);
}
mBaseCallView = null;
mLayoutContainer = null;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DeviceUtils.setScreenLockParams(getWindow());
mActivity = this;
setContentView(R.layout.tuicalling_base_activity);
initStatusBar();
}
private void initStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
@Override
protected void onResume() {
super.onResume();
initView();
// clear notifications after a call is processed
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancelAll();
}
private void initView() {
mLayoutContainer = findViewById(R.id.rl_container);
mLayoutContainer.removeAllViews();
if (null != mBaseCallView) {
if (null != mBaseCallView.getParent()) {
((ViewGroup) mBaseCallView.getParent()).removeView(mBaseCallView);
}
mLayoutContainer.addView(mBaseCallView);
}
}
@Override
public void onBackPressed() {
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
@@ -0,0 +1,38 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
import java.util.Objects;
public class CallingUserModel {
public String userId;
public String userAvatar;
public String userName;
public boolean isEnter;
public boolean isAudioAvailable;
public boolean isVideoAvailable;
public int volume;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
CallingUserModel model = (CallingUserModel) o;
return Objects.equals(userId, model.userId);
}
@Override
public String toString() {
return "CallingUserModel{"
+ "userId='" + userId
+ ", userAvatar='" + userAvatar
+ ", userName='" + userName
+ ", isEnter=" + isEnter
+ ", isAudioAvailable=" + isAudioAvailable
+ ", isVideoAvailable=" + isVideoAvailable
+ ", volume=" + volume
+ '}';
}
}
@@ -0,0 +1,30 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
public class Constants {
//信令超时时长,默认 30s
public static int SIGNALING_MAX_TIME = 30;
public static final int MIN_AUDIO_VOLUME = 10;
public static final int MAX_USER = 9;
public static final int MIN_DURATION_SHOW_LOW_QUALITY = 5000; //Minimum interval for displaying poor network
public static final String EVENT_TUICALLING_CHANGED = "eventTUICallingChanged";
public static final String EVENT_SUB_CALL_STATUS_CHANGED = "eventSubCallStatusChanged";
public static final String EVENT_SUB_CALL_TYPE_CHANGED = "eventSubMediaTypeChanged";
public static final String EVENT_SUB_CAMERA_OPEN = "eventSubCameraOpen";
public static final String EVENT_SUB_CAMERA_FRONT = "eventSubCameraFront";
public static final String EVENT_SUB_MIC_STATUS_CHANGED = "eventSubMicStatusChanged";
public static final String EVENT_SUB_AUDIOPLAYDEVICE_CHANGED = "eventSubAudioPlayDeviceChanged";
public static final String EVENT_SUB_GROUP_MEMBER_SELECTED = "eventSubGroupMemberSelected";
public static final String OPEN_CAMERA = "openCamera";
public static final String SWITCH_CAMERA = "switchCamera";
public static final String MUTE_MIC = "muteMic";
public static final String HANDS_FREE = "handsFree";
public static final String CALL_STATUS = "callStatus";
public static final String CALL_MEDIA_TYPE = "callMediaType";
public static final String GROUP_ID = "groupId";
public static final String SELECT_MEMBER_LIST = "selectMemberList";
}
@@ -0,0 +1,149 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
import android.content.Context;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.TUIVideoView;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallEngine;
import com.tencent.qcloud.tuikit.tuicallkit.config.OfflinePushInfoConfig;
import java.util.List;
public class TUICallingAction {
private final Context mContext;
public TUICallingAction(Context context) {
mContext = context;
}
public void inviteUser(List<String> userIdList, TUICommonDefine.ValueCallback callback) {
TUICallDefine.CallParams params = new TUICallDefine.CallParams();
params.offlinePushInfo = OfflinePushInfoConfig.createOfflinePushInfo(mContext);
params.timeout = Constants.SIGNALING_MAX_TIME;
TUICallEngine.createInstance(mContext).inviteUser(userIdList, params, callback);
}
public void accept(TUICommonDefine.Callback callback) {
TUICallEngine.createInstance(mContext).accept(new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.Accept);
if (callback != null) {
callback.onSuccess();
}
}
@Override
public void onError(int errCode, String errMsg) {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.None);
if (callback != null) {
callback.onError(errCode, errMsg);
}
}
});
}
public void reject(TUICommonDefine.Callback callback) {
TUICallEngine.createInstance(mContext).reject(new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.None);
if (callback != null) {
callback.onSuccess();
}
}
@Override
public void onError(int errCode, String errMsg) {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.None);
if (callback != null) {
callback.onError(errCode, errMsg);
}
}
});
}
public void hangup(TUICommonDefine.Callback callback) {
TUICallEngine.createInstance(mContext).hangup(new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.None);
if (callback != null) {
callback.onSuccess();
}
}
@Override
public void onError(int errCode, String errMsg) {
TUICallingStatusManager.sharedInstance(mContext).updateCallStatus(TUICallDefine.Status.None);
if (callback != null) {
callback.onError(errCode, errMsg);
}
}
});
}
public void switchCallMediaType(TUICallDefine.MediaType callMediaType) {
TUICallEngine.createInstance(mContext).switchCallMediaType(callMediaType);
}
public void openCamera(TUICommonDefine.Camera camera, TUIVideoView videoView, TUICommonDefine.Callback callback) {
TUICallEngine.createInstance(mContext).openCamera(camera, videoView, new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
TUICallDefine.Status status = TUICallingStatusManager.sharedInstance(mContext).getCallStatus();
if (!TUICallDefine.Status.None.equals(status)) {
TUICommonDefine.Camera camera = TUICallingStatusManager.sharedInstance(mContext).getFrontCamera();
TUICallingStatusManager.sharedInstance(mContext).updateCameraOpenStatus(true, camera);
TUICallingStatusManager.sharedInstance(mContext).updateFrontCameraStatus(camera);
}
if (callback != null) {
callback.onSuccess();
}
}
@Override
public void onError(int errCode, String errMsg) {
if (callback != null) {
callback.onError(errCode, errMsg);
}
}
});
}
public void closeCamera() {
TUICallEngine.createInstance(mContext).closeCamera();
TUICommonDefine.Camera frontCamera = TUICallingStatusManager.sharedInstance(mContext).getFrontCamera();
TUICallingStatusManager.sharedInstance(mContext).updateCameraOpenStatus(false, frontCamera);
}
public void switchCamera(TUICommonDefine.Camera camera) {
TUICallEngine.createInstance(mContext).switchCamera(camera);
TUICallingStatusManager.sharedInstance(mContext).updateFrontCameraStatus(camera);
}
public void openMicrophone(TUICommonDefine.Callback callback) {
TUICallEngine.createInstance(mContext).openMicrophone(callback);
TUICallingStatusManager.sharedInstance(mContext).updateMicMuteStatus(false);
}
public void closeMicrophone() {
TUICallEngine.createInstance(mContext).closeMicrophone();
TUICallingStatusManager.sharedInstance(mContext).updateMicMuteStatus(true);
}
public void selectAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice device) {
TUICallEngine.createInstance(mContext).selectAudioPlaybackDevice(device);
TUICallingStatusManager.sharedInstance(mContext).updateAudioPlaybackDevice(device);
}
public void startRemoteView(String userId, TUIVideoView videoView, TUICommonDefine.PlayCallback callback) {
TUICallEngine.createInstance(mContext).startRemoteView(userId, videoView, callback);
}
public void stopRemoteView(String userId) {
TUICallEngine.createInstance(mContext).stopRemoteView(userId);
}
}
@@ -0,0 +1,153 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
import android.content.Context;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.TUICallKitImpl;
import java.util.HashMap;
public class TUICallingStatusManager {
private static TUICallingStatusManager sInstance;
private final Context mContext;
private boolean mIsCameraOpen;
private boolean mIsMicMute;
private TUICommonDefine.Camera mIsFrontCamera = TUICommonDefine.Camera.Front;
private TUICommonDefine.AudioPlaybackDevice mAudioDevice = TUICommonDefine.AudioPlaybackDevice.Speakerphone;
private TUICallDefine.Status mCallStatus = TUICallDefine.Status.None;
private TUICallDefine.Role mCallRole = TUICallDefine.Role.None;
private TUICallDefine.MediaType mMediaType = TUICallDefine.MediaType.Unknown;
private TUICallDefine.Scene mCallScene;
private String mGroupId;
public static TUICallingStatusManager sharedInstance(Context context) {
if (null == sInstance) {
synchronized (TUICallKitImpl.class) {
if (null == sInstance) {
sInstance = new TUICallingStatusManager(context);
}
}
}
return sInstance;
}
private TUICallingStatusManager(Context context) {
mContext = context.getApplicationContext();
}
public boolean isCameraOpen() {
return mIsCameraOpen;
}
public boolean isMicMute() {
return mIsMicMute;
}
public TUICommonDefine.Camera getFrontCamera() {
return mIsFrontCamera;
}
public TUICommonDefine.AudioPlaybackDevice getAudioPlaybackDevice() {
return mAudioDevice;
}
public TUICallDefine.Status getCallStatus() {
return mCallStatus;
}
public void updateCallStatus(TUICallDefine.Status status) {
if (mCallStatus.equals(status)) {
return;
}
mCallStatus = status;
HashMap<String, Object> map = new HashMap<>();
map.put(Constants.CALL_STATUS, status);
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CALL_STATUS_CHANGED, map);
}
public void updateCameraOpenStatus(boolean isOpen, TUICommonDefine.Camera isFrontCamera) {
mIsCameraOpen = isOpen;
HashMap<String, Object> map = new HashMap<>();
map.put(Constants.OPEN_CAMERA, isOpen);
if (isOpen) {
map.put(Constants.SWITCH_CAMERA, isFrontCamera);
}
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CAMERA_OPEN, map);
}
public void updateFrontCameraStatus(TUICommonDefine.Camera isFrontCamera) {
mIsFrontCamera = isFrontCamera;
HashMap<String, Object> map = new HashMap<>();
map.put(Constants.SWITCH_CAMERA, isFrontCamera);
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CAMERA_FRONT, map);
}
public void updateMicMuteStatus(boolean isMicMute) {
mIsMicMute = isMicMute;
HashMap<String, Object> map = new HashMap<>();
map.put(Constants.MUTE_MIC, isMicMute);
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_MIC_STATUS_CHANGED, map);
}
public void updateAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice audioPlaybackDevice) {
mAudioDevice = audioPlaybackDevice;
HashMap<String, Object> map = new HashMap<>();
map.put(Constants.HANDS_FREE, audioPlaybackDevice);
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_AUDIOPLAYDEVICE_CHANGED, map);
}
public void setGroupId(String groupId) {
mGroupId = groupId;
}
public String getGroupId() {
return mGroupId;
}
public void setCallRole(TUICallDefine.Role role) {
mCallRole = role;
}
public TUICallDefine.Role getCallRole() {
return mCallRole;
}
public TUICallDefine.MediaType getMediaType() {
return mMediaType;
}
public void setMediaType(TUICallDefine.MediaType type) {
mMediaType = type;
HashMap<String, Object> map = new HashMap<>();
map.put(Constants.CALL_MEDIA_TYPE, mMediaType);
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CALL_TYPE_CHANGED, map);
}
public TUICallDefine.Scene getCallScene() {
return mCallScene;
}
public void setCallScene(TUICallDefine.Scene scene) {
this.mCallScene = scene;
}
public void clear() {
mIsCameraOpen = false;
mIsFrontCamera = TUICommonDefine.Camera.Front;
mIsMicMute = false;
mAudioDevice = TUICommonDefine.AudioPlaybackDevice.Speakerphone;
mCallStatus = TUICallDefine.Status.None;
mCallRole = TUICallDefine.Role.None;
mMediaType = TUICallDefine.MediaType.Unknown;
mCallScene = null;
mGroupId = "";
}
}
@@ -0,0 +1,119 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tencent.qcloud.tuikit.TUIVideoView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
public class UserLayout extends RelativeLayout {
private RelativeLayout mLayoutImageHead;
private TUIVideoView mTUIVideoView;
private ImageView mImgUserAvatar;
private TextView mTextUserName;
private ImageView mImgAudioInput;
private ImageView mImgLoading;
private boolean mMoveAble;
private boolean mMuteAudio = false;
private boolean mDisableAudioImage = false;
public UserLayout(Context context) {
this(context, null);
}
public UserLayout(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
setClickable(true);
}
private void initView() {
LayoutInflater.from(getContext()).inflate(R.layout.tuicalling_group_user_layout, this, true);
mLayoutImageHead = findViewById(R.id.rl_image_head);
mTUIVideoView = findViewById(R.id.tx_cloud_view);
mImgUserAvatar = findViewById(R.id.img_head);
mTextUserName = findViewById(R.id.tv_name);
mImgAudioInput = findViewById(R.id.iv_audio_input);
mImgLoading = findViewById(R.id.img_loading);
}
public TUIVideoView getVideoView() {
return mTUIVideoView;
}
public void addVideoView(TUIVideoView view) {
if (this.mTUIVideoView != null) {
this.removeView(this.mTUIVideoView);
}
this.mTUIVideoView = view;
this.addView(this.mTUIVideoView);
}
public ImageView getAvatarImage() {
return mImgUserAvatar;
}
public void setVideoAvailable(boolean available) {
if (available) {
mTUIVideoView.setVisibility(VISIBLE);
mLayoutImageHead.setVisibility(GONE);
mTextUserName.setVisibility(VISIBLE);
} else {
mTUIVideoView.setVisibility(GONE);
mLayoutImageHead.setVisibility(VISIBLE);
mTextUserName.setVisibility(VISIBLE);
}
}
public void setUserName(String userName) {
mTextUserName.setText(userName);
}
public void setAudioVolume(int vol, boolean isAudioAvailable) {
if (mMuteAudio || mDisableAudioImage) {
return;
}
mImgAudioInput.setVisibility((isAudioAvailable && vol > Constants.MIN_AUDIO_VOLUME) ? VISIBLE : GONE);
}
public void muteMic(boolean mute) {
mMuteAudio = mute;
if (mDisableAudioImage) {
return;
}
mImgAudioInput.setVisibility(mMuteAudio ? GONE : VISIBLE);
}
public void disableAudioImage(boolean enable) {
mDisableAudioImage = enable;
}
public void startLoading() {
mImgLoading.setVisibility(VISIBLE);
ImageLoader.loadGifImage(getContext(), mImgLoading, R.drawable.tuicalling_loading);
}
public void stopLoading() {
mImgLoading.setVisibility(GONE);
}
public boolean isMoveAble() {
return mMoveAble;
}
public void setMoveAble(boolean enable) {
mMoveAble = enable;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return true;
}
}
@@ -0,0 +1,7 @@
package com.tencent.qcloud.tuikit.tuicallkit.base;
public class UserLayoutEntity {
public String userId = "";
public UserLayout layout;
public CallingUserModel userModel;
}
@@ -0,0 +1,34 @@
package com.tencent.qcloud.tuikit.tuicallkit.config;
import android.content.Context;
import android.text.TextUtils;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
public class OfflinePushInfoConfig {
//Customize offline push information
public static TUICallDefine.OfflinePushInfo createOfflinePushInfo(Context context) {
TUICallDefine.OfflinePushInfo pushInfo = new TUICallDefine.OfflinePushInfo();
pushInfo.setTitle(TextUtils.isEmpty(TUILogin.getNickName()) ? TUILogin.getUserId() : TUILogin.getNickName());
pushInfo.setDesc(context.getString(R.string.tuicalling_have_a_new_call));
// OPPO必须设置ChannelID才可以收到推送消息,如果在控制台已经配置,代码中无需调用
// OPPO must set a ChannelID to receive push messages. If you set it on the console, you don't need set here.
// pushInfo.setAndroidOPPOChannelID("tuikit");
pushInfo.setIgnoreIOSBadge(false);
pushInfo.setIOSSound("phone_ringing.mp3");
pushInfo.setAndroidSound("phone_ringing");
//VIVO message type: 0-push message, 1-System message(have a higher delivery rate)
pushInfo.setAndroidVIVOClassification(1);
//FCM channel ID, you need change PrivateConstants.java and set "fcmPushChannelId",
//If you set it on the console, you don't need set here.
//pushInfo.setAndroidFCMChannelID("fcm_push_channel");
//HuaWei message type: https://developer.huawei.com/consumer/cn/doc/development/HMSCore-Guides/message-classification-0000001149358835
pushInfo.setAndroidHuaWeiCategory("IM");
//IOS push type: if you want user VoIP, please modify type to TUICallDefine.IOSOfflinePushType.VoIP
pushInfo.setIOSPushType(TUICallDefine.IOSOfflinePushType.APNs);
return pushInfo;
}
}
@@ -0,0 +1,133 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.HandlerThread;
import android.text.TextUtils;
import com.tencent.qcloud.tuicore.util.SPUtils;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import java.io.File;
public class CallingBellFeature {
public static final String PROFILE_TUICALLKIT = "per_profile_tuicallkit";
public static final String PROFILE_CALL_BELL = "per_call_bell";
public static final String PROFILE_MUTE_MODE = "per_mute_mode";
private Context mContext;
private final MediaPlayer mMediaPlayer;
private Handler mHandler;
private int mCallingBellResourceId = -1;
private String mCallingBellResourcePath;
public CallingBellFeature(Context context) {
mContext = context.getApplicationContext();
mMediaPlayer = new MediaPlayer();
}
public void startDialingMusic() {
start("", R.raw.phone_dialing);
}
public void startRing() {
if (SPUtils.getInstance(PROFILE_TUICALLKIT).getBoolean(PROFILE_MUTE_MODE, false)) {
return;
}
String path = SPUtils.getInstance(PROFILE_TUICALLKIT).getString(PROFILE_CALL_BELL, "");
if (TextUtils.isEmpty(path)) {
start("", R.raw.phone_ringing);
} else {
start(path, -1);
}
}
public void stopMusic() {
stop();
}
private void start(String resPath, int resId) {
preHandler();
if (TextUtils.isEmpty(resPath) && (-1 == resId)) {
return;
}
if ((-1 != resId && (mCallingBellResourceId == resId))
|| (!TextUtils.isEmpty(resPath) && TextUtils.equals(mCallingBellResourcePath, resPath))) {
return;
}
if (!TextUtils.isEmpty(resPath) && isUrl(resPath)) {
return;
}
AssetFileDescriptor afd0 = null;
if (!TextUtils.isEmpty(resPath) && new File(resPath).exists()) {
mCallingBellResourcePath = resPath;
} else if (-1 != resId) {
mCallingBellResourceId = resId;
afd0 = mContext.getResources().openRawResourceFd(resId);
}
final AssetFileDescriptor afd = afd0;
mHandler.post(new Runnable() {
@Override
public void run() {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mMediaPlayer.reset();
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
if (null != afd) {
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
} else if (!TextUtils.isEmpty(mCallingBellResourcePath)) {
mMediaPlayer.setDataSource(mCallingBellResourcePath);
} else {
return;
}
mMediaPlayer.setLooping(true);
mMediaPlayer.prepare();
mMediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private boolean isUrl(String url) {
return url.startsWith("http://") || url.startsWith("https://");
}
private void preHandler() {
if (null != mHandler) {
return;
}
HandlerThread thread = new HandlerThread("CallingBell");
thread.start();
mHandler = new Handler(thread.getLooper());
}
private void stop() {
if (null == mHandler) {
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.stop();
}
mCallingBellResourceId = -1;
mCallingBellResourcePath = "";
}
});
}
}
@@ -0,0 +1,32 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions;
import android.content.Context;
import com.tencent.qcloud.tuikit.tuicallkit.service.TUICallService;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DeviceUtils;
public class CallingKeepAliveFeature {
private Context mContext;
private boolean mEnableKeepAlive = true;
public CallingKeepAliveFeature(Context context) {
mContext = context;
}
public void enableKeepAlive(boolean enable) {
mEnableKeepAlive = enable;
}
public void startKeepAlive() {
if (!mEnableKeepAlive) {
return;
}
TUICallService.start(mContext);
}
public void stopKeepAlive() {
if (DeviceUtils.isServiceRunning(mContext, TUICallService.class.getName())) {
TUICallService.stop(mContext);
}
}
}
@@ -0,0 +1,72 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.PowerManager;
public class CallingScreenSensorFeature {
private Context mContext;
private SensorManager mSensorManager;
private SensorEventListener mSensorEventListener;
private boolean mEnableCloseScreenNearEar = false;
public CallingScreenSensorFeature(Context context) {
mContext = context;
}
public void enableScreenSensor(boolean enable) {
mEnableCloseScreenNearEar = enable;
}
public void registerSensorEventListener() {
if (!mEnableCloseScreenNearEar) {
return;
}
if (null != mSensorManager) {
return;
}
mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
final PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK,
"TUICalling:TRTCAudioCallWakeLock");
mSensorEventListener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
if (event.values[0] == 0.0) {
if (wakeLock.isHeld()) {
return;
} else {
wakeLock.acquire();
}
} else {
if (!wakeLock.isHeld()) {
return;
} else {
wakeLock.setReferenceCounted(false);
wakeLock.release();
}
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
};
mSensorManager.registerListener(mSensorEventListener, sensor, SensorManager.SENSOR_DELAY_FASTEST);
}
public void unregisterSensorEventListener() {
if (null != mSensorManager && null != mSensorEventListener) {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
mSensorManager.unregisterListener(mSensorEventListener, sensor);
mSensorManager = null;
}
}
}
@@ -0,0 +1,8 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.inviteuser;
class GroupMemberInfo {
protected String userId;
protected String userName;
protected String avatar;
protected boolean isSelected;
}
@@ -0,0 +1,132 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.inviteuser;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.tencent.imsdk.v2.V2TIMGroupMemberFullInfo;
import com.tencent.imsdk.v2.V2TIMGroupMemberInfoResult;
import com.tencent.imsdk.v2.V2TIMManager;
import com.tencent.imsdk.v2.V2TIMValueCallback;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.Constants;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class SelectGroupMemberActivity extends AppCompatActivity {
private static AppCompatActivity mActivity;
private RecyclerView mRecyclerUserList;
private String mGroupId;
private List<GroupMemberInfo> mGroupMemberList = new ArrayList<>();
private List<String> mAlreadySelectList = new ArrayList<>();
private SelectGroupMemberAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tuicallkit_activity_group_user);
mActivity = this;
initStatusBar();
initView();
initData();
}
private void initView() {
Toolbar toolbar = findViewById(R.id.toolbar_group);
toolbar.setNavigationOnClickListener(v -> finish());
Button btnOK = findViewById(R.id.btn_group_ok);
btnOK.setOnClickListener(v -> {
if (mAdapter != null) {
List<String> selectUsers = new ArrayList<>();
for (GroupMemberInfo info : mGroupMemberList) {
if (info != null && !TextUtils.isEmpty(info.userId) && info.isSelected) {
selectUsers.add(info.userId);
}
}
Map<String, Object> map = new HashMap<>();
map.put(Constants.SELECT_MEMBER_LIST, selectUsers);
TUICore.notifyEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_GROUP_MEMBER_SELECTED, map);
}
finish();
});
mRecyclerUserList = findViewById(R.id.rv_user_list);
}
private void initData() {
Intent intent = getIntent();
mGroupId = intent.getStringExtra(Constants.GROUP_ID);
mAlreadySelectList = new ArrayList<>(intent.getStringArrayListExtra(Constants.SELECT_MEMBER_LIST));
mAdapter = new SelectGroupMemberAdapter();
mRecyclerUserList.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
mRecyclerUserList.setAdapter(mAdapter);
updateGroupUserList();
}
private void updateGroupUserList() {
int filter = V2TIMGroupMemberFullInfo.V2TIM_GROUP_MEMBER_FILTER_ALL;
V2TIMManager.getGroupManager().getGroupMemberList(mGroupId, filter, 0,
new V2TIMValueCallback<V2TIMGroupMemberInfoResult>() {
@Override
public void onSuccess(V2TIMGroupMemberInfoResult v2TIMGroupMemberInfoResult) {
List<V2TIMGroupMemberFullInfo> results = v2TIMGroupMemberInfoResult.getMemberInfoList();
mGroupMemberList.clear();
for (V2TIMGroupMemberFullInfo info : results) {
GroupMemberInfo userInfo = new GroupMemberInfo();
userInfo.userId = info.getUserID();
userInfo.avatar = info.getFaceUrl();
userInfo.userName = info.getNickName();
userInfo.isSelected = mAlreadySelectList.contains(userInfo.userId);
mGroupMemberList.add(userInfo);
}
if (mAdapter != null) {
mAdapter.setDataSource(mGroupMemberList);
mAdapter.notifyDataSetChanged();
}
}
@Override
public void onError(int errorCode, String errorMsg) {
}
});
}
public static void finishActivity() {
if (mActivity == null || mActivity.isFinishing()) {
return;
}
mActivity.finish();
mActivity = null;
}
private void initStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
Window window = getWindow();
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
window.setStatusBarColor(Color.TRANSPARENT);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}
}
}
@@ -0,0 +1,81 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.inviteuser;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
import java.util.ArrayList;
import java.util.List;
public class SelectGroupMemberAdapter extends RecyclerView.Adapter<SelectGroupMemberAdapter.GroupMemberViewHolder> {
private Context mContext;
private List<GroupMemberInfo> mGroupMemberList = new ArrayList<>();
public void setDataSource(List<GroupMemberInfo> userList) {
mGroupMemberList = userList;
}
@NonNull
@Override
public GroupMemberViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext();
View view = LayoutInflater.from(mContext).inflate(R.layout.tuicallkit_group_user_list_item, parent, false);
return new GroupMemberViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull GroupMemberViewHolder holder, int position) {
GroupMemberInfo userInfo = mGroupMemberList.get(position);
holder.itemView.setOnClickListener(v -> {
holder.mCheckBox.setChecked(!holder.mCheckBox.isChecked());
userInfo.isSelected = holder.mCheckBox.isChecked();
});
holder.layoutView(userInfo);
}
@Override
public int getItemCount() {
return mGroupMemberList.size();
}
class GroupMemberViewHolder extends RecyclerView.ViewHolder {
private ImageView mImageAvatar;
private TextView mTextName;
private TextView mTextHint;
private CheckBox mCheckBox;
public GroupMemberViewHolder(@NonNull View itemView) {
super(itemView);
mCheckBox = itemView.findViewById(R.id.group_user_check_box);
mImageAvatar = itemView.findViewById(R.id.group_user_avatar);
mTextName = itemView.findViewById(R.id.group_user_name);
mTextHint = itemView.findViewById(R.id.group_user_hint);
}
public void layoutView(GroupMemberInfo userInfo) {
if (userInfo == null || TextUtils.isEmpty(userInfo.userId)) {
return;
}
itemView.setEnabled(!userInfo.isSelected);
mCheckBox.setEnabled(!userInfo.isSelected);
mCheckBox.setChecked(userInfo.isSelected);
mCheckBox.setSelected(userInfo.isSelected);
mTextName.setText((TextUtils.isEmpty(userInfo.userName)) ? userInfo.userId : userInfo.userName);
mTextHint.setVisibility((userInfo.userId.equals(TUILogin.getLoginUser())) ? View.VISIBLE : View.GONE);
ImageLoader.loadImage(mContext, mImageAvatar, userInfo.avatar, R.drawable.tuicalling_ic_avatar);
}
}
}
@@ -0,0 +1,310 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.recents;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.bottomsheet.BottomSheetDialog;
import com.google.android.material.tabs.TabLayout;
import com.tencent.qcloud.tuicore.TUIConstants;
import com.tencent.qcloud.tuicore.TUIConstants.TUICalling.ObjectFactory.RecentCalls;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.TUICallKit;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.recents.interfaces.ICallRecordItemListener;
import com.tencent.qcloud.tuikit.tuicallkit.view.common.SlideRecyclerView;
import java.util.ArrayList;
import java.util.List;
public class RecentCallsFragment extends Fragment {
public static final String TYPE_ALL = "AllCall";
public static final String TYPE_MISS = "MissedCall";
private View mRootView;
private Button mButtonEdit;
private Button mButtonStartCall;
private Button mButtonEditDone;
private Button mButtonClear;
private TabLayout mLayoutTab;
private SlideRecyclerView mRecyclerRecent;
private BottomSheetDialog mBottomSheetDialog;
private ConstraintLayout mLayoutTitle;
private String mType = RecentCallsFragment.TYPE_ALL;
private RecentCallsViewModel mViewModel;
private RecentCallsListAdapter mListAdapter;
private String mChatViewStyle = RecentCalls.UI_STYLE_MINIMALIST;
public RecentCallsFragment() {
}
public RecentCallsFragment(String style) {
mChatViewStyle = style;
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
mRootView = inflater.inflate(R.layout.tuicallkit_record_fragment_main, container, false);
initView();
initData();
initListener();
return mRootView;
}
@Override
public void onResume() {
super.onResume();
if (mViewModel != null) {
mViewModel.queryRecentCalls(getFilter());
}
}
private void initView() {
mButtonEdit = mRootView.findViewById(R.id.btn_call_edit);
mButtonStartCall = mRootView.findViewById(R.id.btn_start_call);
mButtonEditDone = mRootView.findViewById(R.id.btn_edit_done);
mButtonClear = mRootView.findViewById(R.id.btn_clear);
mLayoutTab = mRootView.findViewById(R.id.tab_layout);
mRecyclerRecent = mRootView.findViewById(R.id.recycle_view_list);
mLayoutTitle = mRootView.findViewById(R.id.cl_record_title);
if (TUIConstants.TUICalling.ObjectFactory.RecentCalls.UI_STYLE_MINIMALIST.equals(mChatViewStyle)) {
mLayoutTitle.setBackgroundColor(getResources().getColor(R.color.tuicalling_color_white));
}
}
private void initData() {
mListAdapter = new RecentCallsListAdapter();
mListAdapter.setHasStableIds(true);
mRecyclerRecent.setLayoutManager(new LinearLayoutManager(getContext()));
mRecyclerRecent.setAdapter(mListAdapter);
setAdapterListener();
mViewModel = new ViewModelProvider(requireActivity()).get(RecentCallsViewModel.class);
mViewModel.getCallHistoryList().observe(requireActivity(), recordList -> {
if (mListAdapter != null && TYPE_ALL.equals(mType)) {
mListAdapter.onDataSourceChanged(recordList);
}
});
mViewModel.getCallMissedList().observe(requireActivity(), recordList -> {
if (mListAdapter != null && TYPE_MISS.equals(mType)) {
mListAdapter.onDataSourceChanged(recordList);
}
});
if (mViewModel != null) {
mViewModel.queryRecentCalls(getFilter());
}
}
private TUICallDefine.RecentCallsFilter getFilter() {
TUICallDefine.RecentCallsFilter filter = new TUICallDefine.RecentCallsFilter();
if (RecentCallsFragment.TYPE_MISS.equals(mType)) {
filter.result = TUICallDefine.CallRecords.Result.Missed;
}
return filter;
}
private void initListener() {
mButtonEdit.setOnClickListener(v -> {
startMultiSelect();
updateTabViews(true);
});
mButtonStartCall.setOnClickListener(v -> {
TUICore.startActivity("StartC2CChatMinimalistActivity", null);
});
mButtonEditDone.setOnClickListener(v -> {
stopMultiSelect();
updateTabViews(false);
});
mButtonClear.setOnClickListener(v -> {
showDeleteHistoryDialog();
});
mLayoutTab.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
mType = (tab.getPosition() == 1) ? TYPE_MISS : TYPE_ALL;
updateTabViews(false);
stopMultiSelect();
refreshData();
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
private void refreshData() {
if (mViewModel != null) {
mViewModel.queryRecentCalls(getFilter());
}
}
private void updateTabViews(boolean isEditable) {
if (isEditable) {
mButtonEdit.setVisibility(View.GONE);
mButtonStartCall.setVisibility(View.GONE);
mButtonEditDone.setVisibility(View.VISIBLE);
mButtonClear.setVisibility(View.VISIBLE);
} else {
mButtonEdit.setVisibility(View.VISIBLE);
mButtonStartCall.setVisibility(View.GONE);
mButtonEditDone.setVisibility(View.GONE);
mButtonClear.setVisibility(View.GONE);
}
}
private void setAdapterListener() {
mListAdapter.setOnCallRecordItemListener(new ICallRecordItemListener() {
@Override
public void onItemClick(View view, int viewType, TUICallDefine.CallRecords callRecords) {
if (callRecords == null) {
return;
}
if (mListAdapter.isMultiSelectMode()) {
return;
}
if (callRecords.scene.equals(TUICallDefine.Scene.GROUP_CALL)) {
startGroupInfoActivity(callRecords);
Toast.makeText(getContext(), R.string.tuicallkit_group_recall_unsupport, Toast.LENGTH_SHORT).show();
return;
}
if (TUICallDefine.Role.Caller.equals(callRecords.role)) {
TUICallKit.createInstance(getContext()).call(callRecords.inviteList.get(0), callRecords.mediaType);
} else {
TUICallKit.createInstance(getContext()).call(callRecords.inviter, callRecords.mediaType);
}
}
@Override
public void onItemDeleteClick(View view, int viewType, TUICallDefine.CallRecords callRecords) {
if (callRecords == null) {
return;
}
List<TUICallDefine.CallRecords> list = new ArrayList<>();
list.add(callRecords);
deleteRecordCalls(list);
}
@Override
public void onDetailViewClick(View view, TUICallDefine.CallRecords records) {
if (records == null) {
return;
}
if (TUICallDefine.Scene.SINGLE_CALL.equals(records.scene)) {
startFriendProfileActivity(records);
} else if (TUICallDefine.Scene.GROUP_CALL.equals(records.scene)) {
startGroupInfoActivity(records);
}
}
});
}
private void startFriendProfileActivity(TUICallDefine.CallRecords records) {
Bundle bundle = new Bundle();
if (TUICallDefine.Role.Caller.equals(records.role)) {
bundle.putString(TUIConstants.TUIChat.CHAT_ID, records.inviteList.get(0));
} else {
bundle.putString(TUIConstants.TUIChat.CHAT_ID, records.inviter);
}
String activityName = "FriendProfileActivity";
if (TUIConstants.TUICalling.ObjectFactory.RecentCalls.UI_STYLE_MINIMALIST.equals(mChatViewStyle)) {
activityName = "FriendProfileMinimalistActivity";
}
TUICore.startActivity(activityName, bundle);
}
private void startGroupInfoActivity(TUICallDefine.CallRecords records) {
Bundle bundle = new Bundle();
bundle.putString("group_id", records.groupId);
String activityName = "GroupInfoActivity";
if (TUIConstants.TUICalling.ObjectFactory.RecentCalls.UI_STYLE_MINIMALIST.equals(mChatViewStyle)) {
activityName = "GroupInfoMinimalistActivity";
}
TUICore.startActivity(getContext(), activityName, bundle);
}
private void startMultiSelect() {
RecentCallsListAdapter adapter = (RecentCallsListAdapter) mRecyclerRecent.getAdapter();
if (adapter != null) {
adapter.setShowMultiSelectCheckBox(true);
adapter.notifyDataSetChanged();
}
mRecyclerRecent.disableRecyclerViewSlide(true);
mRecyclerRecent.closeMenu();
}
private void stopMultiSelect() {
RecentCallsListAdapter adapter = (RecentCallsListAdapter) mRecyclerRecent.getAdapter();
if (adapter != null) {
adapter.setShowMultiSelectCheckBox(false);
adapter.notifyDataSetChanged();
}
mRecyclerRecent.disableRecyclerViewSlide(false);
mRecyclerRecent.closeMenu();
}
private void deleteRecordCalls(List<TUICallDefine.CallRecords> selectItem) {
if (mViewModel != null) {
mViewModel.deleteRecordCalls(selectItem);
}
stopMultiSelect();
}
private void clearRecentCalls() {
List<TUICallDefine.CallRecords> selectedItems = new ArrayList<>();
if (mListAdapter != null) {
selectedItems = mListAdapter.getSelectedItem();
}
if (selectedItems == null) {
return;
}
List<TUICallDefine.CallRecords> recordList = new ArrayList<>();
for (TUICallDefine.CallRecords records : selectedItems) {
if (records != null && !TextUtils.isEmpty(records.callId)) {
recordList.add(records);
}
}
if (mViewModel != null) {
mViewModel.deleteRecordCalls(recordList);
}
}
private void showDeleteHistoryDialog() {
if (mBottomSheetDialog == null) {
mBottomSheetDialog = new BottomSheetDialog(getContext(), R.style.TUICallBottomSelectSheet);
}
mBottomSheetDialog.setContentView(R.layout.tuicallkit_record_dialog);
mBottomSheetDialog.setCanceledOnTouchOutside(false);
TextView textPositive = mBottomSheetDialog.findViewById(R.id.tv_clear_call_history);
TextView textCancel = mBottomSheetDialog.findViewById(R.id.tv_clear_cancel);
textPositive.setOnClickListener(v -> {
clearRecentCalls();
mBottomSheetDialog.dismiss();
stopMultiSelect();
});
textCancel.setOnClickListener(v -> {
mBottomSheetDialog.dismiss();
});
mBottomSheetDialog.show();
}
}
@@ -0,0 +1,120 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.recents;
import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.recyclerview.widget.RecyclerView;
import com.tencent.imsdk.v2.V2TIMManager;
import com.tencent.imsdk.v2.V2TIMUserFullInfo;
import com.tencent.imsdk.v2.V2TIMValueCallback;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuicore.util.DateTimeUtil;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class RecentCallsItemHolder extends RecyclerView.ViewHolder {
protected RecordsIconView mCallIconView;
protected TextView mTextUserTitle;
protected ImageView mImageMediaType;
protected TextView mTextCallStatus;
protected TextView mTextCallTime;
protected ImageView mImageDetails;
protected RelativeLayout mLayoutDelete;
protected CheckBox mCheckBoxSelectCall;
protected ConstraintLayout mLayoutView;
public RecentCallsItemHolder(@NonNull View itemView) {
super(itemView);
initView();
}
private void initView() {
mLayoutDelete = itemView.findViewById(R.id.ll_call_delete);
mCheckBoxSelectCall = itemView.findViewById(R.id.cb_call_select);
mCallIconView = itemView.findViewById(R.id.call_icon);
mTextUserTitle = itemView.findViewById(R.id.tv_call_user_id);
mImageMediaType = itemView.findViewById(R.id.call_media_type);
mTextCallStatus = itemView.findViewById(R.id.tv_call_status);
mTextCallTime = itemView.findViewById(R.id.tv_call_time);
mImageDetails = itemView.findViewById(R.id.img_call_details);
mLayoutView = itemView.findViewById(R.id.cl_info_layout);
}
public void layoutViews(Context context, TUICallDefine.CallRecords records, int position) {
if (records == null) {
return;
}
int colorId = TUICallDefine.CallRecords.Result.Missed.equals(records.result)
? R.color.tuicallkit_record_text_red : R.color.tuicalling_color_black;
mTextUserTitle.setTextColor(context.getResources().getColor(colorId));
int imageId = TUICallDefine.MediaType.Video.equals(records.mediaType)
? R.drawable.tuicallkit_record_ic_video_call : R.drawable.tuicallkit_ic_audio_call;
mImageMediaType.setImageDrawable(context.getResources().getDrawable(imageId));
mTextCallStatus.setText(String.valueOf(records.result));
mTextCallTime.setText(DateTimeUtil.getTimeFormatText(new Date(records.beginTime)));
List<String> list = new ArrayList<>();
if (records.inviteList != null) {
list.addAll(records.inviteList);
}
list.add(records.inviter.trim());
list.remove(TUILogin.getLoginUser());
mCallIconView.setTag(list);
V2TIMManager.getInstance().getUsersInfo(list, new V2TIMValueCallback<List<V2TIMUserFullInfo>>() {
@Override
public void onSuccess(List<V2TIMUserFullInfo> userFullInfoList) {
if (null == userFullInfoList || userFullInfoList.isEmpty()) {
return;
}
List<Object> avatarList = new ArrayList<>();
List<String> newUserList = new ArrayList<>();
List<String> nameList = new ArrayList<>();
for (int i = 0; i < userFullInfoList.size(); i++) {
avatarList.add(userFullInfoList.get(i).getFaceUrl());
newUserList.add(userFullInfoList.get(i).getUserID());
String name = (TextUtils.isEmpty(userFullInfoList.get(i).getNickName())) ?
userFullInfoList.get(i).getUserID() : userFullInfoList.get(i).getNickName();
nameList.add(name);
}
if (!TextUtils.isEmpty(records.groupId)) {
avatarList.add(TUILogin.getFaceUrl());
}
List<String> oldUserList = new ArrayList<>((List<String>) mCallIconView.getTag());
if (oldUserList.size() == newUserList.size() && oldUserList.containsAll(newUserList)) {
mCallIconView.setImageId(records.callId);
mCallIconView.displayImage(avatarList).load(records.callId);
mTextUserTitle.setText(String.valueOf(nameList).replaceAll(("[\\[\\]]"), ""));
}
}
@Override
public void onError(int errorCode, String errorMsg) {
List<Object> list = new ArrayList<>();
list.add(TUILogin.getFaceUrl());
mCallIconView.displayImage(list).load(records.callId);
mTextUserTitle.setText(TUILogin.getNickName());
}
});
}
}
@@ -0,0 +1,207 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.recents;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.recents.interfaces.ICallRecordItemListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class RecentCallsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ITEM_TYPE_HEADER = 101;
private static final int ITEM_TYPE_NORMAL = -98;
private static final int HEADER_COUNT = 1;
private Context mContext;
private List<TUICallDefine.CallRecords> mDataSource = new ArrayList<>();
private ICallRecordItemListener mItemListener;
private HashMap<String, Boolean> mSelectedPositions = new HashMap<>();
private boolean mIsMultiSelectMode = false;
public void setOnCallRecordItemListener(ICallRecordItemListener listener) {
this.mItemListener = listener;
}
public void onDataSourceChanged(List<TUICallDefine.CallRecords> dataSource) {
mDataSource.clear();
mDataSource.addAll(dataSource);
notifyDataSetChanged();
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
mContext = parent.getContext().getApplicationContext();
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == ITEM_TYPE_HEADER) {
View view = inflater.inflate(R.layout.tuicallkit_item_head_view, parent, false);
return new HeaderViewHolder(view);
} else {
View view = inflater.inflate(R.layout.tuicallkit_layout_call_list_item, parent, false);
return new RecentCallsItemHolder(view);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof RecentCallsItemHolder) {
RecentCallsItemHolder viewHolder = (RecentCallsItemHolder) holder;
viewHolder.mLayoutView.setOnClickListener(v -> {
if (mItemListener != null) {
int curPos = viewHolder.getBindingAdapterPosition();
mItemListener.onItemClick(viewHolder.itemView, getItemViewType(curPos), getItem(curPos));
}
});
viewHolder.mImageDetails.setOnClickListener(view -> {
if (mItemListener != null) {
mItemListener.onDetailViewClick(view, getItem(viewHolder.getBindingAdapterPosition()));
}
});
//左滑删除按钮事件监听
if (!viewHolder.mLayoutDelete.hasOnClickListeners()) {
viewHolder.mLayoutDelete.setOnClickListener(view -> {
int curPos = viewHolder.getBindingAdapterPosition();
TUICallDefine.CallRecords record = getItem(curPos);
if (record == null || TextUtils.isEmpty(record.callId)) {
return;
}
setItemChecked(record.callId, true);
if (mItemListener != null) {
mItemListener.onItemDeleteClick(view, getItemViewType(curPos), record);
}
});
}
viewHolder.layoutViews(mContext, getItem(position), position);
setCheckBoxStatus(viewHolder);
}
}
@Override
public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
if (holder instanceof RecentCallsItemHolder) {
((RecentCallsItemHolder) holder).mCallIconView.clear();
}
}
private TUICallDefine.CallRecords getItem(int position) {
if (mDataSource == null || mDataSource.isEmpty()) {
return null;
}
int dataPosition = position - HEADER_COUNT;
if (dataPosition < mDataSource.size() && dataPosition >= 0) {
return mDataSource.get(dataPosition);
}
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getItemCount() {
if (mDataSource == null) {
return HEADER_COUNT;
}
return mDataSource.size() + HEADER_COUNT;
}
@Override
public int getItemViewType(int position) {
if (position == 0) {
return ITEM_TYPE_HEADER;
}
return ITEM_TYPE_NORMAL;
}
private void setCheckBoxStatus(RecentCallsItemHolder holder) {
if (holder == null || holder.mCheckBoxSelectCall == null) {
return;
}
if (!mIsMultiSelectMode) {
holder.mCheckBoxSelectCall.setVisibility(View.GONE);
holder.itemView.setOnClickListener(null);
} else {
holder.mCheckBoxSelectCall.setVisibility(View.VISIBLE);
}
}
private int getIndexInAdapter(TUICallDefine.CallRecords records) {
int position = -1;
if (mDataSource != null) {
int indexInData = mDataSource.indexOf(records);
if (indexInData != -1) {
position = indexInData + HEADER_COUNT;
}
}
return position;
}
public void setShowMultiSelectCheckBox(boolean show) {
mIsMultiSelectMode = show;
for (TUICallDefine.CallRecords records : mDataSource) {
if (records == null || TextUtils.isEmpty(records.callId)) {
continue;
}
setItemChecked(records.callId, show);
int currentPosition = getIndexInAdapter(records);
if (currentPosition != -1) {
notifyItemChanged(currentPosition);
}
}
}
private void setItemChecked(String callId, boolean isChecked) {
mSelectedPositions.put(callId, isChecked);
}
public boolean isMultiSelectMode() {
return mIsMultiSelectMode;
}
public List<TUICallDefine.CallRecords> getSelectedItem() {
if (mSelectedPositions.size() == 0) {
return null;
}
List<TUICallDefine.CallRecords> selectList = new ArrayList<>();
for (int i = 0; i < getItemCount(); i++) {
TUICallDefine.CallRecords records = getItem(i);
if (records != null && isItemChecked(records.callId)) {
selectList.add(records);
}
}
return selectList;
}
private boolean isItemChecked(String id) {
if (mSelectedPositions.size() <= 0) {
return false;
}
if (mSelectedPositions.containsKey(id)) {
return mSelectedPositions.get(id);
} else {
return false;
}
}
static class HeaderViewHolder extends RecyclerView.ViewHolder {
public HeaderViewHolder(@NonNull View itemView) {
super(itemView);
}
}
}
@@ -0,0 +1,99 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.recents;
import android.app.Application;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.MutableLiveData;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.CallRecords;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallEngine;
import java.util.ArrayList;
import java.util.List;
public class RecentCallsViewModel extends AndroidViewModel {
private static final String TAG = "RecentCallsViewModel";
private final MutableLiveData<List<CallRecords>> mCallHistoryLiveData = new MutableLiveData<>();
private final MutableLiveData<List<CallRecords>> mCallMissedLiveData = new MutableLiveData<>();
public RecentCallsViewModel(@NonNull Application application) {
super(application);
mCallHistoryLiveData.setValue(new ArrayList<>());
mCallMissedLiveData.setValue(new ArrayList<>());
}
public MutableLiveData<List<CallRecords>> getCallHistoryList() {
return mCallHistoryLiveData;
}
public MutableLiveData<List<CallRecords>> getCallMissedList() {
return mCallMissedLiveData;
}
public void queryRecentCalls(TUICallDefine.RecentCallsFilter filter) {
TUICallEngine.createInstance(getApplication()).queryRecentCalls(filter, new TUICommonDefine.ValueCallback() {
@Override
public void onSuccess(Object data) {
if (data == null || !(data instanceof List)) {
return;
}
List<CallRecords> queryList = (List<CallRecords>) data;
if (filter != null && CallRecords.Result.Missed.equals(filter.result)) {
List<CallRecords> missList = mCallMissedLiveData.getValue();
if (missList != null) {
missList.removeAll(queryList);
missList.addAll(queryList);
}
mCallMissedLiveData.setValue(missList);
} else {
List<CallRecords> historyList = mCallHistoryLiveData.getValue();
if (historyList != null) {
historyList.removeAll(queryList);
historyList.addAll(queryList);
}
mCallHistoryLiveData.setValue(historyList);
}
}
@Override
public void onError(int errCode, String errMsg) {
}
});
}
public void deleteRecordCalls(List<CallRecords> list) {
if (list == null || list.isEmpty()) {
return;
}
ArrayList<CallRecords> missList = new ArrayList<>(mCallMissedLiveData.getValue());
missList.removeAll(list);
mCallMissedLiveData.setValue(missList);
List<CallRecords> allList = new ArrayList<>(mCallHistoryLiveData.getValue());
allList.removeAll(list);
mCallHistoryLiveData.setValue(allList);
List<String> callIdList = new ArrayList<>();
for (CallRecords record : list) {
if (record != null && !TextUtils.isEmpty(record.callId)) {
callIdList.add(record.callId);
}
}
TUICallEngine.createInstance(getApplication()).deleteRecordCalls(callIdList,
new TUICommonDefine.ValueCallback() {
@Override
public void onSuccess(Object data) {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
}
}
@@ -0,0 +1,76 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.recents;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.view.common.RoundCornerImageView;
import com.tencent.qcloud.tuikit.tuicallkit.view.common.gridimage.GridImageSynthesizer;
import java.util.List;
public class RecordsIconView extends RoundCornerImageView {
private int mImageSize = 100;
private int mBackground = Color.parseColor("#cfd3d8");
private int mDefaultImageResId = 0;
private int mImageGap = 6;
private GridImageSynthesizer mGridImageSynthesizer;
public RecordsIconView(Context context) {
super(context);
init(context);
}
public RecordsIconView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initAttrs(attrs);
init(context);
}
public RecordsIconView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initAttrs(attrs);
init(context);
}
private void initAttrs(AttributeSet attributeSet) {
TypedArray ta = getContext().obtainStyledAttributes(attributeSet, R.styleable.SynthesizedImageView);
if (null != ta) {
mBackground = ta.getColor(R.styleable.SynthesizedImageView_image_background, mBackground);
mDefaultImageResId = ta.getResourceId(R.styleable.SynthesizedImageView_default_image, mDefaultImageResId);
mImageSize = ta.getDimensionPixelSize(R.styleable.SynthesizedImageView_image_size, mImageSize);
mImageGap = ta.getDimensionPixelSize(R.styleable.SynthesizedImageView_image_gap, mImageGap);
ta.recycle();
}
}
private void init(Context context) {
mGridImageSynthesizer = new GridImageSynthesizer(context, this);
mGridImageSynthesizer.setMaxSize(mImageSize, mImageSize);
mGridImageSynthesizer.setDefaultImage(mDefaultImageResId);
mGridImageSynthesizer.setBgColor(mBackground);
mGridImageSynthesizer.setGap(mImageGap);
}
public RecordsIconView displayImage(List<Object> imageUrls) {
mGridImageSynthesizer.setImageUrls(imageUrls);
return this;
}
public void setImageId(String id) {
mGridImageSynthesizer.setImageId(id);
}
public void load(String imageId) {
mGridImageSynthesizer.load(imageId);
}
public void clear() {
mGridImageSynthesizer.clearImage();
}
}
@@ -0,0 +1,13 @@
package com.tencent.qcloud.tuikit.tuicallkit.extensions.recents.interfaces;
import android.view.View;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
public interface ICallRecordItemListener {
void onItemClick(View view, int viewType, TUICallDefine.CallRecords callRecords);
void onItemDeleteClick(View view, int viewType, TUICallDefine.CallRecords callRecords);
void onDetailViewClick(View view, TUICallDefine.CallRecords callRecords);
}
@@ -0,0 +1,120 @@
package com.tencent.qcloud.tuikit.tuicallkit.internal;
import android.app.Activity;
import android.app.Application;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.tencent.qcloud.tuicore.TUIConstants;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuikit.tuicallkit.TUICallKitImpl;
/**
* `TUICallKit` uses `ContentProvider` to be registered with `TUICore`.
* (`TUICore` is the connection and communication class of each component)
*/
public final class ServiceInitializer extends ContentProvider {
public void init(Context context) {
TUICallingService callingService = TUICallingService.sharedInstance();
callingService.init(context);
TUICore.registerService(TUIConstants.TUICalling.SERVICE_NAME, callingService);
TUICore.registerExtension(TUIConstants.TUIChat.EXTENSION_INPUT_MORE_AUDIO_CALL, callingService);
TUICore.registerExtension(TUIConstants.TUIChat.EXTENSION_INPUT_MORE_VIDEO_CALL, callingService);
TUIAudioMessageRecordService audioRecordService = new TUIAudioMessageRecordService(context);
TUICore.registerService(TUIConstants.TUICalling.SERVICE_NAME_AUDIO_RECORD, audioRecordService);
if (context instanceof Application) {
((Application) context).registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
private int foregroundActivities = 0;
private boolean isChangingConfiguration;
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
@Override
public void onActivityStarted(Activity activity) {
foregroundActivities++;
if (foregroundActivities == 1 && !isChangingConfiguration) {
if (TUILogin.isUserLogined()) {
TUICallKitImpl.createInstance(context).queryOfflineCall();
}
}
isChangingConfiguration = false;
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
foregroundActivities--;
isChangingConfiguration = activity.isChangingConfigurations();
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
@Override
public boolean onCreate() {
Context appContext = getContext().getApplicationContext();
init(appContext);
return false;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
@Nullable String[] selectionArgs, @Nullable String sortOrder) {
return null;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
@Nullable String[] selectionArgs) {
return 0;
}
}
@@ -0,0 +1,337 @@
package com.tencent.qcloud.tuikit.tuicallkit.internal;
import android.content.Context;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import com.tencent.qcloud.tuicore.TUIConstants;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.interfaces.ITUINotification;
import com.tencent.qcloud.tuicore.interfaces.ITUIService;
import com.tencent.qcloud.tuicore.interfaces.TUIServiceCallback;
import com.tencent.qcloud.tuicore.permission.PermissionCallback;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallEngine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallObserver;
import com.tencent.qcloud.tuikit.tuicallengine.impl.base.TUILog;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
import com.tencent.qcloud.tuikit.tuicallkit.utils.PermissionRequest;
import com.tencent.trtc.TRTCCloud;
import com.tencent.trtc.TRTCCloudDef;
import com.tencent.trtc.TRTCCloudListener;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
public class TUIAudioMessageRecordService implements ITUIService, ITUINotification {
private static final String TAG = "TUIAudioMessageRecordService";
private Context mContext;
private AudioRecordInfo mAudioRecordInfo;
private AudioFocusRequest mFocusRequest;
private AudioManager mAudioManager;
private AudioManager.OnAudioFocusChangeListener mOnFocusChangeListener;
private TUIServiceCallback mAudioRecordValueCallback;
public TUIAudioMessageRecordService(Context context) {
mContext = context.getApplicationContext();
TUICore.registerEvent(TUIConstants.TUILogin.EVENT_LOGIN_STATE_CHANGED,
TUIConstants.TUILogin.EVENT_SUB_KEY_USER_LOGIN_SUCCESS, this);
}
@Override
public Object onCall(String method, Map<String, Object> param, TUIServiceCallback callback) {
this.mAudioRecordValueCallback = callback;
if (TextUtils.equals(TUIConstants.TUICalling.METHOD_NAME_START_RECORD_AUDIO_MESSAGE, method)) {
if (param == null) {
TUILog.e(TAG, "startRecordAudioMessage failed, param is empty");
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START,
TUIConstants.TUICalling.ERROR_INVALID_PARAM, null);
return false;
}
//如果当前在通话中,不支持录音
if (!TUICallDefine.Status.None.equals(TUICallingStatusManager.sharedInstance(mContext).getCallStatus())) {
TUILog.e(TAG, "startRecordAudioMessage failed, The current call status does not support recording");
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START,
TUIConstants.TUICalling.ERROR_STATUS_IN_CALL, null);
return false;
}
//当前已经在录音中
if (mAudioRecordInfo != null) {
TUILog.e(TAG, "startRecordAudioMessage failed, The recording is not over, It cannot be called again");
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START,
TUIConstants.TUICalling.ERROR_STATUS_IS_AUDIO_RECORDING, null);
return false;
}
//获取麦克风权限
PermissionRequest.requestPermissions(mContext, TUICallDefine.MediaType.Audio, new PermissionCallback() {
@Override
public void onGranted() {
//获取音频焦点
initAudioFocusManager();
if (requestAudioFocus() != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
TUILog.e(TAG, "startRecordAudioMessage failed, Failed to obtain audio focus");
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START,
TUIConstants.TUICalling.ERROR_REQUEST_AUDIO_FOCUS_FAILED, null);
return;
}
mAudioRecordInfo = new AudioRecordInfo();
if (param.containsKey(TUIConstants.TUICalling.PARAM_NAME_AUDIO_PATH)) {
mAudioRecordInfo.path = (String) param.get(TUIConstants.TUICalling.PARAM_NAME_AUDIO_PATH);
}
if (param.containsKey(TUIConstants.TUICalling.PARAM_NAME_SDK_APP_ID)) {
mAudioRecordInfo.sdkAppId = (int) param.get(TUIConstants.TUICalling.PARAM_NAME_SDK_APP_ID);
}
if (param.containsKey(TUIConstants.TUICalling.PARAM_NAME_AUDIO_SIGNATURE)) {
mAudioRecordInfo.signature =
(String) param.get(TUIConstants.TUICalling.PARAM_NAME_AUDIO_SIGNATURE);
}
//开启音频采集
TRTCCloud.sharedInstance(mContext).setListener(mTRTCCloudListener);
startRecordAudioMessage();
}
@Override
public void onDenied() {
super.onDenied();
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START,
TUIConstants.TUICalling.ERROR_MIC_PERMISSION_REFUSED, null);
}
});
return true;
}
if (TextUtils.equals(TUIConstants.TUICalling.METHOD_NAME_STOP_RECORD_AUDIO_MESSAGE, method)) {
stopRecordAudioMessage();
}
return true;
}
private TRTCCloudListener mTRTCCloudListener = new TRTCCloudListener() {
@Override
public void onError(int errCode, String errMsg, Bundle extraInfo) {
super.onError(errCode, errMsg, extraInfo);
if (errCode == TUIConstants.TUICalling.ERR_MIC_START_FAIL
|| errCode == TUIConstants.TUICalling.ERR_MIC_NOT_AUTHORIZED
|| errCode == TUIConstants.TUICalling.ERR_MIC_SET_PARAM_FAIL
|| errCode == TUIConstants.TUICalling.ERR_MIC_OCCUPY) {
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START, errCode, null);
}
}
@Override
public void onLocalRecordBegin(int errCode, String storagePath) {
super.onLocalRecordBegin(errCode, storagePath);
int tempCode = convertErrorCode("onLocalRecordBegin", errCode);
if (errCode == TUIConstants.TUICalling.ERROR_NONE) {
TRTCCloud.sharedInstance(mContext).startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_SPEECH);
}
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_START, tempCode, storagePath);
}
@Override
public void onLocalRecordComplete(int errCode, String storagePath) {
super.onLocalRecordComplete(errCode, storagePath);
int tempCode = convertErrorCode("onLocalRecordComplete", errCode);
notifyAudioMessageRecordEvent(TUIConstants.TUICalling.EVENT_SUB_KEY_RECORD_STOP, tempCode, storagePath);
}
};
private TUICallObserver mCallObserver = new TUICallObserver() {
@Override
public void onCallReceived(String callerId, List<String> calleeIdList, String groupId,
TUICallDefine.MediaType callMediaType, String userData) {
super.onCallReceived(callerId, calleeIdList, groupId, callMediaType, userData);
//收到通话邀请,停止录制
stopRecordAudioMessage();
}
};
private void startRecordAudioMessage() {
if (mAudioRecordInfo == null) {
TUILog.e(TAG, "startRecordAudioMessage failed, audioRecordInfo is empty");
return;
}
TUILog.i(TAG, "startRecordAudioMessage, mAudioRecordInfo: " + mAudioRecordInfo);
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("api", "startRecordAudioMessage");
JSONObject params = new JSONObject();
params.put(TUIConstants.TUICalling.PARAM_NAME_SDK_APP_ID, mAudioRecordInfo.sdkAppId);
params.put(TUIConstants.TUICalling.PARAM_NAME_AUDIO_PATH, mAudioRecordInfo.path);
params.put("key", mAudioRecordInfo.signature);
jsonObject.put("params", params);
TRTCCloud.sharedInstance(mContext).callExperimentalAPI(jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
private void stopRecordAudioMessage() {
if (mAudioRecordInfo == null) {
TUILog.w(TAG, "stopRecordAudioMessage, current recording status is Idle,do not need to stop");
return;
}
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("api", "stopRecordAudioMessage");
JSONObject params = new JSONObject();
jsonObject.put("params", params);
TRTCCloud.sharedInstance(mContext).callExperimentalAPI(jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
TUILog.i(TAG, "stopRecordAudioMessage, stopLocalAudio");
TRTCCloud.sharedInstance(mContext).stopLocalAudio();
//清空录制信息
mAudioRecordInfo = null;
//释放音频焦点
abandonAudioFocus();
}
private void notifyAudioMessageRecordEvent(String method, int errCode, String path) {
TUILog.i(TAG, "notifyAudioMessageRecordEvent, method: " + method + ", errCode: " + errCode + ",path: " + path);
if (mAudioRecordValueCallback != null) {
Bundle bundleInfo = new Bundle();
bundleInfo.putString(TUIConstants.TUICalling.EVENT_KEY_RECORD_AUDIO_MESSAGE, method);
bundleInfo.putString(TUIConstants.TUICalling.PARAM_NAME_AUDIO_PATH, path);
mAudioRecordValueCallback.onServiceCallback(errCode, "", bundleInfo);
}
}
@Override
public void onNotifyEvent(String key, String subKey, Map<String, Object> param) {
if (TUIConstants.TUILogin.EVENT_LOGIN_STATE_CHANGED.equals(key)
&& TUIConstants.TUILogin.EVENT_SUB_KEY_USER_LOGIN_SUCCESS.equals(subKey)) {
TUICallEngine.createInstance(mContext).addObserver(mCallObserver);
}
}
private void initAudioFocusManager() {
if (mAudioManager == null) {
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
}
if (mOnFocusChangeListener == null) {
mOnFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN:
break;
case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
//其他应用占用焦点的时候,停止录制
stopRecordAudioMessage();
break;
default:
break;
}
}
};
}
AudioAttributes attributes = null;
//android 5.0
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
attributes = new AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA)
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.build();
}
//android 8.0
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
mFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT)
.setWillPauseWhenDucked(true)
.setAudioAttributes(attributes)
.setOnAudioFocusChangeListener(mOnFocusChangeListener)
.build();
}
}
private int requestAudioFocus() {
if (mAudioManager == null) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
int result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result = mAudioManager.requestAudioFocus(mFocusRequest);
} else {
result = mAudioManager.requestAudioFocus(mOnFocusChangeListener, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
return result;
}
private int abandonAudioFocus() {
if (mAudioManager == null) {
return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
}
int result;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
result = mAudioManager.abandonAudioFocusRequest(mFocusRequest);
} else {
result = mAudioManager.abandonAudioFocus(mOnFocusChangeListener);
}
return result;
}
private int convertErrorCode(String method, int errorCode) {
int targetCode;
switch (errorCode) {
case -1:
targetCode = "onLocalRecordBegin".equals(method) ? TUIConstants.TUICalling.ERROR_RECORD_INIT_FAILED
: TUIConstants.TUICalling.ERROR_RECORD_FAILED;
break;
case -2:
targetCode = TUIConstants.TUICalling.ERROR_PATH_FORMAT_NOT_SUPPORT;
break;
case -3:
targetCode = TUIConstants.TUICalling.ERROR_NO_MESSAGE_TO_RECORD;
break;
case -4:
targetCode = TUIConstants.TUICalling.ERROR_SIGNATURE_ERROR;
break;
case -5:
targetCode = TUIConstants.TUICalling.ERROR_SIGNATURE_EXPIRED;
break;
default:
targetCode = TUIConstants.TUICalling.ERROR_NONE;
break;
}
return targetCode;
}
class AudioRecordInfo {
public String path; // 录制文件地址
public int sdkAppId; // 应用的 SDKAppID
public String signature; // AI 降噪签名
public AudioRecordInfo() {
}
@Override
public String toString() {
return "AudioRecordInfo{"
+ "path=" + path
+ ", SDKAppID=" + sdkAppId
+ '}';
}
}
}
@@ -0,0 +1,391 @@
package com.tencent.qcloud.tuikit.tuicallkit.internal;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import androidx.activity.result.ActivityResultCaller;
import com.tencent.qcloud.tuicore.TUIConstants;
import com.tencent.qcloud.tuicore.TUIConstants.TUICalling.ObjectFactory.RecentCalls;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.interfaces.ITUIExtension;
import com.tencent.qcloud.tuicore.interfaces.ITUINotification;
import com.tencent.qcloud.tuicore.interfaces.ITUIObjectFactory;
import com.tencent.qcloud.tuicore.interfaces.ITUIService;
import com.tencent.qcloud.tuicore.interfaces.TUIExtensionEventListener;
import com.tencent.qcloud.tuicore.interfaces.TUIExtensionInfo;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallEngine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.TUICallKit;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.recents.RecentCallsFragment;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* `TUICore` call (if the `TUICore` module is not imported, use `TUICallKitImpl` instead)
*/
final class TUICallingService implements ITUINotification, ITUIService, ITUIExtension, ITUIObjectFactory {
private static final String TAG = "TUICallingService";
private static final int CALL_MEMBER_LIMIT = 9;
private static final TUICallingService INSTANCE = new TUICallingService();
static TUICallingService sharedInstance() {
return INSTANCE;
}
private Context appContext;
private TUICallingService() {
}
public void init(Context context) {
appContext = context;
TUICore.registerEvent(TUIConstants.TUILogin.EVENT_IMSDK_INIT_STATE_CHANGED,
TUIConstants.TUILogin.EVENT_SUB_KEY_START_INIT, this);
TUICore.registerExtension(TUIConstants.TUIChat.Extension.InputMore.CLASSIC_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIChat.Extension.InputMore.MINIMALIST_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIGroup.Extension.GroupProfileItem.MINIMALIST_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIGroup.Extension.GroupProfileItem.CLASSIC_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIContact.Extension.FriendProfileItem.CLASSIC_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIContact.Extension.FriendProfileItem.MINIMALIST_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.CLASSIC_EXTENSION_ID, this);
TUICore.registerExtension(TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.MINIMALIST_EXTENSION_ID, this);
TUICore.registerObjectFactory(TUIConstants.TUICalling.ObjectFactory.FACTORY_NAME, this);
}
@Override
public Object onCall(String method, Map<String, Object> param) {
Log.i(TAG, "onCall, method: " + method + " ,param: " + param);
if (null != param && TextUtils.equals(TUIConstants.TUICalling.METHOD_NAME_ENABLE_FLOAT_WINDOW, method)) {
boolean enableFloatWindow = (boolean) param.get(TUIConstants.TUICalling.PARAM_NAME_ENABLE_FLOAT_WINDOW);
Log.i(TAG, "onCall, enableFloatWindow: " + enableFloatWindow);
TUICallKit.createInstance(appContext).enableFloatWindow(enableFloatWindow);
return null;
}
if (null != param && TextUtils.equals(TUIConstants.TUICalling.METHOD_NAME_ENABLE_MULTI_DEVICE, method)) {
boolean enable = (boolean) param.get(TUIConstants.TUICalling.PARAM_NAME_ENABLE_MULTI_DEVICE);
Log.i(TAG, "onCall, enableMultiDevice: " + enable);
TUICallEngine.createInstance(appContext).enableMultiDeviceAbility(enable, new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
return null;
}
if (null != param && TextUtils.equals(TUIConstants.TUICalling.METHOD_NAME_CALL, method)) {
String[] userIDs = (String[]) param.get(TUIConstants.TUICalling.PARAM_NAME_USERIDS);
String typeString = (String) param.get(TUIConstants.TUICalling.PARAM_NAME_TYPE);
String groupID = (String) param.get(TUIConstants.TUICalling.PARAM_NAME_GROUPID);
List<String> userIdList = new ArrayList<>(Arrays.asList(userIDs));
TUICallDefine.MediaType mediaType = TUICallDefine.MediaType.Unknown;
if (TUIConstants.TUICalling.TYPE_AUDIO.equals(typeString)) {
mediaType = TUICallDefine.MediaType.Audio;
} else if (TUIConstants.TUICalling.TYPE_VIDEO.equals(typeString)) {
mediaType = TUICallDefine.MediaType.Video;
}
if (!TextUtils.isEmpty(groupID)) {
TUICallKit.createInstance(appContext).groupCall(groupID, userIdList, mediaType);
} else if (userIdList.size() == 1) {
TUICallKit.createInstance(appContext).call(userIdList.get(0), mediaType);
} else {
Log.e(TAG, "onCall ignored, groupId is empty and userList is not 1, cannot start call or groupCall");
}
}
return null;
}
@Override
public Object onCreateObject(String objectName, Map<String, Object> param) {
if (TextUtils.equals(objectName, RecentCalls.OBJECT_NAME)) {
String style = RecentCalls.UI_STYLE_MINIMALIST;
if (param != null && RecentCalls.UI_STYLE_CLASSIC.equals(param.get(RecentCalls.UI_STYLE))) {
style = RecentCalls.UI_STYLE_CLASSIC;
}
return new RecentCallsFragment(style);
}
return null;
}
@Override
public List<TUIExtensionInfo> onGetExtension(String extensionID, Map<String, Object> param) {
Log.i(TAG, "onGetExtension, extensionID: " + extensionID + " ,param: " + param);
if (TextUtils.equals(extensionID,
TUIConstants.TUIChat.Extension.InputMore.CLASSIC_EXTENSION_ID)) {
return getClassicChatInputMoreExtension(param);
} else if (TextUtils.equals(extensionID,
TUIConstants.TUIGroup.Extension.GroupProfileItem.MINIMALIST_EXTENSION_ID)) {
return getMinimalistGroupProfileExtension(param);
} else if (TextUtils.equals(extensionID,
TUIConstants.TUIContact.Extension.FriendProfileItem.CLASSIC_EXTENSION_ID)) {
return getClassicFriendProfileExtension(param);
} else if (TextUtils.equals(extensionID,
TUIConstants.TUIContact.Extension.FriendProfileItem.MINIMALIST_EXTENSION_ID)) {
return getMinimalistFriendProfileExtension(param);
} else if (TextUtils.equals(extensionID,
TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.MINIMALIST_EXTENSION_ID)) {
return getMinimalistChatNavigationMoreExtension(param);
}
return null;
}
private List<TUIExtensionInfo> getClassicChatInputMoreExtension(Map<String, Object> param) {
TUIExtensionInfo voiceCallExtension = new TUIExtensionInfo();
voiceCallExtension.setWeight(600);
TUIExtensionInfo videoCallExtension = new TUIExtensionInfo();
videoCallExtension.setWeight(500);
String userID = getOrDefault(param, TUIConstants.TUIChat.Extension.InputMore.USER_ID, null);
String groupID = getOrDefault(param, TUIConstants.TUIChat.Extension.InputMore.GROUP_ID, null);
ResultTUIExtensionEventListener voiceListener = new ResultTUIExtensionEventListener();
voiceListener.mediaType = TUICallDefine.MediaType.Audio;
voiceListener.userID = userID;
voiceListener.groupID = groupID;
ResultTUIExtensionEventListener videoListener = new ResultTUIExtensionEventListener();
videoListener.mediaType = TUICallDefine.MediaType.Video;
videoListener.userID = userID;
videoListener.groupID = groupID;
voiceCallExtension.setText(appContext.getString(R.string.tuicalling_audio_call));
voiceCallExtension.setIcon(R.drawable.tuicalling_ic_audio_call);
voiceCallExtension.setExtensionListener(voiceListener);
voiceListener.activityResultCaller = getOrDefault(param,
TUIConstants.TUIChat.Extension.InputMore.CONTEXT, null);
videoCallExtension.setText(appContext.getString(R.string.tuicalling_video_call));
videoCallExtension.setIcon(R.drawable.tuicalling_ic_video_call);
videoCallExtension.setExtensionListener(videoListener);
videoListener.activityResultCaller = getOrDefault(param,
TUIConstants.TUIChat.Extension.InputMore.CONTEXT, null);
boolean filterVoice = getOrDefault(param, TUIConstants.TUIChat.Extension.InputMore.FILTER_VOICE_CALL, false);
boolean filterVideo = getOrDefault(param, TUIConstants.TUIChat.Extension.InputMore.FILTER_VIDEO_CALL, false);
List<TUIExtensionInfo> extensionInfoList = new ArrayList<>();
if (!filterVoice) {
extensionInfoList.add(voiceCallExtension);
}
if (!filterVideo) {
extensionInfoList.add(videoCallExtension);
}
return extensionInfoList;
}
private List<TUIExtensionInfo> getMinimalistGroupProfileExtension(Map<String, Object> param) {
TUIExtensionInfo voiceCallExtension = new TUIExtensionInfo();
voiceCallExtension.setWeight(200);
TUIExtensionInfo videoCallExtension = new TUIExtensionInfo();
videoCallExtension.setWeight(100);
String groupID = getOrDefault(param, TUIConstants.TUIGroup.Extension.GroupProfileItem.GROUP_ID, null);
ResultTUIExtensionEventListener voiceListener = new ResultTUIExtensionEventListener();
voiceListener.mediaType = TUICallDefine.MediaType.Audio;
voiceListener.groupID = groupID;
voiceListener.isClassicUI = false;
ResultTUIExtensionEventListener videoListener = new ResultTUIExtensionEventListener();
videoListener.mediaType = TUICallDefine.MediaType.Video;
videoListener.groupID = groupID;
videoListener.isClassicUI = false;
voiceCallExtension.setText(appContext.getString(R.string.tuicalling_audio_call));
voiceCallExtension.setIcon(R.drawable.tuicallkit_profile_minimalist_audio_icon);
voiceCallExtension.setExtensionListener(voiceListener);
voiceListener.activityResultCaller = getOrDefault(param,
TUIConstants.TUIGroup.Extension.GroupProfileItem.CONTEXT, null);
voiceListener.isClassicUI = false;
videoCallExtension.setText(appContext.getString(R.string.tuicalling_video_call));
videoCallExtension.setIcon(R.drawable.tuicallkit_profile_minimalist_video_icon);
videoCallExtension.setExtensionListener(videoListener);
videoListener.isClassicUI = false;
videoListener.activityResultCaller = getOrDefault(param,
TUIConstants.TUIGroup.Extension.GroupProfileItem.CONTEXT, null);
List<TUIExtensionInfo> extensionInfoList = new ArrayList<>();
extensionInfoList.add(videoCallExtension);
extensionInfoList.add(voiceCallExtension);
return extensionInfoList;
}
private List<TUIExtensionInfo> getClassicFriendProfileExtension(Map<String, Object> param) {
TUIExtensionInfo voiceCallExtension = new TUIExtensionInfo();
voiceCallExtension.setWeight(300);
TUIExtensionInfo videoCallExtension = new TUIExtensionInfo();
videoCallExtension.setWeight(200);
String userID = getOrDefault(param, TUIConstants.TUIContact.Extension.FriendProfileItem.USER_ID, null);
ResultTUIExtensionEventListener voiceListener = new ResultTUIExtensionEventListener();
voiceListener.mediaType = TUICallDefine.MediaType.Audio;
voiceListener.userID = userID;
ResultTUIExtensionEventListener videoListener = new ResultTUIExtensionEventListener();
videoListener.mediaType = TUICallDefine.MediaType.Video;
videoListener.userID = userID;
voiceCallExtension.setText(appContext.getString(R.string.tuicalling_audio_call));
voiceCallExtension.setExtensionListener(voiceListener);
videoCallExtension.setText(appContext.getString(R.string.tuicalling_video_call));
videoCallExtension.setExtensionListener(videoListener);
List<TUIExtensionInfo> extensionInfoList = new ArrayList<>();
extensionInfoList.add(videoCallExtension);
extensionInfoList.add(voiceCallExtension);
return extensionInfoList;
}
private List<TUIExtensionInfo> getMinimalistFriendProfileExtension(Map<String, Object> param) {
TUIExtensionInfo voiceCallExtension = new TUIExtensionInfo();
voiceCallExtension.setWeight(300);
TUIExtensionInfo videoCallExtension = new TUIExtensionInfo();
videoCallExtension.setWeight(200);
String userID = getOrDefault(param, TUIConstants.TUIContact.Extension.FriendProfileItem.USER_ID, null);
ResultTUIExtensionEventListener voiceListener = new ResultTUIExtensionEventListener();
voiceListener.mediaType = TUICallDefine.MediaType.Audio;
voiceListener.userID = userID;
voiceListener.isClassicUI = false;
ResultTUIExtensionEventListener videoListener = new ResultTUIExtensionEventListener();
videoListener.mediaType = TUICallDefine.MediaType.Video;
videoListener.userID = userID;
videoListener.isClassicUI = false;
voiceCallExtension.setIcon(R.drawable.tuicallkit_profile_minimalist_audio_icon);
voiceCallExtension.setText(appContext.getString(R.string.tuicalling_audio_call));
voiceCallExtension.setExtensionListener(voiceListener);
videoCallExtension.setIcon(R.drawable.tuicallkit_profile_minimalist_video_icon);
videoCallExtension.setText(appContext.getString(R.string.tuicalling_video_call));
videoCallExtension.setExtensionListener(videoListener);
List<TUIExtensionInfo> extensionInfoList = new ArrayList<>();
extensionInfoList.add(videoCallExtension);
extensionInfoList.add(voiceCallExtension);
return extensionInfoList;
}
private List<TUIExtensionInfo> getMinimalistChatNavigationMoreExtension(Map<String, Object> param) {
String userID = getOrDefault(param, TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.USER_ID, null);
String groupID = getOrDefault(param, TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.GROUP_ID, null);
ResultTUIExtensionEventListener voiceListener = new ResultTUIExtensionEventListener();
voiceListener.mediaType = TUICallDefine.MediaType.Audio;
voiceListener.groupID = groupID;
voiceListener.userID = userID;
voiceListener.isClassicUI = false;
voiceListener.activityResultCaller = getOrDefault(param,
TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.CONTEXT, null);
ResultTUIExtensionEventListener videoListener = new ResultTUIExtensionEventListener();
videoListener.mediaType = TUICallDefine.MediaType.Video;
videoListener.groupID = groupID;
videoListener.userID = userID;
videoListener.isClassicUI = false;
videoListener.activityResultCaller = getOrDefault(param,
TUIConstants.TUIChat.Extension.ChatNavigationMoreItem.CONTEXT, null);
TUIExtensionInfo voiceCallExtension = new TUIExtensionInfo();
TUIExtensionInfo videoCallExtension = new TUIExtensionInfo();
voiceCallExtension.setIcon(R.drawable.tuicallkit_chat_title_bar_minimalist_audio_call_icon);
voiceCallExtension.setExtensionListener(voiceListener);
videoCallExtension.setIcon(R.drawable.tuicallkit_chat_title_bar_minimalist_video_call_icon);
videoCallExtension.setExtensionListener(videoListener);
List<TUIExtensionInfo> extensionInfoList = new ArrayList<>();
extensionInfoList.add(voiceCallExtension);
extensionInfoList.add(videoCallExtension);
return extensionInfoList;
}
private <T> T getOrDefault(Map map, Object key, T defaultValue) {
if (map == null || map.isEmpty()) {
return defaultValue;
}
Object object = map.get(key);
try {
if (object != null) {
return (T) object;
}
} catch (ClassCastException e) {
return defaultValue;
}
return defaultValue;
}
@Override
public void onNotifyEvent(String key, String subKey, Map<String, Object> param) {
if (TUIConstants.TUILogin.EVENT_IMSDK_INIT_STATE_CHANGED.equals(key)
&& TUIConstants.TUILogin.EVENT_SUB_KEY_START_INIT.equals(subKey)) {
TUICallKit.createInstance(appContext);
adaptiveComponentReport();
}
}
private void adaptiveComponentReport() {
ITUIService service = TUICore.getService(TUIConstants.TUIChat.SERVICE_NAME);
try {
JSONObject params = new JSONObject();
params.put("framework", 1);
if (service == null) {
params.put("component", 14);
} else {
params.put("component", 15);
}
params.put("language", 1);
JSONObject jsonObject = new JSONObject();
jsonObject.put("api", "setFramework");
jsonObject.put("params", params);
TUICallEngine.createInstance(appContext).callExperimentalAPI(jsonObject.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
class ResultTUIExtensionEventListener extends TUIExtensionEventListener {
public ActivityResultCaller activityResultCaller;
public TUICallDefine.MediaType mediaType;
public boolean isClassicUI = true;
public String userID;
public String groupID;
@Override
public void onClicked(Map<String, Object> param) {
if (!TextUtils.isEmpty(groupID)) {
String groupMemberSelectActivityName =
TUIConstants.TUIContact.StartActivity.GroupMemberSelect.CLASSIC_ACTIVITY_NAME;
if (!isClassicUI) {
groupMemberSelectActivityName =
TUIConstants.TUIContact.StartActivity.GroupMemberSelect.MINIMALIST_ACTIVITY_NAME;
}
Bundle bundle = new Bundle();
bundle.putString(TUIConstants.TUIContact.StartActivity.GroupMemberSelect.GROUP_ID, groupID);
bundle.putBoolean(TUIConstants.TUIContact.StartActivity.GroupMemberSelect.SELECT_FOR_CALL, true);
bundle.putInt(TUIConstants.TUIContact.StartActivity.GroupMemberSelect.MEMBER_LIMIT, CALL_MEMBER_LIMIT);
TUICore.startActivityForResult(activityResultCaller, groupMemberSelectActivityName, bundle, result -> {
Intent data = result.getData();
if (data != null) {
List<String> stringList = data.getStringArrayListExtra(
TUIConstants.TUIContact.StartActivity.GroupMemberSelect.DATA_LIST);
TUICallKit.createInstance(appContext).groupCall(groupID, stringList, mediaType);
}
});
} else if (!TextUtils.isEmpty(userID)) {
TUICallKit.createInstance(appContext).call(userID, mediaType);
} else {
Log.e(TAG, "onClicked event ignored, groupId is empty or userId is empty, cannot start call");
}
}
}
}
@@ -0,0 +1,80 @@
package com.tencent.qcloud.tuikit.tuicallkit.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DeviceUtils;
/**
* TUICallKit are kept alive to ensure that applications are not killed by the system in the background.
* You can delete them if they are not needed.
*/
public class TUICallService extends Service {
private static final int NOTIFICATION_ID = 1001;
@Override
public void onCreate() {
super.onCreate();
// 获取服务通知
Notification notification = createForegroundNotification();
//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的ID
startForeground(NOTIFICATION_ID, notification);
}
public static void start(Context context) {
if (DeviceUtils.isServiceRunning(context, TUICallService.class.getName())) {
return;
}
Intent starter = new Intent(context, TUICallService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(starter);
} else {
context.startService(starter);
}
}
public static void stop(Context context) {
Intent intent = new Intent(context, TUICallService.class);
context.stopService(intent);
}
private Notification createForegroundNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
String notificationChannelId = "notification_channel_id_01";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String channelName = "TRTC Foreground Service Notification";
NotificationChannel notificationChannel = new NotificationChannel(
notificationChannelId, channelName, NotificationManager.IMPORTANCE_LOW);
notificationChannel.setDescription("Channel description");
if (notificationManager != null) {
notificationManager.createNotificationChannel(notificationChannel);
}
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, notificationChannelId);
return builder.build();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
stopSelf();
}
}
@@ -0,0 +1,79 @@
package com.tencent.qcloud.tuikit.tuicallkit.utils;
import android.app.ActivityManager;
import android.content.Context;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Window;
import android.view.WindowManager;
import java.util.List;
public class DeviceUtils {
public static void setScreenLockParams(Window window) {
if (null == window) {
return;
}
PowerManager powerManager = (PowerManager) window.getContext().getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = powerManager.isScreenOn();
if (isScreenOn) {
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
}
}
public static int getScreenWidth(Context context) {
DisplayMetrics metric = new DisplayMetrics();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metric);
return metric.widthPixels;
}
public static int getScreenHeight(Context context) {
DisplayMetrics metric = new DisplayMetrics();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metric);
return metric.heightPixels;
}
public static boolean isAppRunningForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos = activityManager.getRunningAppProcesses();
if (runningAppProcessInfos == null) {
return false;
}
String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcessInfo : runningAppProcessInfos) {
if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
&& appProcessInfo.processName.equals(packageName)) {
return true;
}
}
return false;
}
public static boolean isServiceRunning(Context context, String className) {
if (context == null || TextUtils.isEmpty(className)) {
return false;
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> info = am.getRunningServices(0x7FFFFFFF);
if (info == null || info.size() == 0) {
return false;
}
for (ActivityManager.RunningServiceInfo aInfo : info) {
if (className.equals(aInfo.service.getClassName())) {
return true;
}
}
return false;
}
}
@@ -0,0 +1,214 @@
package com.tencent.qcloud.tuikit.tuicallkit.utils;
import android.content.Context;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import java.util.ArrayList;
public class DisplayUtils {
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
// init stack layout
public static ArrayList<RelativeLayout.LayoutParams> initFloatParamList(Context context, int width, int height) {
ArrayList<RelativeLayout.LayoutParams> list = new ArrayList<RelativeLayout.LayoutParams>();
// the largest layout in the bottom
RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
list.add(layoutParams0);
final int midMargin = dip2px(context, 10);
final int lrMargin = dip2px(context, 15);
final int bottomMargin = dip2px(context, 50);
final int subWidth = dip2px(context, 120);
final int subHeight = dip2px(context, 180);
for (int i = 2; i >= 0; i--) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(subWidth, subHeight);
layoutParams.leftMargin = width - lrMargin - subWidth;
layoutParams.topMargin = height - (bottomMargin + midMargin * (i + 1) + subHeight * i) - subHeight;
list.add(layoutParams);
}
for (int i = 2; i >= 0; i--) {
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(subWidth, subHeight);
layoutParams.leftMargin = lrMargin;
layoutParams.topMargin = height - (bottomMargin + midMargin * (i + 1) + subHeight * i) - subHeight;
list.add(layoutParams);
}
return list;
}
// init one grid layout ,center in the screen
public static ArrayList<RelativeLayout.LayoutParams> initGrid1Param(Context context, int width, int height) {
int margin = dip2px(context, 10);
int grid4W = (width - margin * 2) / 2;
int grid4H = (height - margin * 2) / 2;
RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams0.addRule(RelativeLayout.CENTER_IN_PARENT);
ArrayList<RelativeLayout.LayoutParams> list = new ArrayList<>();
list.add(layoutParams0);
return list;
}
// init two grid layout ,split the screen
public static ArrayList<RelativeLayout.LayoutParams> initGrid2Param(Context context, int width, int height) {
int margin = dip2px(context, 10);
int grid4W = (width - margin * 2) / 2;
int grid4H = (height - margin * 2) / 2;
RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams0.addRule(RelativeLayout.CENTER_VERTICAL);
layoutParams0.leftMargin = margin;
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams1.addRule(RelativeLayout.CENTER_VERTICAL);
layoutParams1.rightMargin = margin;
ArrayList<RelativeLayout.LayoutParams> list = new ArrayList<>();
list.add(layoutParams0);
list.add(layoutParams1);
return list;
}
// init three grid layout
public static ArrayList<RelativeLayout.LayoutParams> initGrid3Param(Context context, int width, int height) {
int margin = dip2px(context, 10);
int grid3W = (width - margin * 2) / 2;
int grid3H = (height - margin * 2) / 2;
RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid3W, grid3H);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams0.topMargin = margin;
layoutParams0.leftMargin = margin;
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid3W, grid3H);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams1.topMargin = margin;
layoutParams1.rightMargin = margin;
RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid3W, grid3H);
layoutParams2.addRule(RelativeLayout.CENTER_HORIZONTAL);
layoutParams2.bottomMargin = margin;
layoutParams2.topMargin = margin + grid3H;
ArrayList<RelativeLayout.LayoutParams> list = new ArrayList<>();
list.add(layoutParams0);
list.add(layoutParams1);
list.add(layoutParams2);
return list;
}
// init four grid layout
public static ArrayList<RelativeLayout.LayoutParams> initGrid4Param(Context context, int width, int height) {
int margin = dip2px(context, 10);
int grid4W = (width - margin * 2) / 2;
int grid4H = (height - margin * 2) / 2;
RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams0.topMargin = margin;
layoutParams0.leftMargin = margin;
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams1.topMargin = margin;
layoutParams1.rightMargin = margin;
RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams2.bottomMargin = margin;
layoutParams2.topMargin = margin + grid4H;
layoutParams2.leftMargin = margin;
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(grid4W, grid4H);
layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams3.bottomMargin = margin;
layoutParams3.topMargin = margin + grid4H;
layoutParams3.rightMargin = margin;
ArrayList<RelativeLayout.LayoutParams> list = new ArrayList<>();
list.add(layoutParams0);
list.add(layoutParams1);
list.add(layoutParams2);
list.add(layoutParams3);
return list;
}
// init nine grid layout
public static ArrayList<RelativeLayout.LayoutParams> initGrid9Param(Context context, int width, int height) {
int margin = dip2px(context, 10);
int grid9W = (width - margin * 2) / 3;
int grid9H = (height - margin * 2) / 3;
RelativeLayout.LayoutParams layoutParams0 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams0.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams0.topMargin = margin;
layoutParams0.leftMargin = margin;
RelativeLayout.LayoutParams layoutParams1 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams1.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams1.topMargin = margin;
layoutParams1.leftMargin = margin + grid9W;
RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_TOP);
layoutParams2.topMargin = margin;
layoutParams2.rightMargin = margin;
RelativeLayout.LayoutParams layoutParams3 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams3.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams3.leftMargin = margin;
layoutParams3.topMargin = margin + grid9H;
RelativeLayout.LayoutParams layoutParams4 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams4.topMargin = margin + grid9H;
layoutParams4.leftMargin = margin + grid9W;
RelativeLayout.LayoutParams layoutParams5 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams5.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams5.topMargin = margin + grid9H;
layoutParams5.rightMargin = margin;
RelativeLayout.LayoutParams layoutParams6 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams6.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
layoutParams6.bottomMargin = margin;
layoutParams6.leftMargin = margin;
layoutParams6.topMargin = margin + grid9H * 2;
RelativeLayout.LayoutParams layoutParams7 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams7.bottomMargin = margin;
layoutParams7.topMargin = margin + grid9H * 2;
layoutParams7.leftMargin = margin + grid9W;
RelativeLayout.LayoutParams layoutParams8 = new RelativeLayout.LayoutParams(grid9W, grid9H);
layoutParams8.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
layoutParams8.bottomMargin = margin;
layoutParams8.topMargin = margin + grid9H * 2;
layoutParams8.rightMargin = margin;
ArrayList<RelativeLayout.LayoutParams> list = new ArrayList<>();
list.add(layoutParams0);
list.add(layoutParams1);
list.add(layoutParams2);
list.add(layoutParams3);
list.add(layoutParams4);
list.add(layoutParams5);
list.add(layoutParams6);
list.add(layoutParams7);
list.add(layoutParams8);
return list;
}
}
@@ -0,0 +1,107 @@
package com.tencent.qcloud.tuikit.tuicallkit.utils;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import androidx.annotation.RawRes;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
import com.bumptech.glide.request.RequestOptions;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import java.security.MessageDigest;
import java.util.concurrent.ExecutionException;
public class ImageLoader {
private static int radius = 0;
public static void clear(Context context, ImageView imageView) {
Glide.with(context).clear(imageView);
}
public static void loadImage(Context context, ImageView imageView, Object url) {
loadImage(context, imageView, url, R.drawable.tuicalling_ic_avatar);
}
public static void loadImage(Context context, ImageView imageView, Object url, @DrawableRes int resId) {
if (url == null || url == "") {
if (imageView != null && resId != 0) {
imageView.setImageResource(resId);
}
return;
}
Glide.with(context).load(url).placeholder(resId).error(loadTransform(context, resId, radius)).into(imageView);
}
public static void loadImage(Context context, ImageView imageView, @RawRes @DrawableRes Integer resourceId) {
Glide.with(context).load(resourceId).into(imageView);
}
public static Bitmap loadBitmap(Context context, Object imageUrl, int targetImageSize) throws InterruptedException,
ExecutionException {
if (imageUrl == null) {
return null;
}
return Glide.with(context).asBitmap().load(imageUrl)
.apply(loadTransform(context, R.drawable.tuicalling_ic_avatar, radius))
.into(targetImageSize, targetImageSize)
.get();
}
public static void loadGifImage(Context context, ImageView imageView, @RawRes @DrawableRes int resourceId) {
Glide.with(context).asGif().load(resourceId).into(imageView);
}
private static RequestBuilder<Drawable> loadTransform(Context context, @DrawableRes int resId, int radius) {
return Glide.with(context).load(resId).placeholder(resId)
.apply(new RequestOptions().centerCrop().transform(new GlideRoundTransform(context, radius)));
}
public static class GlideRoundTransform extends BitmapTransformation {
private static float radius = 0f;
public GlideRoundTransform(Context context, int dp) {
radius = Resources.getSystem().getDisplayMetrics().density * dp;
}
@Override
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
return roundCrop(pool, toTransform);
}
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
if (source == null) {
return null;
}
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
if (result == null) {
result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(result);
Paint paint = new Paint();
paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
paint.setAntiAlias(true);
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
canvas.drawRoundRect(rectF, radius, radius, paint);
return result;
}
@Override
public void updateDiskCacheKey(MessageDigest messageDigest) {
}
}
}
@@ -0,0 +1,119 @@
package com.tencent.qcloud.tuikit.tuicallkit.utils;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import com.tencent.qcloud.tuicore.TUIConstants;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.permission.PermissionCallback;
import com.tencent.qcloud.tuicore.permission.PermissionRequester;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.utils.BrandUtils;
import com.tencent.qcloud.tuikit.tuicallengine.utils.PermissionUtils;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import java.util.ArrayList;
import java.util.List;
public class PermissionRequest {
public static void requestPermissions(Context context, TUICallDefine.MediaType type, PermissionCallback callback) {
StringBuilder title = new StringBuilder().append(context.getString(R.string.tuicalling_permission_microphone));
StringBuilder reason = new StringBuilder();
String microphonePermissionsDescription = (String) TUICore.createObject(
TUIConstants.Privacy.PermissionsFactory.FACTORY_NAME,
TUIConstants.Privacy.PermissionsFactory.PermissionsName.MICROPHONE_PERMISSIONS, null);
if (!TextUtils.isEmpty(microphonePermissionsDescription)) {
reason.append(microphonePermissionsDescription);
} else {
reason.append(context.getString(R.string.tuicalling_permission_mic_reason));
}
List<String> permissionList = new ArrayList<>();
permissionList.add(Manifest.permission.RECORD_AUDIO);
if (TUICallDefine.MediaType.Video.equals(type)) {
title.append(context.getString(R.string.tuicalling_permission_separator));
title.append(context.getString(R.string.tuicalling_permission_camera));
String cameraPermissionsDescription = (String) TUICore.createObject(
TUIConstants.Privacy.PermissionsFactory.FACTORY_NAME,
TUIConstants.Privacy.PermissionsFactory.PermissionsName.CAMERA_PERMISSIONS, null);
if (!TextUtils.isEmpty(cameraPermissionsDescription)) {
reason.append(cameraPermissionsDescription);
} else {
reason.append(context.getString(R.string.tuicalling_permission_camera_reason));
}
permissionList.add(Manifest.permission.CAMERA);
}
//Android S(31) need apply for this permission,refer to:https://developer.android.com/about/versions/12/features/bluetooth-permissions?hl=zh-cn
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
title.append(context.getString(R.string.tuicalling_permission_separator));
title.append(context.getString(R.string.tuicalling_permission_bluetooth));
reason.append(context.getString(R.string.tuicalling_permission_bluetooth_reason));
permissionList.add(Manifest.permission.BLUETOOTH_CONNECT);
}
ApplicationInfo applicationInfo = context.getApplicationInfo();
String appName = context.getPackageManager().getApplicationLabel(applicationInfo).toString();
String[] permissions = permissionList.toArray(new String[0]);
PermissionRequester.newInstance(permissions)
.title(context.getString(R.string.tuicalling_permission_title, appName, title))
.description(reason.toString())
.settingsTip(context.getString(R.string.tuicalling_permission_tips, title) + "\n" + reason.toString())
.callback(callback)
.request();
}
public static void requestFloatPermission(Context context) {
if (PermissionUtils.hasPermission(context)) {
return;
}
startCommonSettings(context);
}
private static void startCommonSettings(Context context) {
try {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
intent.setData(Uri.parse("package:" + context.getPackageName()));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void requestVivoFloatPermission(Context context) {
Intent intent = new Intent();
String model = BrandUtils.getModel();
boolean isVivoY85 = false;
if (!TextUtils.isEmpty(model)) {
isVivoY85 = model.contains("Y85") && !model.contains("Y85A");
}
if (!TextUtils.isEmpty(model) && (isVivoY85 || model.contains("vivo Y53L"))) {
intent.setClassName("com.vivo.permissionmanager",
"com.vivo.permissionmanager.activity.PurviewTabActivity");
intent.putExtra("tabId", "1");
} else {
intent.setClassName("com.vivo.permissionmanager",
"com.vivo.permissionmanager.activity.SoftPermissionDetailActivity");
intent.setAction("secure.intent.action.softPermissionDetail");
}
intent.putExtra("packagename", context.getPackageName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
context.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
}
}
@@ -0,0 +1,112 @@
package com.tencent.qcloud.tuikit.tuicallkit.utils;
import android.text.TextUtils;
import com.tencent.imsdk.v2.V2TIMManager;
import com.tencent.imsdk.v2.V2TIMUserFullInfo;
import com.tencent.imsdk.v2.V2TIMValueCallback;
import com.tencent.qcloud.tuikit.tuicallengine.impl.base.TUILog;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import java.util.ArrayList;
import java.util.List;
public class UserInfoUtils {
private static final String TAG = "UserInfoUtils";
private static final String ERROR_MSG_EMPTY_LIST = "userList is empty";
public UserInfoUtils() {
}
public void getUserInfo(String userId, final UserCallback callback) {
if (TextUtils.isEmpty(userId)) {
TUILog.e(TAG, "getUserInfo, userId is empty.");
if (callback != null) {
callback.onFailed(-1, ERROR_MSG_EMPTY_LIST);
}
return;
}
List<String> list = new ArrayList<>();
list.add(userId);
internalGetUserInfo(list, callback);
}
public void getUserInfo(List<CallingUserModel> userIdList, final UserCallback callback) {
if (null == userIdList || userIdList.isEmpty()) {
TUILog.e(TAG, "getUserInfo, userIdList is empty.");
if (callback != null) {
callback.onFailed(-1, ERROR_MSG_EMPTY_LIST);
}
return;
}
List<String> list = new ArrayList<>();
for (CallingUserModel model : userIdList) {
if (null != model && !TextUtils.isEmpty(model.userId)) {
list.add(model.userId);
}
}
internalGetUserInfo(list, callback);
}
private void internalGetUserInfo(List<String> userList, UserCallback callback) {
if (null == userList || userList.isEmpty()) {
TUILog.e(TAG, "internalGetUserInfo, userIdList is empty.");
if (callback != null) {
callback.onFailed(-1, ERROR_MSG_EMPTY_LIST);
}
return;
}
V2TIMManager.getInstance().getUsersInfo(userList, new V2TIMValueCallback<List<V2TIMUserFullInfo>>() {
@Override
public void onError(int errorCode, String errorMsg) {
TUILog.e(TAG, "internalGetUserInfo fail, errorCode: " + errorCode + ", errorMsg: " + errorMsg);
if (callback != null) {
callback.onFailed(errorCode, errorMsg);
}
}
@Override
public void onSuccess(List<V2TIMUserFullInfo> userFullInfoList) {
if (null == userFullInfoList || userFullInfoList.isEmpty()) {
TUILog.e(TAG, "internalGetUserInfo result is empty");
if (null != callback) {
callback.onFailed(-1, ERROR_MSG_EMPTY_LIST);
}
return;
}
List<CallingUserModel> list = new ArrayList<>();
for (int i = 0; i < userFullInfoList.size(); i++) {
CallingUserModel model = new CallingUserModel();
model.userName = userFullInfoList.get(i).getNickName();
model.userId = userFullInfoList.get(i).getUserID();
model.userAvatar = userFullInfoList.get(i).getFaceUrl();
if (!isUrl(model.userAvatar)) {
model.userAvatar = "";
}
model.userName = TextUtils.isEmpty(model.userName) ? model.userId : model.userName;
TUILog.i(TAG, "internalGetUserInfo, model: " + model);
list.add(model);
}
if (callback != null) {
callback.onSuccess(list);
}
}
});
}
private boolean isUrl(String url) {
return !TextUtils.isEmpty(url) && (url.startsWith("http://") || url.startsWith("https://"));
}
public interface UserCallback {
void onSuccess(List<CallingUserModel> list);
void onFailed(int errorCode, String errorMsg);
}
}
@@ -0,0 +1,898 @@
package com.tencent.qcloud.tuikit.tuicallkit.view;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuicore.interfaces.ITUINotification;
import com.tencent.qcloud.tuicore.util.ToastUtil;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.TUIVideoView;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.MediaType;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.Role;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.Scene;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine.Status;
import com.tencent.qcloud.tuikit.tuicallengine.impl.base.TUILog;
import com.tencent.qcloud.tuikit.tuicallengine.utils.PermissionUtils;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.BaseCallActivity;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.Constants;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingAction;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayoutEntity;
import com.tencent.qcloud.tuikit.tuicallkit.extensions.inviteuser.SelectGroupMemberActivity;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
import com.tencent.qcloud.tuikit.tuicallkit.utils.PermissionRequest;
import com.tencent.qcloud.tuikit.tuicallkit.utils.UserInfoUtils;
import com.tencent.qcloud.tuikit.tuicallkit.view.common.RoundCornerImageView;
import com.tencent.qcloud.tuikit.tuicallkit.view.component.BaseUserView;
import com.tencent.qcloud.tuikit.tuicallkit.view.component.TUICallingSingleVideoUserView;
import com.tencent.qcloud.tuikit.tuicallkit.view.component.TUICallingUserView;
import com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow.FloatCallView;
import com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow.FloatWindowService;
import com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow.HomeWatcher;
import com.tencent.qcloud.tuikit.tuicallkit.view.function.BaseFunctionView;
import com.tencent.qcloud.tuikit.tuicallkit.view.function.TUICallingAudioFunctionView;
import com.tencent.qcloud.tuikit.tuicallkit.view.function.TUICallingSwitchAudioView;
import com.tencent.qcloud.tuikit.tuicallkit.view.function.TUICallingVideoFunctionView;
import com.tencent.qcloud.tuikit.tuicallkit.view.function.TUICallingVideoInviteFunctionView;
import com.tencent.qcloud.tuikit.tuicallkit.view.function.TUICallingWaitFunctionView;
import com.tencent.qcloud.tuikit.tuicallkit.view.root.BaseCallView;
import com.tencent.qcloud.tuikit.tuicallkit.view.root.TUICallingGroupView;
import com.tencent.qcloud.tuikit.tuicallkit.view.root.TUICallingImageView;
import com.tencent.qcloud.tuikit.tuicallkit.view.root.TUICallingSingleView;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class TUICallingViewManager implements ITUINotification {
private static final String TAG = "TUICallingViewManager";
private final Context mContext;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final UserLayoutFactory mUserLayoutFactory;
private final TUICallingAction mCallingAction;
private CallingUserModel mSelfUserModel;
private List<CallingUserModel> mInviteeList = new ArrayList<>();
private CallingUserModel mInviter;
private boolean mEnableFloatView = false;
private BaseCallView mBaseCallView;
private BaseFunctionView mFunctionView;
private BaseUserView mUserView;
private ImageView mImageFloatFunction;
private FloatCallView mFloatCallView;
private LinearLayout mOtherUserLayout;
private HomeWatcher mHomeWatcher;
private UserInfoUtils mUserInfoUtils;
public TUICallingViewManager(Context context) {
mContext = context.getApplicationContext();
mUserLayoutFactory = new UserLayoutFactory(mContext);
mCallingAction = new TUICallingAction(context);
mUserInfoUtils = new UserInfoUtils();
registerCallingEvent();
}
private void registerCallingEvent() {
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CAMERA_OPEN, this);
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_MIC_STATUS_CHANGED, this);
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CALL_STATUS_CHANGED, this);
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CALL_TYPE_CHANGED, this);
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_GROUP_MEMBER_SELECTED, this);
}
public void createCallingView(List<CallingUserModel> inviteeList, CallingUserModel inviter) {
mInviter = inviter;
mInviteeList = inviteeList;
initHomeWatcher();
TUILog.i(TAG, "createCallingView mInviter: " + mInviter + " ,mInviteeList: " + mInviteeList);
if (Scene.SINGLE_CALL.equals(TUICallingStatusManager.sharedInstance(mContext).getCallScene())) {
initSingleWaitingView();
} else {
initGroupWaitingView();
}
}
//JoinInGroupCall
public void createGroupCallingAcceptView() {
initSelfModel();
mUserLayoutFactory.allocUserLayout(mSelfUserModel);
initGroupAcceptCallView();
}
public void updateCallingUserView(List<CallingUserModel> inviteeList, CallingUserModel inviter) {
mInviter = inviter;
mInviteeList = inviteeList;
CallingUserModel userModel;
if (TUICallDefine.Role.Caller.equals(TUICallingStatusManager.sharedInstance(mContext).getCallRole())) {
userModel = inviteeList.get(0);
for (CallingUserModel model : inviteeList) {
reloadUserModel(model);
}
} else {
userModel = inviter;
reloadUserModel(inviter);
}
if (null != mUserView) {
mUserView.updateUserInfo(userModel);
}
initOtherInviteeView();
}
public void showCallingView() {
TUILog.i(TAG, "showCallingView: mBaseCallView: " + mBaseCallView);
BaseCallActivity.updateBaseView(mBaseCallView);
Intent intent = new Intent(mContext, BaseCallActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
public void closeCallingView() {
if (null != mBaseCallView) {
mBaseCallView.finish();
}
mBaseCallView = null;
BaseCallActivity.finishActivity();
SelectGroupMemberActivity.finishActivity();
mSelfUserModel = null;
mInviteeList.clear();
mInviter = new CallingUserModel();
TUICallingStatusManager.sharedInstance(mContext).clear();
mFunctionView = null;
mFloatCallView = null;
mUserView = null;
mImageFloatFunction = null;
mOtherUserLayout = null;
if (null != mHomeWatcher) {
mHomeWatcher.stopWatch();
mHomeWatcher = null;
}
FloatWindowService.stopService(mContext);
}
private void updateCallStatus(TUICallDefine.Status status) {
if (TUICallDefine.Status.None.equals(status)) {
closeCallingView();
return;
}
if (!TUICallDefine.Status.Accept.equals(status)) {
return;
}
if (null != mBaseCallView) {
if (Scene.SINGLE_CALL.equals(TUICallingStatusManager.sharedInstance(mContext).getCallScene())) {
initSingleAcceptCallView();
} else {
initGroupAcceptCallView();
}
}
if (null != mFloatCallView) {
updateFloatView(TUICallingStatusManager.sharedInstance(mContext).getCallStatus());
}
}
private void updateCallType(TUICallDefine.MediaType type) {
if (TUICallDefine.MediaType.Unknown.equals(type)) {
return;
}
if (mBaseCallView == null) {
return;
}
mBaseCallView.finish();
mBaseCallView = null;
if (TUICallDefine.Status.Waiting.equals(TUICallingStatusManager.sharedInstance(mContext).getCallStatus())) {
initSingleWaitingView();
} else {
initAudioPlayDevice();
initSingleAcceptCallView();
}
if (null != mFloatCallView) {
updateFloatView(TUICallingStatusManager.sharedInstance(mContext).getCallStatus());
}
}
public void userCallingTimeStr(String time) {
if (null == mBaseCallView) {
return;
}
mMainHandler.post(new Runnable() {
@Override
public void run() {
if (null != mFloatCallView) {
mFloatCallView.updateCallTimeView(time);
}
if (null != mBaseCallView) {
mBaseCallView.updateCallTimeView(time);
}
}
});
}
public void userEnter(CallingUserModel userModel) {
if (null == userModel || TextUtils.isEmpty(userModel.userId)) {
return;
}
userModel.isEnter = true;
if (!mInviteeList.contains(userModel)) {
mInviteeList.add(userModel);
}
if (null != mBaseCallView) {
mBaseCallView.userEnter(userModel);
}
if (TextUtils.isEmpty(userModel.userName) || TextUtils.isEmpty(userModel.userAvatar)) {
loadUserInfo(userModel);
}
}
public void userLeave(CallingUserModel userModel) {
if (userModel == null || TextUtils.isEmpty(userModel.userId)) {
return;
}
if (mInviter != null && userModel.userId.equals(mInviter.userId)) {
mInviter = new CallingUserModel();
}
mInviteeList.remove(userModel);
initOtherInviteeView();
if (null != mBaseCallView) {
mBaseCallView.userLeave(userModel);
}
}
public void updateUser(CallingUserModel userModel) {
if (null == userModel || TextUtils.isEmpty(userModel.userId)) {
return;
}
initSelfModel();
if (mSelfUserModel != null && userModel.userId.equals(mSelfUserModel.userId)) {
userModel.isVideoAvailable = mSelfUserModel.isVideoAvailable;
userModel.isAudioAvailable = mSelfUserModel.isAudioAvailable;
}
if (null != mBaseCallView) {
mBaseCallView.updateUserInfo(userModel);
}
if (mFloatCallView != null) {
CallingUserModel model = findCallingUserModel(mFloatCallView.getCurrentUser());
if (model != null && userModel.userId.equals(model.userId)) {
updateFloatView(TUICallingStatusManager.sharedInstance(mContext).getCallStatus());
}
return;
}
UserLayout layout = mUserLayoutFactory.findUserLayout(userModel.userId);
if (null != layout) {
layout.setAudioVolume(userModel.volume, userModel.isAudioAvailable);
}
}
public void enableFloatWindow(boolean enable) {
mEnableFloatView = enable;
}
private void initSingleWaitingView() {
initSelfModel();
initAudioPlayDevice();
if (TUICallDefine.MediaType.Video.equals(TUICallingStatusManager.sharedInstance(mContext).getMediaType())) {
initSingleVideoWaitingView();
} else {
initSingleAudioWaitingView();
}
}
private void initSingleAudioWaitingView() {
mBaseCallView = new TUICallingImageView(mContext);
String hint;
TUICallDefine.Role callRole = TUICallingStatusManager.sharedInstance(mContext).getCallRole();
if (TUICallDefine.Role.Caller.equals(callRole)) {
hint = mContext.getString(R.string.tuicalling_waiting_accept);
mFunctionView = new TUICallingAudioFunctionView(mContext);
} else {
hint = mContext.getString(R.string.tuicalling_invite_audio_call);
mFunctionView = new TUICallingWaitFunctionView(mContext);
}
mUserView = new TUICallingUserView(mContext);
if (TUICallDefine.Role.Caller.equals(callRole) && !mInviteeList.isEmpty()) {
mUserView.updateUserInfo(mInviteeList.get(0));
} else {
mUserView.updateUserInfo(mInviter);
}
mBaseCallView.updateUserView(mUserView);
mBaseCallView.updateCallingHint(hint);
mBaseCallView.updateFunctionView(mFunctionView);
updateViewColor();
initFloatingWindowBtn();
BaseCallActivity.updateBaseView(mBaseCallView);
}
private void initSingleVideoWaitingView() {
mUserLayoutFactory.allocUserLayout(mSelfUserModel);
mBaseCallView = new TUICallingSingleView(mContext, mUserLayoutFactory);
String hint = "";
if (TUICallDefine.Role.Caller.equals(TUICallingStatusManager.sharedInstance(mContext).getCallRole())) {
hint = mContext.getString(R.string.tuicalling_waiting_accept);
mFunctionView = new TUICallingVideoInviteFunctionView(mContext);
} else {
hint = mContext.getString(R.string.tuicalling_invite_video_call);
mFunctionView = new TUICallingWaitFunctionView(mContext);
}
mUserView = new TUICallingSingleVideoUserView(mContext, hint);
mBaseCallView.updateUserView(mUserView);
mBaseCallView.updateSwitchAudioView(new TUICallingSwitchAudioView(mContext));
mFunctionView.setLocalUserLayout(mUserLayoutFactory.findUserLayout(TUILogin.getLoginUser()));
mBaseCallView.updateFunctionView(mFunctionView);
updateViewColor();
initFloatingWindowBtn();
BaseCallActivity.updateBaseView(mBaseCallView);
}
private void initGroupWaitingView() {
initSelfModel();
initAudioPlayDevice();
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
String hint;
if (TUICallDefine.Role.Caller.equals(TUICallingStatusManager.sharedInstance(mContext).getCallRole())) {
mUserLayoutFactory.allocUserLayout(mSelfUserModel);
for (CallingUserModel model : mInviteeList) {
if (null != model && !TextUtils.isEmpty(model.userId)) {
mUserLayoutFactory.allocUserLayout(model);
}
}
mBaseCallView = new TUICallingGroupView(mContext, mUserLayoutFactory);
hint = mContext.getString(R.string.tuicalling_waiting_accept);
mFunctionView = (TUICallDefine.MediaType.Video.equals(mediaType))
? new TUICallingVideoFunctionView(mContext) :
new TUICallingAudioFunctionView(mContext);
initInviteUserFunction();
} else {
mBaseCallView = new TUICallingImageView(mContext);
mUserView = new TUICallingUserView(mContext);
mFunctionView = new TUICallingWaitFunctionView(mContext);
hint = TUICallDefine.MediaType.Audio.equals(mediaType)
? mContext.getString(R.string.tuicalling_invite_audio_call) :
mContext.getString(R.string.tuicalling_invite_video_call);
mBaseCallView.updateUserView(mUserView);
mBaseCallView.addOtherUserView(initOtherInviteeView());
}
mFunctionView.setLocalUserLayout(mUserLayoutFactory.findUserLayout(TUILogin.getLoginUser()));
mBaseCallView.updateCallingHint(hint);
mBaseCallView.updateFunctionView(mFunctionView);
updateViewColor();
updateFunctionStatus();
initFloatingWindowBtn();
}
private void initSingleAcceptCallView() {
if (TUICallDefine.MediaType.Audio.equals(TUICallingStatusManager.sharedInstance(mContext).getMediaType())) {
initSingleAudioAcceptCallView();
} else {
initSingleVideoAcceptCallView();
}
}
private void initSingleAudioAcceptCallView() {
if (mBaseCallView != null) {
mBaseCallView.finish();
mBaseCallView = null;
}
mBaseCallView = new TUICallingImageView(mContext);
mFunctionView = new TUICallingAudioFunctionView(mContext);
mUserView = new TUICallingUserView(mContext);
TUICallDefine.Role role = TUICallingStatusManager.sharedInstance(mContext).getCallRole();
if (TUICallDefine.Role.Caller.equals(role) && !mInviteeList.isEmpty()) {
mUserView.updateUserInfo(mInviteeList.get(0));
} else {
mUserView.updateUserInfo(mInviter);
}
mBaseCallView.updateFunctionView(mFunctionView);
mBaseCallView.updateUserView(mUserView);
mBaseCallView.updateCallingHint("");
updateViewColor();
updateFunctionStatus();
initFloatingWindowBtn();
BaseCallActivity.updateBaseView(mBaseCallView);
}
private void initSingleVideoAcceptCallView() {
if (null == mBaseCallView) {
mBaseCallView = new TUICallingSingleView(mContext, mUserLayoutFactory);
mBaseCallView.updateSwitchAudioView(new TUICallingSwitchAudioView(mContext));
}
mFunctionView = new TUICallingVideoFunctionView(mContext);
mFunctionView.setLocalUserLayout(mUserLayoutFactory.findUserLayout(TUILogin.getLoginUser()));
mBaseCallView.updateUserView(null);
mBaseCallView.updateFunctionView(mFunctionView);
updateViewColor();
updateFunctionStatus();
initFloatingWindowBtn();
BaseCallActivity.updateBaseView(mBaseCallView);
}
private void initGroupAcceptCallView() {
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
if (TUICallDefine.Role.Caller.equals(TUICallingStatusManager.sharedInstance(mContext).getCallRole())) {
if (null == mBaseCallView) {
mBaseCallView = new TUICallingGroupView(mContext, mUserLayoutFactory);
}
if (TUICallDefine.MediaType.Video.equals(mediaType)) {
mFunctionView = new TUICallingVideoFunctionView(mContext);
mBaseCallView.updateFunctionView(mFunctionView);
}
} else {
if (null != mBaseCallView) {
mBaseCallView.finish();
}
mUserLayoutFactory.allocUserLayout(mInviter);
for (CallingUserModel model : mInviteeList) {
if (null != model && !TextUtils.isEmpty(model.userId)) {
mUserLayoutFactory.allocUserLayout(model);
}
}
mBaseCallView = new TUICallingGroupView(mContext, mUserLayoutFactory);
mFunctionView = (TUICallDefine.MediaType.Video.equals(mediaType))
? new TUICallingVideoFunctionView(mContext)
: new TUICallingAudioFunctionView(mContext);
mBaseCallView.updateFunctionView(mFunctionView);
}
mFunctionView.setLocalUserLayout(mUserLayoutFactory.findUserLayout(TUILogin.getLoginUser()));
mBaseCallView.updateCallingHint("");
updateViewColor();
updateFunctionStatus();
initInviteUserFunction();
initFloatingWindowBtn();
BaseCallActivity.updateBaseView(mBaseCallView);
}
private View initOtherInviteeView() {
if (mOtherUserLayout == null) {
mOtherUserLayout = new LinearLayout(mContext);
} else {
mOtherUserLayout.removeAllViews();
}
List<CallingUserModel> otherInviteeList = mInviteeList;
int squareWidth = mContext.getResources().getDimensionPixelOffset(R.dimen.tuicalling_small_image_size);
int leftMargin = mContext.getResources().getDimensionPixelOffset(R.dimen.tuicalling_small_image_left_margin);
for (int index = 0; index < otherInviteeList.size(); index++) {
final CallingUserModel model = otherInviteeList.get(index);
final ImageView imageView = new ImageView(mContext);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(squareWidth, squareWidth);
if (index != 0) {
layoutParams.leftMargin = leftMargin;
}
imageView.setLayoutParams(layoutParams);
ImageLoader.loadImage(mContext, imageView, model.userAvatar, R.drawable.tuicalling_ic_avatar);
mOtherUserLayout.addView(imageView);
}
return mOtherUserLayout;
}
private void updateViewColor() {
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
int backgroundColor = TUICallDefine.MediaType.Video.equals(mediaType)
? mContext.getResources().getColor(R.color.tuicalling_color_video_background)
: mContext.getResources().getColor(R.color.tuicalling_color_audio_background);
int textColor = TUICallDefine.MediaType.Video.equals(mediaType)
? mContext.getResources().getColor(R.color.tuicalling_color_white)
: mContext.getResources().getColor(R.color.tuicalling_color_black);
if (null != mBaseCallView) {
mBaseCallView.updateBackgroundColor(backgroundColor);
mBaseCallView.updateTextColor(textColor);
}
if (null != mUserView) {
mUserView.updateTextColor(textColor);
}
if (null != mFunctionView) {
mFunctionView.updateTextColor(textColor);
}
}
private void updateFunctionStatus() {
TUICallingStatusManager statusManager = TUICallingStatusManager.sharedInstance(mContext);
statusManager.updateCameraOpenStatus(statusManager.isCameraOpen(), statusManager.getFrontCamera());
mCallingAction.selectAudioPlaybackDevice(statusManager.getAudioPlaybackDevice());
if (statusManager.isMicMute()) {
mCallingAction.closeMicrophone();
} else {
mCallingAction.openMicrophone(null);
}
}
private void initAudioPlayDevice() {
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
TUICommonDefine.AudioPlaybackDevice device = TUICallDefine.MediaType.Audio.equals(mediaType)
? TUICommonDefine.AudioPlaybackDevice.Earpiece : TUICommonDefine.AudioPlaybackDevice.Speakerphone;
TUICallingStatusManager.sharedInstance(mContext).updateAudioPlaybackDevice(device);
}
private void initSelfModel() {
if (mSelfUserModel != null) {
return;
}
mSelfUserModel = new CallingUserModel();
mSelfUserModel.userId = TUILogin.getLoginUser();
mSelfUserModel.userAvatar = TUILogin.getFaceUrl();
mSelfUserModel.userName = TUILogin.getNickName();
}
private void reloadUserModel(CallingUserModel model) {
if (model == null || TextUtils.isEmpty(model.userId)) {
return;
}
UserLayout layout = mUserLayoutFactory.findUserLayout(model.userId);
if (null != layout) {
layout.setUserName(model.userName);
ImageLoader.loadImage(mContext, layout.getAvatarImage(), model.userAvatar, R.drawable.tuicalling_ic_avatar);
}
}
private void initFloatingWindowBtn() {
if (mBaseCallView == null) {
return;
}
if (mEnableFloatView) {
mImageFloatFunction = new ImageView(mContext);
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
int resId = TUICallDefine.MediaType.Video.equals(mediaType)
? R.drawable.tuicalling_ic_move_back_white : R.drawable.tuicalling_ic_move_back_black;
mImageFloatFunction.setBackgroundResource(resId);
ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
mImageFloatFunction.setLayoutParams(lp);
mImageFloatFunction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startFloatService();
}
});
}
if (mImageFloatFunction != null && mImageFloatFunction.getParent() != null) {
((ViewGroup) mImageFloatFunction.getParent()).removeView(mImageFloatFunction);
}
mBaseCallView.enableFloatView(mImageFloatFunction);
}
private void startFloatService() {
if (!mEnableFloatView) {
return;
}
if (null != mFloatCallView) {
return;
}
if (PermissionUtils.hasPermission(mContext)) {
mFloatCallView = createFloatView();
updateFloatView(TUICallingStatusManager.sharedInstance(mContext).getCallStatus());
FloatWindowService.startFloatService(mContext, mFloatCallView);
BaseCallActivity.finishActivity();
} else {
PermissionRequest.requestFloatPermission(mContext);
}
}
private FloatCallView createFloatView() {
FloatCallView floatView = new FloatCallView(mContext, mUserLayoutFactory);
floatView.setOnClickListener(new FloatCallView.OnClickListener() {
@Override
public void onClick() {
FloatWindowService.stopService(mContext);
mFloatCallView = null;
mImageFloatFunction = null;
MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
Scene callScene = TUICallingStatusManager.sharedInstance(mContext).getCallScene();
if (MediaType.Video.equals(mediaType) && Scene.SINGLE_CALL.equals(callScene)) {
Role callRole = TUICallingStatusManager.sharedInstance(mContext).getCallRole();
CallingUserModel model = (Role.Called.equals(callRole)) ? mInviter : mInviteeList.get(0);
Status status = TUICallingStatusManager.sharedInstance(mContext).getCallStatus();
if (Status.Accept.equals(status)) {
resetVideoCloudView(mSelfUserModel);
resetVideoCloudView(model);
} else {
resetVideoCloudView(mSelfUserModel);
}
}
showCallingView();
}
});
return floatView;
}
private void resetVideoCloudView(CallingUserModel model) {
UserLayout userLayout = mUserLayoutFactory.findUserLayout(model.userId);
if (null == userLayout) {
userLayout = mUserLayoutFactory.allocUserLayout(model);
}
TUIVideoView videoView = userLayout.getVideoView();
if (videoView != null) {
if (null != videoView.getParent()) {
((ViewGroup) videoView.getParent()).removeView(videoView);
}
userLayout.addVideoView(videoView);
}
}
private void updateFloatView(TUICallDefine.Status status) {
if (null == mFloatCallView) {
return;
}
TUICallDefine.MediaType type = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
Scene scene = TUICallingStatusManager.sharedInstance(mContext).getCallScene();
if (TUICallDefine.MediaType.Video.equals(type) && Scene.SINGLE_CALL.equals(scene)) {
mFloatCallView.enableCallingHint(false);
String userId;
if (TUICallDefine.Status.Waiting.equals(status)) {
userId = TUILogin.getLoginUser();
} else {
TUICallDefine.Role callRole = TUICallingStatusManager.sharedInstance(mContext).getCallRole();
userId = (TUICallDefine.Role.Caller.equals(callRole)) ? mInviteeList.get(0).userId : mInviter.userId;
}
mFloatCallView.updateView(true, userId);
} else {
mFloatCallView.enableCallingHint(TUICallDefine.Status.Waiting.equals(status));
mFloatCallView.updateView(false, null);
}
}
private void initHomeWatcher() {
if (null == mHomeWatcher) {
mHomeWatcher = new HomeWatcher(mContext);
}
mHomeWatcher.setOnHomePressedListener(new HomeWatcher.OnHomePressedListener() {
@Override
public void onHomePressed() {
if (PermissionUtils.hasPermission(mContext)) {
startFloatService();
}
}
@Override
public void onRecentAppsPressed() {
if (PermissionUtils.hasPermission(mContext)) {
startFloatService();
}
}
});
mHomeWatcher.startWatch();
}
private CallingUserModel findCallingUserModel(String userId) {
if (TextUtils.isEmpty(userId)) {
return null;
}
for (UserLayoutEntity entity : mUserLayoutFactory.mLayoutEntityList) {
if (entity != null && userId.equals(entity.userId)) {
return entity.userModel;
}
}
return null;
}
private void initInviteUserFunction() {
if (mBaseCallView == null) {
return;
}
Button inviteUserBtn = new Button(mContext);
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
int resId = TUICallDefine.MediaType.Video.equals(mediaType)
? R.drawable.tuicalling_ic_add_user_white : R.drawable.tuicalling_ic_add_user_black;
inviteUserBtn.setBackgroundResource(resId);
inviteUserBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String groupId = TUICallingStatusManager.sharedInstance(mContext).getGroupId();
if (TextUtils.isEmpty(groupId)) {
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_groupid_is_empty));
return;
}
TUICallDefine.Status status = TUICallingStatusManager.sharedInstance(mContext).getCallStatus();
TUICallDefine.Role role = TUICallingStatusManager.sharedInstance(mContext).getCallRole();
if (TUICallDefine.Role.Called.equals(role) && !TUICallDefine.Status.Accept.equals(status)) {
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_status_is_not_accept));
return;
}
ArrayList<String> list = new ArrayList<>();
list.add(mInviter.userId);
for (CallingUserModel model : mInviteeList) {
if (model != null && !TextUtils.isEmpty(model.userId) && !list.contains(model.userId)) {
list.add(model.userId);
}
}
if (!list.contains(TUILogin.getLoginUser())) {
list.add(TUILogin.getLoginUser());
}
TUILog.i(TAG, "initInviteUserFunction, groupId: " + groupId + " ,list: " + list);
Bundle bundle = new Bundle();
bundle.putString(Constants.GROUP_ID, groupId);
bundle.putStringArrayList(Constants.SELECT_MEMBER_LIST, list);
TUICore.startActivity("SelectGroupMemberActivity", bundle);
}
});
mBaseCallView.enableAddUserView(inviteUserBtn);
}
private void inviteUsersToGroupCall(List<String> userIdList) {
if (userIdList == null || userIdList.isEmpty()) {
TUILog.e(TAG, "inviteUsersToGroupCall, userIdList is empty: " + userIdList);
return;
}
mCallingAction.inviteUser(userIdList, new TUICommonDefine.ValueCallback() {
@Override
public void onSuccess(Object data) {
if (!(data instanceof List)) {
return;
}
List<String> list = (List<String>) data;
TUILog.i(TAG, "inviteUsersToGroupCall success, list:" + list);
for (String userId : list) {
if (!TextUtils.isEmpty(userId)) {
CallingUserModel userModel = new CallingUserModel();
userModel.userId = userId;
addUser(userModel);
}
}
}
@Override
public void onError(int errCode, String errMsg) {
}
});
}
private void addUser(CallingUserModel userModel) {
if (!mInviteeList.contains(userModel)) {
mInviteeList.add(userModel);
}
if (null != mBaseCallView) {
mBaseCallView.userAdd(userModel);
}
loadUserInfo(userModel);
}
private void loadUserInfo(CallingUserModel model) {
UserLayout userLayout = mUserLayoutFactory.findUserLayout(model.userId);
if (userLayout == null) {
return;
}
mUserInfoUtils.getUserInfo(model.userId, new UserInfoUtils.UserCallback() {
@Override
public void onSuccess(List<CallingUserModel> list) {
if (list != null && !list.isEmpty()) {
CallingUserModel tempModel = list.get(0);
if (tempModel != null && model.userId.equals(tempModel.userId)) {
model.userAvatar = tempModel.userAvatar;
model.userName = tempModel.userName;
}
}
userLayout.setUserName(model.userName);
userLayout.setVideoAvailable(model.isVideoAvailable);
ImageLoader.loadImage(mContext, userLayout.getAvatarImage(), model.userAvatar,
R.drawable.tuicalling_ic_avatar);
}
@Override
public void onFailed(int errorCode, String errorMsg) {
}
});
}
@Override
public void onNotifyEvent(String key, String subKey, Map<String, Object> param) {
if (param == null) {
return;
}
if (Constants.EVENT_TUICALLING_CHANGED.equals(key)) {
switch (subKey) {
case Constants.EVENT_SUB_CAMERA_OPEN:
if (mSelfUserModel == null) {
break;
}
mSelfUserModel.isVideoAvailable = (boolean) param.get(Constants.OPEN_CAMERA);
UserLayout selfLayout = mUserLayoutFactory.findUserLayout(mSelfUserModel.userId);
if (selfLayout == null) {
break;
}
selfLayout.setVideoAvailable(mSelfUserModel.isVideoAvailable);
if (Scene.SINGLE_CALL.equals(TUICallingStatusManager.sharedInstance(mContext).getCallScene())) {
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(180, 180);
lp.addRule(RelativeLayout.CENTER_IN_PARENT);
((RoundCornerImageView) selfLayout.getAvatarImage()).setRadius(15);
selfLayout.getAvatarImage().setLayoutParams(lp);
}
ImageLoader.loadImage(mContext, selfLayout.getAvatarImage(), mSelfUserModel.userAvatar);
break;
case Constants.EVENT_SUB_MIC_STATUS_CHANGED:
if (mSelfUserModel != null) {
mSelfUserModel.isAudioAvailable = ((Boolean) param.get(Constants.MUTE_MIC));
}
UserLayout layout = mUserLayoutFactory.findUserLayout(TUILogin.getLoginUser());
if (null != layout) {
layout.muteMic(mSelfUserModel.isAudioAvailable);
}
break;
case Constants.EVENT_SUB_CALL_STATUS_CHANGED:
updateCallStatus((TUICallDefine.Status) param.get(Constants.CALL_STATUS));
break;
case Constants.EVENT_SUB_CALL_TYPE_CHANGED:
updateCallType((TUICallDefine.MediaType) param.get(Constants.CALL_MEDIA_TYPE));
break;
case Constants.EVENT_SUB_GROUP_MEMBER_SELECTED:
List<String> list = (List<String>) param.get(Constants.SELECT_MEMBER_LIST);
inviteUsersToGroupCall(list);
break;
default:
break;
}
}
}
}
@@ -0,0 +1,49 @@
package com.tencent.qcloud.tuikit.tuicallkit.view;
import android.content.Context;
import android.text.TextUtils;
import android.view.View;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayoutEntity;
import java.util.LinkedList;
public class UserLayoutFactory {
private Context mContext;
public LinkedList<UserLayoutEntity> mLayoutEntityList = new LinkedList<>();
public UserLayoutFactory(Context context) {
mContext = context;
}
public UserLayout allocUserLayout(CallingUserModel model) {
if (null == model || TextUtils.isEmpty(model.userId)) {
return null;
}
UserLayout userLayout = findUserLayout(model.userId);
if (null != userLayout) {
return userLayout;
}
UserLayoutEntity layoutEntity = new UserLayoutEntity();
layoutEntity.userId = model.userId;
layoutEntity.userModel = model;
layoutEntity.layout = new UserLayout(mContext);
layoutEntity.layout.setVisibility(View.VISIBLE);
mLayoutEntityList.add(layoutEntity);
return layoutEntity.layout;
}
public UserLayout findUserLayout(String userId) {
if (TextUtils.isEmpty(userId)) {
return null;
}
for (UserLayoutEntity layoutEntity : mLayoutEntityList) {
if (userId.equals(layoutEntity.userId)) {
return layoutEntity.layout;
}
}
return null;
}
}
@@ -0,0 +1,132 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.common;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
public class RoundCornerImageView extends AppCompatImageView {
private final Path path = new Path();
private final RectF rectF = new RectF();
private final PaintFlagsDrawFilter aliasFilter = new PaintFlagsDrawFilter(0,
Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
private int radius;
private int leftTopRadius;
private int rightTopRadius;
private int rightBottomRadius;
private int leftBottomRadius;
public RoundCornerImageView(@NonNull Context context) {
super(context);
init(context, null);
}
public RoundCornerImageView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public RoundCornerImageView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs) {
setLayerType(View.LAYER_TYPE_HARDWARE, null);
int defaultRadius = 0;
if (attrs != null) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RoundCornerImageView);
radius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_corner_radius, defaultRadius);
leftTopRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_left_top_radius,
defaultRadius);
rightTopRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_right_top_radius,
defaultRadius);
rightBottomRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_right_bottom_radius,
defaultRadius);
leftBottomRadius = array.getDimensionPixelOffset(R.styleable.RoundCornerImageView_left_bottom_radius,
defaultRadius);
array.recycle();
}
if (defaultRadius == leftTopRadius) {
leftTopRadius = radius;
}
if (defaultRadius == rightTopRadius) {
rightTopRadius = radius;
}
if (defaultRadius == rightBottomRadius) {
rightBottomRadius = radius;
}
if (defaultRadius == leftBottomRadius) {
leftBottomRadius = radius;
}
}
public void setRadius(int radius) {
this.radius = radius;
leftBottomRadius = radius;
rightBottomRadius = radius;
rightTopRadius = radius;
leftTopRadius = radius;
}
public int getRadius() {
return radius;
}
public void setLeftTopRadius(int leftTopRadius) {
this.leftTopRadius = leftTopRadius;
}
public void setLeftBottomRadius(int leftBottomRadius) {
this.leftBottomRadius = leftBottomRadius;
}
public void setRightTopRadius(int rightTopRadius) {
this.rightTopRadius = rightTopRadius;
}
public void setRightBottomRadius(int rightBottomRadius) {
this.rightBottomRadius = rightBottomRadius;
}
public int getLeftTopRadius() {
return leftTopRadius;
}
public int getLeftBottomRadius() {
return leftBottomRadius;
}
public int getRightTopRadius() {
return rightTopRadius;
}
public int getRightBottomRadius() {
return rightBottomRadius;
}
@Override
protected void onDraw(Canvas canvas) {
path.reset();
canvas.setDrawFilter(aliasFilter);
rectF.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
// left-top -> right-top -> right-bottom -> left-bottom
float[] radius = {leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius,
rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius};
path.addRoundRect(rectF, radius, Path.Direction.CW);
canvas.clipPath(path);
super.onDraw(canvas);
}
}
@@ -0,0 +1,226 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.common;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* 支持左滑删除的RecyclerView
*/
public class SlideRecyclerView extends RecyclerView {
private static final String TAG = "SlideRecyclerView";
private static final int INVALID_POSITION = -1; // 触摸到的点不在子View范围内
private static final int INVALID_CHILD_WIDTH = -1; // 子ItemView不含两个子View
private static final int SNAP_VELOCITY = 600; // 最小滑动速度
private VelocityTracker mVelocityTracker; // 速度追踪器
private ViewGroup mFlingView; // 触碰的子View
private Scroller mScroller; // 触碰动画
private Rect mTouchFrame; // 子View所在的矩形范围
private float mLastX; // 滑动过程中记录上次触碰点X
private float mFirstX;
private float mFirstY; // 首次触碰范围
private int mTouchSlop; // 认为是滑动的最小距离(一般由系统提供)
private boolean mIsSlide; // 是否滑动子View
private int mPosition; // 触碰的view的位置
private int mMenuViewWidth; // 菜单按钮宽度
private boolean mDisableRecyclerViewSlide;
public SlideRecyclerView(Context context) {
this(context, null);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
mScroller = new Scroller(context);
}
public void disableRecyclerViewSlide(boolean disable) {
mDisableRecyclerViewSlide = disable;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int x = (int) e.getX();
int y = (int) e.getY();
obtainVelocity(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mFirstX = mLastX = x;
mFirstY = y;
// 获取触碰点所在的position
mPosition = pointToPosition(x, y);
if (mPosition != INVALID_POSITION) {
View view = mFlingView;
// 获取触碰点所在的view
mFlingView = (ViewGroup) getChildAt(mPosition - ((LinearLayoutManager) getLayoutManager())
.findFirstVisibleItemPosition());
// 如果之前触碰的view已经打开,而当前碰到的view不是那个view则立即关闭之前的view
if (view != null && mFlingView != view && view.getScrollX() != 0) {
view.scrollTo(0, 0);
}
if (mFlingView.getChildCount() == 2) {
mMenuViewWidth = mFlingView.getChildAt(1).getWidth();
} else {
mMenuViewWidth = INVALID_CHILD_WIDTH;
}
}
break;
case MotionEvent.ACTION_MOVE:
mVelocityTracker.computeCurrentVelocity(1000);
// 此处有俩判断,满足其一则认为是侧滑:
// 1.如果x方向速度大于y方向速度,且大于最小速度限制;
// 2.如果x方向的侧滑距离大于y方向滑动距离,且x方向达到最小滑动距离;
float xVelocity = mVelocityTracker.getXVelocity();
float yVelocity = mVelocityTracker.getYVelocity();
//找到最上面第一个view,不处理滑动事件但是也不拦截后续事件
View topView = ((LinearLayoutManager) getLayoutManager()).findViewByPosition(0);
if (topView == mFlingView) {
mIsSlide = false;
} else if (Math.abs(xVelocity) > SNAP_VELOCITY && Math.abs(xVelocity) > Math.abs(yVelocity)
|| Math.abs(x - mFirstX) >= mTouchSlop
&& Math.abs(x - mFirstX) > Math.abs(y - mFirstY)) {
mIsSlide = true;
return true;
}
break;
case MotionEvent.ACTION_UP:
releaseVelocity();
break;
default:
break;
}
return super.onInterceptTouchEvent(e);
}
@Override
public boolean onTouchEvent(MotionEvent e) {
if (mIsSlide && mPosition != INVALID_POSITION) {
float x = e.getX();
obtainVelocity(e);
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
// 随手指滑动
if (mMenuViewWidth != INVALID_CHILD_WIDTH) {
float dx = mLastX - x;
if (mFlingView.getScrollX() + dx <= mMenuViewWidth
&& mFlingView.getScrollX() + dx > 0) {
if (mMenuViewWidth != INVALID_CHILD_WIDTH) {
int scrollX = mFlingView.getScrollX();
mVelocityTracker.computeCurrentVelocity(1000);
// 此处有两个原因决定是否打开菜单:
// 1.菜单被拉出宽度大于菜单宽度一半;
// 2.横向滑动速度大于最小滑动速度;
// 注意:向左滑则速度为负值
if (mVelocityTracker.getXVelocity() < -SNAP_VELOCITY) { // 向左侧滑达到侧滑最低速度,则打开
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0,
Math.abs(mMenuViewWidth - scrollX));
} else if (mVelocityTracker.getXVelocity() >= SNAP_VELOCITY) { // 向右侧滑达到侧滑最低速度,则关闭
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
} else if (scrollX >= mMenuViewWidth / 2) { // 如果超过删除按钮一半,则打开
mScroller.startScroll(scrollX, 0, mMenuViewWidth - scrollX, 0,
Math.abs(mMenuViewWidth - scrollX));
} else { // 其他情况则关闭
mScroller.startScroll(scrollX, 0, -scrollX, 0, Math.abs(scrollX));
}
invalidate();
}
mMenuViewWidth = INVALID_CHILD_WIDTH;
mIsSlide = false;
mPosition = INVALID_POSITION;
}
mLastX = x;
}
break;
case MotionEvent.ACTION_UP:
releaseVelocity();
break;
default:
break;
}
return true;
} else {
// 防止RecyclerView正常滑动时,还有菜单未关闭
closeMenu();
releaseVelocity();
}
return super.onTouchEvent(e);
}
private void releaseVelocity() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
private void obtainVelocity(MotionEvent event) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}
public int pointToPosition(int x, int y) {
int firstPosition = ((LinearLayoutManager) getLayoutManager()).findFirstVisibleItemPosition();
Rect frame = mTouchFrame;
if (frame == null) {
mTouchFrame = new Rect();
frame = mTouchFrame;
}
final int count = getChildCount();
for (int i = count - 1; i >= 0; i--) {
final View child = getChildAt(i);
if (child.getVisibility() == View.VISIBLE) {
child.getHitRect(frame);
if (frame.contains(x, y)) {
return firstPosition + i;
}
}
}
return INVALID_POSITION;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()) {
if (mDisableRecyclerViewSlide) {
mFlingView.scrollTo(0, 0);
} else {
mFlingView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
}
invalidate();
}
}
public void closeMenu() {
if (mFlingView != null && mFlingView.getScrollX() != 0) {
mFlingView.scrollTo(0, 0);
}
}
}
@@ -0,0 +1,93 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.common.gridimage;
import android.graphics.Bitmap;
import android.graphics.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GridImageData implements Cloneable {
private static final int MAX_SIZE = 9;
protected List<Object> imageUrlList;
protected Map<Integer, Bitmap> bitmapMap;
protected int defaultImageResId;
protected int bgColor = Color.parseColor("#cfd3d8");
protected int targetImageSize;
protected int maxWidth;
protected int maxHeight;
protected int rowCount;
protected int columnCount;
protected int gap = 6;
public GridImageData() {
}
public GridImageData(List<Object> imageUrlList, int defaultImageResId) {
this.imageUrlList = imageUrlList;
this.defaultImageResId = defaultImageResId;
}
public int getDefaultImageResId() {
return defaultImageResId;
}
public void setDefaultImageResId(int defaultImageResId) {
this.defaultImageResId = defaultImageResId;
}
public List<Object> getImageUrlList() {
return imageUrlList;
}
public void setImageUrlList(List<Object> imageUrlList) {
this.imageUrlList = imageUrlList;
}
public void putBitmap(Bitmap bitmap, int position) {
if (null != bitmapMap) {
synchronized (bitmapMap) {
bitmapMap.put(position, bitmap);
}
} else {
bitmapMap = new HashMap<>();
synchronized (bitmapMap) {
bitmapMap.put(position, bitmap);
}
}
}
public Bitmap getBitmap(int position) {
if (null != bitmapMap) {
synchronized (bitmapMap) {
return bitmapMap.get(position);
}
}
return null;
}
public int size() {
if (null != imageUrlList) {
return Math.min(imageUrlList.size(), MAX_SIZE);
} else {
return 0;
}
}
@Override
protected GridImageData clone() throws CloneNotSupportedException {
GridImageData gridImageData = (GridImageData) super.clone();
if (imageUrlList != null) {
gridImageData.imageUrlList = new ArrayList<>(imageUrlList.size());
gridImageData.imageUrlList.addAll(imageUrlList);
}
if (bitmapMap != null) {
gridImageData.bitmapMap = new HashMap<>();
gridImageData.bitmapMap.putAll(bitmapMap);
}
return gridImageData;
}
}
@@ -0,0 +1,317 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.common.gridimage;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.text.TextUtils;
import android.widget.ImageView;
import com.tencent.qcloud.tuicore.TUIConfig;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class GridImageSynthesizer {
private GridImageData mGridImageData;
private Context mContext;
private ImageView mImageView;
private String mCurrentImageId = "";
public GridImageSynthesizer(Context mContext, ImageView imageView) {
this.mContext = mContext;
this.mImageView = imageView;
init();
}
private void init() {
mGridImageData = new GridImageData();
}
public void setImageUrls(List<Object> list) {
mGridImageData.setImageUrlList(list);
}
public void setMaxSize(int maxWidth, int maxHeight) {
mGridImageData.maxWidth = maxWidth;
mGridImageData.maxHeight = maxHeight;
}
public int getDefaultImage() {
return mGridImageData.getDefaultImageResId();
}
public void setDefaultImage(int defaultImageResId) {
mGridImageData.setDefaultImageResId(defaultImageResId);
}
public void setBgColor(int bgColor) {
mGridImageData.bgColor = bgColor;
}
public void setGap(int gap) {
mGridImageData.gap = gap;
}
public void setImageId(String id) {
mCurrentImageId = id;
}
public String getImageId() {
return mCurrentImageId;
}
private int[] calculateGridParam(int imagesSize) {
int[] gridParam = new int[2];
if (imagesSize < 3) {
gridParam[0] = 1;
gridParam[1] = imagesSize;
} else if (imagesSize <= 4) {
gridParam[0] = 2;
gridParam[1] = 2;
} else {
gridParam[0] = imagesSize / 3 + (imagesSize % 3 == 0 ? 0 : 1);
gridParam[1] = 3;
}
return gridParam;
}
private boolean asyncLoadImageList(GridImageData imageData) {
boolean loadSuccess = true;
List<Object> imageUrlList = imageData.getImageUrlList();
for (int i = 0; i < imageUrlList.size(); i++) {
Bitmap defaultIcon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.tuicalling_ic_avatar);
try {
Bitmap bitmap = asyncLoadImage(imageUrlList.get(i), imageData.targetImageSize);
imageData.putBitmap(bitmap, i);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
imageData.putBitmap(defaultIcon, i);
}
}
return loadSuccess;
}
private Bitmap synthesizeImageList(GridImageData imageData) {
Bitmap mergeBitmap = Bitmap.createBitmap(imageData.maxWidth, imageData.maxHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mergeBitmap);
drawDrawable(canvas, imageData);
canvas.save();
canvas.restore();
return mergeBitmap;
}
private void drawDrawable(Canvas canvas, GridImageData imageData) {
canvas.drawColor(imageData.bgColor);
int size = imageData.size();
int tCenter = (imageData.maxHeight + imageData.gap) / 2;
int bCenter = (imageData.maxHeight - imageData.gap) / 2;
int lCenter = (imageData.maxWidth + imageData.gap) / 2;
int rCenter = (imageData.maxWidth - imageData.gap) / 2;
int center = (imageData.maxHeight - imageData.targetImageSize) / 2;
for (int i = 0; i < size; i++) {
int rowNum = i / imageData.columnCount;
int columnNum = i % imageData.columnCount;
int left = ((int) (imageData.targetImageSize * (imageData.columnCount == 1 ? columnNum + 0.5 : columnNum)
+ imageData.gap * (columnNum + 1)));
int top = ((int) (imageData.targetImageSize * (imageData.columnCount == 1 ? rowNum + 0.5 : rowNum)
+ imageData.gap * (rowNum + 1)));
int right = left + imageData.targetImageSize;
int bottom = top + imageData.targetImageSize;
Bitmap bitmap = imageData.getBitmap(i);
if (size == 1) {
drawBitmapAtPosition(canvas, left, top, right, bottom, bitmap);
} else if (size == 2) {
drawBitmapAtPosition(canvas, left, center, right, center + imageData.targetImageSize, bitmap);
} else if (size == 3) {
if (i == 0) {
drawBitmapAtPosition(canvas, center, top, center + imageData.targetImageSize, bottom, bitmap);
} else {
drawBitmapAtPosition(canvas, imageData.gap * i + imageData.targetImageSize * (i - 1), tCenter,
imageData.gap * i + imageData.targetImageSize * i, tCenter + imageData.targetImageSize,
bitmap);
}
} else if (size == 4) {
drawBitmapAtPosition(canvas, left, top, right, bottom, bitmap);
} else if (size == 5) {
if (i == 0) {
drawBitmapAtPosition(canvas, rCenter - imageData.targetImageSize,
rCenter - imageData.targetImageSize, rCenter, rCenter, bitmap);
} else if (i == 1) {
drawBitmapAtPosition(canvas, lCenter, rCenter - imageData.targetImageSize,
lCenter + imageData.targetImageSize, rCenter, bitmap);
} else {
drawBitmapAtPosition(canvas, imageData.gap * (i - 1) + imageData.targetImageSize * (i - 2),
tCenter, imageData.gap * (i - 1) + imageData.targetImageSize * (i - 1), tCenter
+ imageData.targetImageSize, bitmap);
}
} else if (size == 6) {
if (i < 3) {
drawBitmapAtPosition(canvas, imageData.gap * (i + 1) + imageData.targetImageSize * i,
bCenter - imageData.targetImageSize,
imageData.gap * (i + 1) + imageData.targetImageSize * (i + 1), bCenter, bitmap);
} else {
drawBitmapAtPosition(canvas, imageData.gap * (i - 2) + imageData.targetImageSize * (i - 3),
tCenter, imageData.gap * (i - 2) + imageData.targetImageSize * (i - 2), tCenter
+ imageData.targetImageSize, bitmap);
}
} else if (size == 7) {
if (i == 0) {
drawBitmapAtPosition(canvas, center, imageData.gap, center + imageData.targetImageSize,
imageData.gap + imageData.targetImageSize, bitmap);
} else if (i > 0 && i < 4) {
drawBitmapAtPosition(canvas, imageData.gap * i + imageData.targetImageSize * (i - 1), center,
imageData.gap * i + imageData.targetImageSize * i, center + imageData.targetImageSize,
bitmap);
} else {
drawBitmapAtPosition(canvas, imageData.gap * (i - 3) + imageData.targetImageSize * (i - 4),
tCenter + imageData.targetImageSize / 2,
imageData.gap * (i - 3) + imageData.targetImageSize * (i - 3),
tCenter + imageData.targetImageSize / 2 + imageData.targetImageSize, bitmap);
}
} else if (size == 8) {
if (i == 0) {
drawBitmapAtPosition(canvas, rCenter - imageData.targetImageSize, imageData.gap, rCenter,
imageData.gap + imageData.targetImageSize, bitmap);
} else if (i == 1) {
drawBitmapAtPosition(canvas, lCenter, imageData.gap, lCenter + imageData.targetImageSize,
imageData.gap + imageData.targetImageSize, bitmap);
} else if (i > 1 && i < 5) {
drawBitmapAtPosition(canvas, imageData.gap * (i - 1) + imageData.targetImageSize * (i - 2),
center, imageData.gap * (i - 1) + imageData.targetImageSize * (i - 1),
center + imageData.targetImageSize, bitmap);
} else {
drawBitmapAtPosition(canvas, imageData.gap * (i - 4) + imageData.targetImageSize * (i - 5),
tCenter + imageData.targetImageSize / 2,
imageData.gap * (i - 4) + imageData.targetImageSize * (i - 4),
tCenter + imageData.targetImageSize / 2 + imageData.targetImageSize, bitmap);
}
} else if (size == 9) {
drawBitmapAtPosition(canvas, left, top, right, bottom, bitmap);
}
}
}
private void drawBitmapAtPosition(Canvas canvas, int left, int top, int right, int bottom, Bitmap bitmap) {
if (null == bitmap) {
if (mGridImageData.getDefaultImageResId() > 0) {
bitmap = BitmapFactory.decodeResource(mContext.getResources(), mGridImageData.getDefaultImageResId());
}
}
if (null != bitmap) {
Rect rect = new Rect(left, top, right, bottom);
canvas.drawBitmap(bitmap, null, rect, null);
}
}
private Bitmap asyncLoadImage(Object imgUrl, int targetImageSize) throws ExecutionException, InterruptedException {
return ImageLoader.loadBitmap(mContext, imgUrl, targetImageSize);
}
public void load(String imageId) {
if (mGridImageData.size() == 0) {
if (imageId != null && !TextUtils.equals(imageId, mCurrentImageId)) {
return;
}
ImageLoader.loadImage(mContext, mImageView, getDefaultImage());
return;
}
if (mGridImageData.size() == 1) {
if (imageId != null && !TextUtils.equals(imageId, mCurrentImageId)) {
return;
}
ImageLoader.loadImage(mContext, mImageView, mGridImageData.getImageUrlList().get(0));
return;
}
clearImage();
GridImageData copyGridImageData;
try {
copyGridImageData = mGridImageData.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
List<Object> urlList = new ArrayList();
if (mGridImageData.imageUrlList != null) {
urlList.addAll(mGridImageData.imageUrlList);
}
copyGridImageData = new GridImageData(urlList, mGridImageData.defaultImageResId);
}
int[] gridParam = calculateGridParam(mGridImageData.size());
copyGridImageData.rowCount = gridParam[0];
copyGridImageData.columnCount = gridParam[1];
copyGridImageData.targetImageSize = (copyGridImageData.maxWidth - (copyGridImageData.columnCount + 1)
* copyGridImageData.gap) / (copyGridImageData.columnCount == 1 ? 2 : copyGridImageData.columnCount);
final String finalImageId = imageId;
final GridImageData finalCopyGridImageData = copyGridImageData;
ThreadUtils.execute(new Runnable() {
@Override
public void run() {
final File file = new File(TUIConfig.getImageBaseDir() + finalImageId);
boolean cacheBitmapExists = false;
Bitmap existsBitmap = null;
if (file.exists() && file.isFile()) {
BitmapFactory.Options options = new BitmapFactory.Options();
existsBitmap = BitmapFactory.decodeFile(file.getPath(), options);
if (options.outWidth > 0 && options.outHeight > 0) {
cacheBitmapExists = true;
}
}
if (!cacheBitmapExists) {
asyncLoadImageList(finalCopyGridImageData);
existsBitmap = synthesizeImageList(finalCopyGridImageData);
storeBitmap(file, existsBitmap);
}
loadImage(existsBitmap, finalImageId);
}
});
}
private void loadImage(Bitmap bitmap, String targetId) {
ThreadUtils.runOnUIThread(() -> {
if (TextUtils.equals(getImageId(), targetId)) {
ImageLoader.loadImage(mContext, mImageView, bitmap);
}
});
}
public void clearImage() {
ImageLoader.clear(mContext, mImageView);
}
private void storeBitmap(File outFile, Bitmap bitmap) {
if (!outFile.exists() || outFile.isDirectory()) {
outFile.getParentFile().mkdirs();
}
FileOutputStream fOut = null;
try {
outFile.deleteOnExit();
outFile.createNewFile();
fOut = new FileOutputStream(outFile);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
} catch (IOException e) {
outFile.deleteOnExit();
} finally {
if (null != fOut) {
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
outFile.deleteOnExit();
}
}
}
}
}
@@ -0,0 +1,26 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.common.gridimage;
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadUtils {
private static Handler mMainHandler = new Handler(Looper.getMainLooper());
private static ExecutorService mExecutors;
public static void execute(Runnable runnable) {
if (mExecutors == null) {
mExecutors = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<>());
}
mExecutors.execute(runnable);
}
public static void runOnUIThread(Runnable runnable) {
mMainHandler.post(runnable);
}
}
@@ -0,0 +1,18 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.component;
import android.content.Context;
import android.widget.RelativeLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
public abstract class BaseUserView extends RelativeLayout {
public BaseUserView(Context context) {
super(context);
}
public void updateUserInfo(CallingUserModel userModel) {
}
public void updateTextColor(int color) {
}
}
@@ -0,0 +1,43 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.component;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
public class TUICallingSingleVideoUserView extends BaseUserView {
private Context mContext;
private ImageView mImageAvatar;
private TextView mTextUserName;
private TextView mTextVideoHint;
private String mHintMessage;
public TUICallingSingleVideoUserView(Context context, String message) {
super(context);
mContext = context.getApplicationContext();
mHintMessage = message;
initView();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_single_video_user_view, this);
mImageAvatar = findViewById(R.id.iv_user_avatar);
mTextUserName = findViewById(R.id.tv_user_name);
mTextVideoHint = findViewById(R.id.tv_video_tag);
mTextVideoHint.setText(mHintMessage);
}
@Override
public void updateUserInfo(CallingUserModel model) {
super.updateUserInfo(model);
if (null != model && !TextUtils.isEmpty(model.userId)) {
ImageLoader.loadImage(mContext, mImageAvatar, model.userAvatar, R.drawable.tuicalling_ic_avatar);
mTextUserName.setText(TextUtils.isEmpty(model.userName) ? model.userId : model.userName);
}
}
}
@@ -0,0 +1,42 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.component;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.TextView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
public class TUICallingUserView extends BaseUserView {
private Context mContext;
private ImageView mImageAvatar;
private TextView mTextUserName;
public TUICallingUserView(Context context) {
super(context);
mContext = context.getApplicationContext();
initView();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_user_view, this);
mImageAvatar = findViewById(R.id.img_avatar);
mTextUserName = findViewById(R.id.tv_name);
}
@Override
public void updateUserInfo(CallingUserModel model) {
super.updateUserInfo(model);
ImageLoader.loadImage(mContext, mImageAvatar, model.userAvatar, R.drawable.tuicalling_ic_avatar);
mTextUserName.setText(TextUtils.isEmpty(model.userName) ? model.userId : model.userName);
}
@Override
public void updateTextColor(int color) {
super.updateTextColor(color);
mTextUserName.setTextColor(color);
}
}
@@ -0,0 +1,171 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tencent.qcloud.tuikit.TUIVideoView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayoutEntity;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
import com.tencent.qcloud.tuikit.tuicallkit.view.UserLayoutFactory;
import java.util.Objects;
public class FloatCallView extends RelativeLayout {
private static final int UPDATE_COUNT = 3;
private static final int UPDATE_INTERVAL = 300;
private static final int MESSAGE_LAYOUT_EMPTY = 1;
private static final int MESSAGE_VIEW_EMPTY = 2;
private int mCount = 0;
private final Context mContext;
private RelativeLayout mLayoutVideoView;
private ImageView mImageAudio;
private TextView mTextHint;
private TextView mTextTime;
private ImageView mImageFloatAvatar;
private OnClickListener mOnClickListener;
private UserLayoutFactory mUserLayoutFactory;
private UserLayout mVideoLayout;
private String mCurrentUser;
private boolean mIsVideoAvailable;
public FloatCallView(Context context, UserLayoutFactory factory) {
super(context);
mContext = context;
mUserLayoutFactory = factory;
initView(context);
}
private void initView(Context context) {
LayoutInflater.from(context).inflate(R.layout.tuicalling_floatwindow_layout, this);
mLayoutVideoView = findViewById(R.id.rl_video_view);
mImageFloatAvatar = findViewById(R.id.img_float_avatar);
mTextHint = findViewById(R.id.tv_float_hint);
mImageAudio = findViewById(R.id.float_audioView);
mTextTime = findViewById(R.id.tv_float_time);
setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (null != mOnClickListener) {
mOnClickListener.onClick();
}
}
});
}
public void updateView(boolean isVideoAvailable, String userId) {
mIsVideoAvailable = isVideoAvailable;
if (!isVideoAvailable) {
mLayoutVideoView.setVisibility(GONE);
mImageFloatAvatar.setVisibility(GONE);
mImageAudio.setVisibility(VISIBLE);
return;
}
if (null == mUserLayoutFactory || TextUtils.isEmpty(userId)) {
return;
}
mCurrentUser = userId;
mImageAudio.setVisibility(GONE);
CallingUserModel model = new CallingUserModel();
for (UserLayoutEntity entity : mUserLayoutFactory.mLayoutEntityList) {
if (entity != null && Objects.equals(entity.userId, userId)) {
model = entity.userModel;
break;
}
}
if (model != null && !model.isVideoAvailable) {
mLayoutVideoView.removeAllViews();
mLayoutVideoView.setVisibility(GONE);
mImageFloatAvatar.setVisibility(VISIBLE);
ImageLoader.loadImage(mContext, mImageFloatAvatar, model.userAvatar, R.drawable.tuicalling_ic_avatar);
return;
}
mImageFloatAvatar.setVisibility(GONE);
mVideoLayout = mUserLayoutFactory.findUserLayout(userId);
if (null == mVideoLayout) {
mViewHandler.sendEmptyMessageDelayed(MESSAGE_LAYOUT_EMPTY, UPDATE_INTERVAL);
return;
}
reloadVideoView();
}
private void reloadVideoView() {
if (null == mVideoLayout) {
return;
}
TUIVideoView videoView = mVideoLayout.getVideoView();
if (videoView == null) {
mViewHandler.sendEmptyMessageDelayed(MESSAGE_VIEW_EMPTY, UPDATE_INTERVAL);
return;
}
if (null != videoView.getParent()) {
((ViewGroup) videoView.getParent()).removeView(videoView);
}
mLayoutVideoView.removeAllViews();
mLayoutVideoView.setVisibility(VISIBLE);
mLayoutVideoView.addView(videoView);
}
private final Handler mViewHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == MESSAGE_LAYOUT_EMPTY && null == mVideoLayout && mCount <= UPDATE_COUNT) {
mVideoLayout = mUserLayoutFactory.findUserLayout(mCurrentUser);
sendEmptyMessageDelayed(MESSAGE_LAYOUT_EMPTY, UPDATE_INTERVAL);
mCount++;
} else if (msg.what == MESSAGE_VIEW_EMPTY && mCount < UPDATE_COUNT) {
sendEmptyMessageDelayed(MESSAGE_VIEW_EMPTY, UPDATE_INTERVAL);
mCount++;
} else {
reloadVideoView();
mCount = 0;
}
}
};
public void enableCallingHint(boolean enable) {
mTextHint.setVisibility(enable ? VISIBLE : GONE);
}
public void updateCallTimeView(String time) {
if (mIsVideoAvailable) {
mTextTime.setVisibility(GONE);
return;
}
mTextTime.setText(time);
mTextTime.setVisibility(TextUtils.isEmpty(time) ? INVISIBLE : VISIBLE);
}
public void setOnClickListener(OnClickListener callback) {
mOnClickListener = callback;
}
public String getCurrentUser() {
return mCurrentUser;
}
public interface OnClickListener {
void onClick();
}
}
@@ -0,0 +1,202 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow;
import android.animation.ValueAnimator;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import com.tencent.qcloud.tuikit.tuicallkit.R;
public class FloatWindowService extends Service {
private static Intent mStartIntent;
private static FloatCallView mCallView;
private static Context mContext;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mWindowLayoutParams;
private int mScreenWidth;
private int mWidth;
private int mTouchStartX;
private int mTouchStartY;
private int mTouchCurrentX;
private int mTouchCurrentY;
private int mStartX;
private int mStartY;
private int mStopX;
private int mStopY;
private boolean mIsMove;
public static void startFloatService(Context context, FloatCallView callView) {
mContext = context;
mCallView = callView;
mStartIntent = new Intent(context, FloatWindowService.class);
context.startService(mStartIntent);
}
public static void stopService(Context context) {
if (null != mStartIntent) {
context.stopService(mStartIntent);
}
}
@Override
public void onCreate() {
super.onCreate();
initWindow();
}
@Override
public IBinder onBind(Intent intent) {
return new FloatBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
if (null != mCallView) {
mWindowManager.removeView(mCallView);
mCallView = null;
}
}
private void initWindow() {
mWindowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
mWindowLayoutParams = getViewParams();
mScreenWidth = mWindowManager.getDefaultDisplay().getWidth();
if (null != mCallView) {
mCallView.setBackgroundResource(R.drawable.tuicalling_bg_floatwindow_left);
mWindowManager.addView(mCallView, mWindowLayoutParams);
mCallView.setOnTouchListener(new FloatingListener());
}
}
private WindowManager.LayoutParams getViewParams() {
mWindowLayoutParams = new WindowManager.LayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
}
mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
mWindowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
mWindowLayoutParams.x = 0;
mWindowLayoutParams.y = mWindowManager.getDefaultDisplay().getHeight() / 2;
mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
mWindowLayoutParams.format = PixelFormat.TRANSPARENT;
return mWindowLayoutParams;
}
public class FloatBinder extends Binder {
public FloatWindowService getService() {
return FloatWindowService.this;
}
}
private class FloatingListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mIsMove = false;
mTouchStartX = (int) event.getRawX();
mTouchStartY = (int) event.getRawY();
mStartX = (int) event.getRawX();
mStartY = (int) event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
mTouchCurrentX = (int) event.getRawX();
mTouchCurrentY = (int) event.getRawY();
if (mWindowLayoutParams != null && null != mCallView) {
mWindowLayoutParams.x += mTouchCurrentX - mTouchStartX;
mWindowLayoutParams.y += mTouchCurrentY - mTouchStartY;
mWindowManager.updateViewLayout(mCallView, mWindowLayoutParams);
}
mTouchStartX = mTouchCurrentX;
mTouchStartY = mTouchCurrentY;
break;
case MotionEvent.ACTION_UP:
mStopX = (int) event.getRawX();
mStopY = (int) event.getRawY();
if (Math.abs(mStartX - mStopX) >= 5 || Math.abs(mStartY - mStopY) >= 5) {
mIsMove = true;
if (null != mCallView) {
mWidth = mCallView.getWidth();
if (mTouchCurrentX > (mScreenWidth / 2)) {
startScroll(mStopX, mScreenWidth - mWidth, false);
} else {
startScroll(mStopX, 0, true);
}
}
}
break;
default:
break;
}
return mIsMove;
}
}
private void startScroll(int start, int end, boolean isLeft) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end).setDuration(300);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mWindowLayoutParams == null || mCallView == null) {
return;
}
mWidth = mCallView.getWidth();
if (isLeft) {
mWindowLayoutParams.x = (int) (start * (1 - animation.getAnimatedFraction()));
mCallView.setBackgroundResource(R.drawable.tuicalling_bg_floatwindow_left);
} else {
float end = (mScreenWidth - start - mWidth) * animation.getAnimatedFraction();
mWindowLayoutParams.x = (int) (start + end);
mCallView.setBackgroundResource(R.drawable.tuicalling_bg_floatwindow_right);
}
calculateHeight();
mWindowManager.updateViewLayout(mCallView, mWindowLayoutParams);
}
});
valueAnimator.start();
}
private void calculateHeight() {
if (mWindowLayoutParams == null) {
return;
}
int height = mCallView.getHeight();
int screenHeight = mWindowManager.getDefaultDisplay().getHeight();
int resourceId = mContext.getResources().getIdentifier("status_bar_height",
"dimen", "android");
int statusBarHeight = mContext.getResources().getDimensionPixelSize(resourceId);
if (mWindowLayoutParams.y < 0) {
mWindowLayoutParams.y = 0;
} else if (mWindowLayoutParams.y > (screenHeight - height - statusBarHeight)) {
mWindowLayoutParams.y = screenHeight - height - statusBarHeight;
}
}
}
@@ -0,0 +1,71 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.floatwindow;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
public class HomeWatcher {
private Context mContext;
private IntentFilter mFilter;
private OnHomePressedListener mListener;
private InnerReceiver mReceiver;
private boolean mIsRegisted;
public HomeWatcher(Context context) {
mContext = context.getApplicationContext();
mFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mReceiver = new InnerReceiver();
}
public void setOnHomePressedListener(OnHomePressedListener listener) {
mListener = listener;
}
public void startWatch() {
if (null != mReceiver && !mIsRegisted) {
mContext.registerReceiver(mReceiver, mFilter);
mIsRegisted = true;
}
}
public void stopWatch() {
try {
if (null != mReceiver) {
mIsRegisted = false;
mContext.unregisterReceiver(mReceiver);
}
} catch (Exception e) {
e.printStackTrace();
}
}
class InnerReceiver extends BroadcastReceiver {
private static final String SYSTEM_DIALOG_REASON_KEY = "reason";
private static final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
private static final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps";
private static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null && action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) {
String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY);
if (mListener != null) {
if (SYSTEM_DIALOG_REASON_HOME_KEY.equals(reason)) {
mListener.onHomePressed();
} else if (SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
mListener.onRecentAppsPressed();
}
}
}
}
}
public interface OnHomePressedListener {
void onHomePressed();
void onRecentAppsPressed();
}
}
@@ -0,0 +1,73 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.function;
import android.content.Context;
import android.widget.RelativeLayout;
import com.tencent.qcloud.tuicore.TUICore;
import com.tencent.qcloud.tuicore.interfaces.ITUINotification;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallkit.base.Constants;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingAction;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayout;
import java.util.Map;
public abstract class BaseFunctionView extends RelativeLayout {
protected Context mContext;
protected TUICallingAction mCallingAction;
protected UserLayout mLocalUserLayout;
public BaseFunctionView(Context context) {
super(context);
mContext = context.getApplicationContext();
mCallingAction = new TUICallingAction(context);
registerEvent();
}
private void registerEvent() {
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_CAMERA_OPEN, mNotification);
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_MIC_STATUS_CHANGED,
mNotification);
TUICore.registerEvent(Constants.EVENT_TUICALLING_CHANGED, Constants.EVENT_SUB_AUDIOPLAYDEVICE_CHANGED,
mNotification);
}
private final ITUINotification mNotification = new ITUINotification() {
@Override
public void onNotifyEvent(String key, String subKey, Map<String, Object> param) {
if (Constants.EVENT_TUICALLING_CHANGED.equals(key) && param != null) {
switch (subKey) {
case Constants.EVENT_SUB_CAMERA_OPEN:
updateCameraOpenStatus((boolean) param.get(Constants.OPEN_CAMERA));
break;
case Constants.EVENT_SUB_MIC_STATUS_CHANGED:
updateMicMuteStatus((Boolean) param.get(Constants.MUTE_MIC));
break;
case Constants.EVENT_SUB_AUDIOPLAYDEVICE_CHANGED:
TUICommonDefine.AudioPlaybackDevice device =
(TUICommonDefine.AudioPlaybackDevice) param.get(Constants.HANDS_FREE);
updateAudioPlayDevice(TUICommonDefine.AudioPlaybackDevice.Speakerphone.equals(device));
break;
default:
break;
}
}
}
};
public void updateCameraOpenStatus(boolean isOpen) {
}
public void updateMicMuteStatus(boolean isMicMute) {
}
public void updateAudioPlayDevice(boolean isSpeaker) {
}
public void updateTextColor(int color) {
}
public void setLocalUserLayout(UserLayout layout) {
mLocalUserLayout = layout;
}
}
@@ -0,0 +1,94 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.function;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.tencent.qcloud.tuicore.util.ToastUtil;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
public class TUICallingAudioFunctionView extends BaseFunctionView {
private LinearLayout mLayoutMute;
private LinearLayout mLayoutHangup;
private LinearLayout mLayoutHandsFree;
private ImageView mImageMute;
private ImageView mImageHandsFree;
public TUICallingAudioFunctionView(Context context) {
super(context);
initView();
initListener();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_funcation_view_audio, this);
mLayoutMute = findViewById(R.id.ll_mute);
mImageMute = findViewById(R.id.img_mute);
mLayoutHangup = findViewById(R.id.ll_hangup);
mLayoutHandsFree = findViewById(R.id.ll_handsfree);
mImageHandsFree = findViewById(R.id.img_handsfree);
}
private void initListener() {
mLayoutMute.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean isMicMute = TUICallingStatusManager.sharedInstance(mContext).isMicMute();
if (isMicMute) {
mCallingAction.openMicrophone(new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
} else {
mCallingAction.closeMicrophone();
}
int resId = isMicMute ? R.string.tuicalling_toast_disable_mute : R.string.tuicalling_toast_enable_mute;
ToastUtil.toastShortMessage(mContext.getString(resId));
}
});
mLayoutHangup.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallingAction.hangup(null);
}
});
mLayoutHandsFree.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean isSpeaker = TUICommonDefine.AudioPlaybackDevice.Speakerphone
.equals(TUICallingStatusManager.sharedInstance(mContext).getAudioPlaybackDevice());
if (isSpeaker) {
mCallingAction.selectAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice.Earpiece);
} else {
mCallingAction.selectAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice.Speakerphone);
}
int resId = isSpeaker ? R.string.tuicalling_toast_use_handset : R.string.tuicalling_toast_speaker;
ToastUtil.toastShortMessage(mContext.getString(resId));
}
});
}
@Override
public void updateMicMuteStatus(boolean isMicMute) {
super.updateMicMuteStatus(isMicMute);
mImageMute.setActivated(isMicMute);
}
@Override
public void updateAudioPlayDevice(boolean isSpeaker) {
super.updateAudioPlayDevice(isSpeaker);
mImageHandsFree.setActivated(isSpeaker);
}
}
@@ -0,0 +1,33 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.function;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingAction;
public class TUICallingSwitchAudioView extends RelativeLayout {
private final Context mContext;
public TUICallingSwitchAudioView(Context context) {
super(context);
mContext = context.getApplicationContext();
initView();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_switch_audio_view, this);
LinearLayout layoutSwitchToAudio = findViewById(R.id.ll_switch_audio);
layoutSwitchToAudio.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TUICallingAction callingAction = new TUICallingAction(mContext);
callingAction.switchCallMediaType(TUICallDefine.MediaType.Audio);
}
});
}
}
@@ -0,0 +1,131 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.function;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.tencent.qcloud.tuicore.util.ToastUtil;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
public class TUICallingVideoFunctionView extends BaseFunctionView {
private LinearLayout mLayoutOpenCamera;
private LinearLayout mLayoutMute;
private LinearLayout mLayoutHandsFree;
private LinearLayout mLayoutHangup;
private ImageView mImageOpenCamera;
private ImageView mImageMute;
private ImageView mImageHandsFree;
private ImageView mImageSwitchCamera;
public TUICallingVideoFunctionView(Context context) {
super(context);
initView();
initClickListener();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_funcation_view_video, this);
mLayoutMute = (LinearLayout) findViewById(R.id.ll_mute);
mImageMute = (ImageView) findViewById(R.id.iv_mute);
mLayoutHandsFree = (LinearLayout) findViewById(R.id.ll_handsfree);
mImageHandsFree = (ImageView) findViewById(R.id.iv_handsfree);
mLayoutOpenCamera = (LinearLayout) findViewById(R.id.ll_open_camera);
mImageOpenCamera = (ImageView) findViewById(R.id.img_camera);
mLayoutHangup = (LinearLayout) findViewById(R.id.ll_hangup);
mImageSwitchCamera = (ImageView) findViewById(R.id.switch_camera);
}
private void initClickListener() {
mLayoutMute.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean isMicMute = TUICallingStatusManager.sharedInstance(mContext).isMicMute();
if (isMicMute) {
mCallingAction.openMicrophone(new TUICommonDefine.Callback() {
@Override
public void onSuccess() {
}
@Override
public void onError(int errCode, String errMsg) {
}
});
} else {
mCallingAction.closeMicrophone();
}
int resId = isMicMute ? R.string.tuicalling_toast_disable_mute : R.string.tuicalling_toast_enable_mute;
ToastUtil.toastShortMessage(mContext.getString(resId));
}
});
mLayoutHandsFree.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
boolean isSpeaker = TUICommonDefine.AudioPlaybackDevice.Speakerphone
.equals(TUICallingStatusManager.sharedInstance(mContext).getAudioPlaybackDevice());
if (isSpeaker) {
mCallingAction.selectAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice.Earpiece);
} else {
mCallingAction.selectAudioPlaybackDevice(TUICommonDefine.AudioPlaybackDevice.Speakerphone);
}
int resId = isSpeaker ? R.string.tuicalling_toast_use_handset : R.string.tuicalling_toast_speaker;
ToastUtil.toastShortMessage(mContext.getString(resId));
}
});
mLayoutOpenCamera.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (TUICallingStatusManager.sharedInstance(mContext).isCameraOpen()) {
mCallingAction.closeCamera();
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_toast_disable_camera));
} else {
if (null != mLocalUserLayout) {
mCallingAction.openCamera(TUICallingStatusManager.sharedInstance(mContext).getFrontCamera(),
mLocalUserLayout.getVideoView(), null);
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_toast_enable_camera));
}
}
}
});
mImageSwitchCamera.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TUICommonDefine.Camera camera = TUICallingStatusManager.sharedInstance(mContext).getFrontCamera();
mCallingAction.switchCamera(TUICommonDefine.Camera.Front.equals(camera)
? TUICommonDefine.Camera.Back : TUICommonDefine.Camera.Front);
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_toast_switch_camera));
}
});
mLayoutHangup.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallingAction.hangup(null);
}
});
}
@Override
public void updateCameraOpenStatus(boolean isOpen) {
super.updateCameraOpenStatus(isOpen);
mImageOpenCamera.setActivated(isOpen);
}
@Override
public void updateMicMuteStatus(boolean isMicMute) {
super.updateMicMuteStatus(isMicMute);
mImageMute.setActivated(isMicMute);
}
@Override
public void updateAudioPlayDevice(boolean isSpeaker) {
super.updateAudioPlayDevice(isSpeaker);
mImageHandsFree.setActivated(isSpeaker);
}
}
@@ -0,0 +1,48 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.function;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.tencent.qcloud.tuicore.util.ToastUtil;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
public class TUICallingVideoInviteFunctionView extends BaseFunctionView {
private LinearLayout mLayoutCancel;
private ImageView mImageSwitchCamera;
public TUICallingVideoInviteFunctionView(Context context) {
super(context);
initView();
initClickListener();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_funcation_view_video_inviting, this);
mLayoutCancel = findViewById(R.id.ll_cancel);
mImageSwitchCamera = findViewById(R.id.img_switch_camera);
}
private void initClickListener() {
mLayoutCancel.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallingAction.hangup(null);
}
});
mImageSwitchCamera.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
TUICommonDefine.Camera camera = TUICallingStatusManager.sharedInstance(mContext).getFrontCamera();
mCallingAction.switchCamera(TUICommonDefine.Camera.Front.equals(camera)
? TUICommonDefine.Camera.Back : TUICommonDefine.Camera.Front);
ToastUtil.toastShortMessage(mContext.getString(R.string.tuicalling_toast_switch_camera));
}
});
}
}
@@ -0,0 +1,52 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.function;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
public class TUICallingWaitFunctionView extends BaseFunctionView {
private LinearLayout mLayoutReject;
private LinearLayout mLayoutDialing;
private TextView mTextReject;
private TextView mTextDialing;
public TUICallingWaitFunctionView(Context context) {
super(context);
initView();
initListener();
}
private void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_funcation_view_audio_waiting, this);
mLayoutReject = findViewById(R.id.ll_decline);
mLayoutDialing = findViewById(R.id.ll_answer);
mTextReject = findViewById(R.id.tv_reject);
mTextDialing = findViewById(R.id.tv_dialing);
}
private void initListener() {
mLayoutReject.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallingAction.reject(null);
}
});
mLayoutDialing.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mCallingAction.accept(null);
}
});
}
@Override
public void updateTextColor(int color) {
mTextReject.setTextColor(color);
mTextDialing.setTextColor(color);
}
}
@@ -0,0 +1,77 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.root;
import android.content.Context;
import android.view.View;
import android.widget.RelativeLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingAction;
public abstract class BaseCallView extends RelativeLayout {
protected Context mContext;
protected TUICallingAction mCallingAction;
public BaseCallView(Context context) {
super(context);
mContext = context;
mCallingAction = new TUICallingAction(context);
}
public void userEnter(CallingUserModel userModel) {
}
public void userAdd(CallingUserModel userModel) {
}
public void userLeave(CallingUserModel userModel) {
}
public void updateUserInfo(CallingUserModel userModel) {
}
public void updateCallingHint(String hint) {
}
public void updateCallTimeView(String time) {
}
public void updateUserView(View view) {
}
public void updateFunctionView(View view) {
}
public void updateSwitchAudioView(View view) {
}
public void addOtherUserView(View view) {
}
public void enableFloatView(View view) {
}
public void enableAddUserView(View view) {
}
public void updateBackgroundColor(int color) {
}
public void updateTextColor(int color) {
}
public void finish() {
clearAllViews();
removeAllViews();
detachAllViewsFromParent();
onDetachedFromWindow();
}
public void clearAllViews() {
updateCallingHint("");
updateCallTimeView(null);
updateUserView(null);
updateFunctionView(null);
updateSwitchAudioView(null);
addOtherUserView(null);
}
}
@@ -0,0 +1,318 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.root;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallengine.TUICallDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.Constants;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayoutEntity;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DeviceUtils;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DisplayUtils;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
import com.tencent.qcloud.tuikit.tuicallkit.view.UserLayoutFactory;
import java.util.ArrayList;
import java.util.Iterator;
public class TUICallingGroupView extends BaseCallView {
private TextView mTextCallHint;
private RelativeLayout mLayoutFunction;
private TextView mTextTime;
private UserLayoutFactory mUserLayoutFactory;
private View mRootView;
private RelativeLayout mLayoutFloatView;
private RelativeLayout mLayoutAddUserView;
private RelativeLayout mLayoutGroupManager;
private int mCount = 0;
private boolean mInitParam = false;
private ArrayList<LayoutParams> mGrid1ParamList;
private ArrayList<LayoutParams> mGrid2ParamList;
private ArrayList<LayoutParams> mGrid3ParamList;
private ArrayList<LayoutParams> mGrid4ParamList;
private ArrayList<LayoutParams> mGrid9ParamList;
public TUICallingGroupView(Context context, UserLayoutFactory factory) {
super(context);
mUserLayoutFactory = factory;
initView();
initData();
}
public void initView() {
mRootView = LayoutInflater.from(mContext).inflate(R.layout.tuicalling_background_group_view, this);
mLayoutGroupManager = findViewById(R.id.group_layout_manager);
mLayoutFloatView = findViewById(R.id.rl_float_view);
mLayoutAddUserView = findViewById(R.id.rl_add_user_view);
mTextCallHint = findViewById(R.id.tv_group_call_hint);
mTextTime = findViewById(R.id.tv_group_time);
mLayoutFunction = findViewById(R.id.rl_group_function);
}
private void initData() {
for (UserLayoutEntity entity : mUserLayoutFactory.mLayoutEntityList) {
if (null == entity || TextUtils.isEmpty(entity.userId)) {
continue;
}
UserLayout layout = allocUserLayout(entity.userModel);
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
if (entity.userId.equals(TUILogin.getLoginUser())) {
if (TUICallDefine.MediaType.Video.equals(mediaType)) {
layout.setVideoAvailable(true);
TUICommonDefine.Camera camera = TUICallingStatusManager.sharedInstance(mContext).getFrontCamera();
mCallingAction.openCamera(camera, layout.getVideoView(), null);
} else {
ImageLoader.loadImage(mContext, entity.layout.getAvatarImage(), TUILogin.getFaceUrl(),
R.drawable.tuicalling_ic_avatar);
entity.layout.setUserName(TUILogin.getNickName());
}
} else {
if (null != entity.userModel) {
if (TUICallDefine.MediaType.Video.equals(mediaType) && entity.userModel.isVideoAvailable) {
layout.setVideoAvailable(true);
mCallingAction.startRemoteView(entity.userId, layout.getVideoView(), null);
}
ImageLoader.loadImage(mContext, entity.layout.getAvatarImage(), entity.userModel.userAvatar,
R.drawable.tuicalling_ic_avatar);
entity.layout.setUserName(entity.userModel.userName);
if (entity.userModel.isEnter) {
entity.layout.stopLoading();
} else {
entity.layout.startLoading();
}
}
}
}
}
@Override
public void userEnter(CallingUserModel userModel) {
super.userEnter(userModel);
UserLayout layout = mUserLayoutFactory.findUserLayout(userModel.userId);
if (null == layout) {
layout = allocUserLayout(userModel);
}
if (layout == null) {
return;
}
layout.stopLoading();
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
layout.setVideoAvailable(TUICallDefine.MediaType.Video.equals(mediaType));
if (TUICallDefine.MediaType.Video.equals(mediaType)) {
mCallingAction.startRemoteView(userModel.userId, layout.getVideoView(), null);
} else {
ImageLoader.loadImage(mContext, layout.getAvatarImage(), userModel.userAvatar,
R.drawable.tuicalling_ic_avatar);
}
}
@Override
public void userAdd(CallingUserModel userModel) {
super.userAdd(userModel);
UserLayout layout = mUserLayoutFactory.findUserLayout(userModel.userId);
if (null == layout) {
layout = allocUserLayout(userModel);
}
if (layout == null) {
return;
}
layout.startLoading();
TUICallDefine.MediaType mediaType = TUICallingStatusManager.sharedInstance(mContext).getMediaType();
layout.setVideoAvailable(TUICallDefine.MediaType.Video.equals(mediaType));
if (TUICallDefine.MediaType.Video.equals(mediaType)) {
mCallingAction.startRemoteView(userModel.userId, layout.getVideoView(), null);
} else {
ImageLoader.loadImage(mContext, layout.getAvatarImage(), userModel.userAvatar,
R.drawable.tuicalling_ic_avatar);
}
}
@Override
public void userLeave(CallingUserModel userModel) {
super.userLeave(userModel);
if (null != userModel) {
recyclerUserLayout(userModel.userId);
mCallingAction.stopRemoteView(userModel.userId);
}
}
@Override
public void updateUserInfo(CallingUserModel userModel) {
super.updateUserInfo(userModel);
UserLayout userLayout = mUserLayoutFactory.findUserLayout(userModel.userId);
if (null != userLayout) {
userLayout.setVideoAvailable(userModel.isVideoAvailable);
ImageLoader.loadImage(mContext, userLayout.getAvatarImage(), userModel.userAvatar,
R.drawable.tuicalling_ic_avatar);
}
}
@Override
public void updateCallingHint(String hint) {
super.updateCallingHint(hint);
mTextCallHint.setText(hint);
mTextCallHint.setVisibility(TextUtils.isEmpty(hint) ? GONE : VISIBLE);
}
@Override
public void updateCallTimeView(String time) {
mTextTime.setText(time);
mTextTime.setVisibility(TextUtils.isEmpty(time) ? GONE : VISIBLE);
}
@Override
public void updateFunctionView(View view) {
mLayoutFunction.removeAllViews();
if (null != view) {
mLayoutFunction.addView(view);
}
}
@Override
public void updateBackgroundColor(int color) {
mRootView.setBackgroundColor(color);
}
@Override
public void updateTextColor(int color) {
super.updateTextColor(color);
mTextCallHint.setTextColor(color);
mTextTime.setTextColor(color);
}
@Override
public void enableFloatView(View view) {
mLayoutFloatView.removeAllViews();
if (null != view) {
mLayoutFloatView.addView(view);
}
}
@Override
public void enableAddUserView(View view) {
mLayoutAddUserView.removeAllViews();
if (null != view) {
mLayoutAddUserView.addView(view);
}
}
@Override
public void finish() {
if (null != mLayoutGroupManager) {
recyclerAllUserLayout();
}
super.finish();
}
private UserLayout allocUserLayout(CallingUserModel userModel) {
if (null == userModel || TextUtils.isEmpty(userModel.userId)) {
return null;
}
if (mCount > Constants.MAX_USER) {
return null;
}
UserLayout userLayout = mUserLayoutFactory.allocUserLayout(userModel);
if (null != userLayout.getParent()) {
((ViewGroup) userLayout.getParent()).removeView(userLayout);
}
mLayoutGroupManager.addView(userLayout);
mCount++;
post(new Runnable() {
@Override
public void run() {
makeGirdLayout(true);
}
});
return userLayout;
}
private void recyclerUserLayout(String userId) {
if (TextUtils.isEmpty(userId)) {
return;
}
Iterator iterator = mUserLayoutFactory.mLayoutEntityList.iterator();
while (iterator.hasNext()) {
UserLayoutEntity item = (UserLayoutEntity) iterator.next();
if (userId.equals(item.userId)) {
mLayoutGroupManager.removeView(item.layout);
iterator.remove();
mCount--;
break;
}
}
post(new Runnable() {
@Override
public void run() {
makeGirdLayout(true);
}
});
}
private void recyclerAllUserLayout() {
if (null == mUserLayoutFactory) {
return;
}
Iterator iterator = mUserLayoutFactory.mLayoutEntityList.iterator();
while (iterator.hasNext()) {
UserLayoutEntity item = (UserLayoutEntity) iterator.next();
mLayoutGroupManager.removeView(item.layout);
iterator.remove();
}
mCount = 0;
}
private void makeGirdLayout(boolean needUpdate) {
if (!mInitParam) {
int width = DeviceUtils.getScreenWidth(mContext);
int height = DeviceUtils.getScreenHeight(mContext);
int size = Math.min(width, height);
mGrid1ParamList = DisplayUtils.initGrid1Param(getContext(), size, size);
mGrid2ParamList = DisplayUtils.initGrid2Param(getContext(), size, size);
mGrid3ParamList = DisplayUtils.initGrid3Param(getContext(), size, size);
mGrid4ParamList = DisplayUtils.initGrid4Param(getContext(), size, size);
mGrid9ParamList = DisplayUtils.initGrid9Param(getContext(), size, size);
mInitParam = true;
}
if (null == mUserLayoutFactory || mUserLayoutFactory.mLayoutEntityList.isEmpty()) {
return;
}
if (needUpdate) {
ArrayList<LayoutParams> paramList;
if (mCount <= 1) {
paramList = mGrid1ParamList;
UserLayoutEntity entity = mUserLayoutFactory.mLayoutEntityList.get(0);
entity.layout.setLayoutParams(paramList.get(0));
return;
} else if (mCount == 2) {
paramList = mGrid2ParamList;
} else if (mCount == 3) {
paramList = mGrid3ParamList;
} else if (mCount == 4) {
paramList = mGrid4ParamList;
} else {
paramList = mGrid9ParamList;
}
int layoutIndex = TextUtils.isEmpty(TUILogin.getLoginUser()) ? 0 : 1;
for (int i = 0; i < mUserLayoutFactory.mLayoutEntityList.size(); i++) {
UserLayoutEntity entity = mUserLayoutFactory.mLayoutEntityList.get(i);
if (entity.userId.equals(TUILogin.getLoginUser())) {
entity.layout.setLayoutParams(paramList.get(0));
} else if (layoutIndex < paramList.size()) {
entity.layout.setLayoutParams(paramList.get(layoutIndex++));
}
}
}
}
}
@@ -0,0 +1,107 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.root;
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tencent.qcloud.tuikit.tuicallkit.R;
public class TUICallingImageView extends BaseCallView {
private Context mContext;
private RelativeLayout mLayoutUserView;
private LinearLayout mLayoutOtherUserContainer;
private RelativeLayout mLayoutFunction;
private TextView mTextTime;
private TextView mTextCallHint;
private View mRootView;
private RelativeLayout mLayoutFloatView;
private RelativeLayout mLayoutAddUserView;
public TUICallingImageView(Context context) {
super(context);
mContext = context;
initView();
}
public void initView() {
mRootView = LayoutInflater.from(mContext).inflate(R.layout.tuicalling_background_image_view, this);
mLayoutFloatView = findViewById(R.id.rl_float_view);
mLayoutAddUserView = findViewById(R.id.rl_add_user_view);
mLayoutUserView = findViewById(R.id.rl_single_audio_view);
mTextCallHint = findViewById(R.id.tv_call_hint);
mLayoutOtherUserContainer = findViewById(R.id.ll_other_user_container);
mTextTime = findViewById(R.id.tv_image_time);
mLayoutFunction = findViewById(R.id.rl_image_function);
}
@Override
public void updateUserView(View view) {
super.updateUserView(view);
mLayoutUserView.removeAllViews();
if (null != view) {
mLayoutUserView.addView(view);
}
}
@Override
public void addOtherUserView(View view) {
super.addOtherUserView(view);
mLayoutOtherUserContainer.removeAllViews();
if (null != view) {
mLayoutOtherUserContainer.addView(view);
}
}
@Override
public void updateCallingHint(String hint) {
super.updateCallingHint(hint);
mTextCallHint.setText(hint);
mTextCallHint.setVisibility(TextUtils.isEmpty(hint) ? GONE : VISIBLE);
}
@Override
public void updateCallTimeView(String time) {
mTextTime.setText(time);
mTextTime.setVisibility(TextUtils.isEmpty(time) ? GONE : VISIBLE);
}
@Override
public void updateFunctionView(View view) {
mLayoutFunction.removeAllViews();
if (null != view) {
mLayoutFunction.addView(view);
}
}
@Override
public void updateBackgroundColor(int color) {
mRootView.setBackgroundColor(color);
}
@Override
public void updateTextColor(int color) {
super.updateTextColor(color);
mTextCallHint.setTextColor(color);
mTextTime.setTextColor(color);
}
@Override
public void enableFloatView(View view) {
mLayoutFloatView.removeAllViews();
if (null != view) {
mLayoutFloatView.addView(view);
}
}
@Override
public void enableAddUserView(View view) {
mLayoutAddUserView.removeAllViews();
if (null != view) {
mLayoutAddUserView.addView(view);
}
}
}
@@ -0,0 +1,283 @@
package com.tencent.qcloud.tuikit.tuicallkit.view.root;
import android.content.Context;
import android.text.TextUtils;
import android.view.GestureDetector;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.tencent.qcloud.tuicore.TUILogin;
import com.tencent.qcloud.tuikit.TUICommonDefine;
import com.tencent.qcloud.tuikit.tuicallkit.R;
import com.tencent.qcloud.tuikit.tuicallkit.base.CallingUserModel;
import com.tencent.qcloud.tuikit.tuicallkit.base.TUICallingStatusManager;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayout;
import com.tencent.qcloud.tuikit.tuicallkit.base.UserLayoutEntity;
import com.tencent.qcloud.tuikit.tuicallkit.utils.DisplayUtils;
import com.tencent.qcloud.tuikit.tuicallkit.utils.ImageLoader;
import com.tencent.qcloud.tuikit.tuicallkit.view.UserLayoutFactory;
import com.tencent.qcloud.tuikit.tuicallkit.view.common.RoundCornerImageView;
import java.util.ArrayList;
import java.util.Iterator;
public class TUICallingSingleView extends BaseCallView {
private Context mContext;
private UserLayoutFactory mUserLayoutFactory;
private TextView mTextTime;
private RelativeLayout mLayoutUserWaitView;
private RelativeLayout mLayoutSwitchAudio;
private RelativeLayout mLayoutFunction;
private RelativeLayout mLayoutFloatView;
private RelativeLayout mLayoutUserContainer;
private ArrayList<LayoutParams> mFloatParamList;
private int mCount = 0;
public TUICallingSingleView(Context context, UserLayoutFactory factory) {
super(context);
mContext = context.getApplicationContext();
mUserLayoutFactory = factory;
initView();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initData();
}
public void initView() {
LayoutInflater.from(mContext).inflate(R.layout.tuicalling_background_single_view, this);
mLayoutUserContainer = findViewById(R.id.rl_video_container);
mLayoutFloatView = findViewById(R.id.rl_float_view);
mLayoutUserWaitView = findViewById(R.id.rl_single_video_user);
mLayoutSwitchAudio = findViewById(R.id.rl_switch_audio);
mTextTime = findViewById(R.id.tv_single_time);
mLayoutFunction = findViewById(R.id.rl_single_function);
}
private void initData() {
for (UserLayoutEntity entity : mUserLayoutFactory.mLayoutEntityList) {
if (null != entity && !TextUtils.isEmpty(entity.userId) && entity.userId.equals(TUILogin.getLoginUser())) {
UserLayout layout = allocUserLayout(entity.userModel);
if (layout == null) {
continue;
}
layout.setVideoAvailable(true);
if (!TUICallingStatusManager.sharedInstance(mContext).isCameraOpen()) {
TUICommonDefine.Camera camera = TUICallingStatusManager.sharedInstance(mContext).getFrontCamera();
mCallingAction.openCamera(camera, layout.getVideoView(), null);
}
}
}
}
@Override
public void userEnter(CallingUserModel userModel) {
super.userEnter(userModel);
UserLayout layout = findUserLayout(userModel.userId);
if (null == layout) {
layout = allocUserLayout(userModel);
}
layout.setVideoAvailable(userModel.isVideoAvailable);
mCallingAction.startRemoteView(userModel.userId, layout.getVideoView(), null);
}
public void updateUserInfo(CallingUserModel userModel) {
super.updateUserInfo(userModel);
UserLayout layout = findUserLayout(userModel.userId);
if (layout != null) {
layout.setVideoAvailable(userModel.isVideoAvailable);
ImageLoader.loadImage(mContext, layout.getAvatarImage(), userModel.userAvatar,
R.drawable.tuicalling_ic_avatar);
}
}
@Override
public void updateUserView(View view) {
super.updateUserView(view);
mLayoutUserWaitView.removeAllViews();
if (null != view) {
mLayoutUserWaitView.addView(view);
}
}
@Override
public void updateSwitchAudioView(View view) {
mLayoutSwitchAudio.removeAllViews();
if (null != view) {
mLayoutSwitchAudio.addView(view);
}
}
@Override
public void updateCallTimeView(String time) {
mTextTime.setText(time);
mTextTime.setVisibility(TextUtils.isEmpty(time) ? GONE : VISIBLE);
}
@Override
public void updateTextColor(int color) {
super.updateTextColor(color);
mTextTime.setTextColor(color);
}
@Override
public void updateFunctionView(View view) {
mLayoutFunction.removeAllViews();
if (null != view) {
mLayoutFunction.addView(view);
}
}
@Override
public void enableFloatView(View view) {
mLayoutFloatView.removeAllViews();
if (null != view) {
mLayoutFloatView.addView(view);
}
}
public void finish() {
recyclerAllUserLayout();
super.finish();
}
private UserLayout findUserLayout(String userId) {
if (TextUtils.isEmpty(userId)) {
return null;
}
return (null == mUserLayoutFactory) ? null : mUserLayoutFactory.findUserLayout(userId);
}
private UserLayout allocUserLayout(CallingUserModel userModel) {
if (null == userModel || TextUtils.isEmpty(userModel.userId)) {
return null;
}
UserLayout userLayout = mUserLayoutFactory.allocUserLayout(userModel);
if (null != userLayout.getParent()) {
((ViewGroup) userLayout.getParent()).removeView(userLayout);
}
initGestureListener(userLayout);
userLayout.setVisibility(VISIBLE);
userLayout.disableAudioImage(true);
RelativeLayout.LayoutParams lp = new LayoutParams(180, 180);
lp.addRule(CENTER_IN_PARENT);
((RoundCornerImageView) userLayout.getAvatarImage()).setRadius(15);
userLayout.getAvatarImage().setLayoutParams(lp);
mLayoutUserContainer.addView(userLayout);
mCount++;
this.post(new Runnable() {
@Override
public void run() {
makeFloatLayout();
}
});
return userLayout;
}
private void recyclerAllUserLayout() {
if (null == mUserLayoutFactory || null == mLayoutUserContainer) {
return;
}
Iterator iterator = mUserLayoutFactory.mLayoutEntityList.iterator();
while (iterator.hasNext()) {
UserLayoutEntity item = (UserLayoutEntity) iterator.next();
mLayoutUserContainer.removeView(item.layout);
iterator.remove();
}
mCount = 0;
}
private void initGestureListener(UserLayout layout) {
GestureDetector detector = new GestureDetector(getContext(), new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(MotionEvent e) {
layout.performClick();
return false;
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (!layout.isMoveAble()) {
return false;
}
ViewGroup.LayoutParams params = layout.getLayoutParams();
if (params instanceof LayoutParams) {
LayoutParams layoutParams = (LayoutParams) layout.getLayoutParams();
int newX = (int) (layoutParams.leftMargin + (e2.getX() - e1.getX()));
int newY = (int) (layoutParams.topMargin + (e2.getY() - e1.getY()));
if (newX >= 0 && newX <= (getWidth() - layout.getWidth())
&& newY >= 0 && newY <= (getHeight() - layout.getHeight())) {
layoutParams.leftMargin = newX;
layoutParams.topMargin = newY;
layout.setLayoutParams(layoutParams);
}
}
return true;
}
});
layout.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return detector.onTouchEvent(event);
}
});
}
private UserLayoutEntity findEntity(String userId) {
for (UserLayoutEntity entity : mUserLayoutFactory.mLayoutEntityList) {
if (entity.userId.equals(userId)) {
return entity;
}
}
return null;
}
private void makeFloatLayout() {
if (null == mFloatParamList || mFloatParamList.size() == 0) {
mFloatParamList = DisplayUtils.initFloatParamList(getContext(), getWidth(), getHeight());
}
int size = mUserLayoutFactory.mLayoutEntityList.size();
for (int i = 0; i < size; i++) {
UserLayoutEntity entity = mUserLayoutFactory.mLayoutEntityList.get(size - i - 1);
LayoutParams layoutParams = mFloatParamList.get(i);
entity.layout.setLayoutParams(layoutParams);
entity.layout.setMoveAble(i != 0);
addFloatViewClickListener(entity);
entity.layout.bringToFront();
}
}
private void addFloatViewClickListener(UserLayoutEntity entity) {
final String userId = entity.userId;
entity.layout.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (!TextUtils.isEmpty(userId)) {
makeFullVideoView(userId);
}
}
});
}
private void makeFullVideoView(String userId) {
UserLayoutEntity entity = findEntity(userId);
mUserLayoutFactory.mLayoutEntityList.remove(entity);
mUserLayoutFactory.mLayoutEntityList.addLast(entity);
makeFloatLayout();
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 654 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 571 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 811 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tuicalling_ic_camera_disable" android:state_activated="false" android:state_enabled="true" />
<item android:drawable="@drawable/tuicalling_ic_camera_enable" android:state_activated="true" android:state_enabled="true" />
</selector>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tuicalling_ic_dialing_pressed" android:state_pressed="true" android:state_enabled="true" />
<item android:drawable="@drawable/tuicalling_ic_dialing" android:state_pressed="false" android:state_enabled="true" />
</selector>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tuicalling_color_white" />
<corners android:radius="15dp" />
</shape>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tuicalling_color_bg_float_view" />
<corners
android:bottomRightRadius="15dp"
android:topRightRadius="15dp" />
</shape>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/tuicalling_color_bg_float_view" />
<corners
android:bottomLeftRadius="15dp"
android:topLeftRadius="15dp" />
</shape>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tuicalling_ic_handsfree_enable" android:state_activated="true" android:state_enabled="true" />
<item android:drawable="@drawable/tuicalling_ic_handsfree_disable" android:state_activated="false" android:state_enabled="true" />
</selector>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/tuicalling_ic_hangup_pressed" android:state_enabled="true" android:state_pressed="true" />
<item android:drawable="@drawable/tuicalling_ic_hangup" android:state_enabled="true" android:state_pressed="false" />
</selector>

Some files were not shown because too many files have changed in this diff Show More