添加项目
@@ -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'
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 696 B |
|
After Width: | Height: | Size: 749 B |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 698 B |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 9.3 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 642 B |
|
After Width: | Height: | Size: 654 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 647 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 630 B |
|
After Width: | Height: | Size: 537 B |
|
After Width: | Height: | Size: 489 B |
|
After Width: | Height: | Size: 571 B |
|
After Width: | Height: | Size: 811 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
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>
|
||||