) emitter -> {
try {
mDatabase = AppDatabase.getInstance(mActivity);
@@ -131,13 +200,13 @@ public class AddressUserDetailActivity extends BaseActivity {
}
});
- mBtnOften.setOnClickListener(v -> {
+ mLlAddContact.setOnClickListener(v -> {
if (mOftenContactByUserId == null) {
OftenContactBean b = new OftenContactBean();
b.setUserId(mUserByUserId.getUserId());
mOftenContactDao.insertUser(b);
mOftenContactByUserId = mOftenContactDao.getOftenContactByUserId(mUserByUserId.getUserId());
- mBtnOften.setText("移除常用联系人");
+ mTvAddContact.setText("移除好友");
ToastUtils.show("添加成功");
} else {
//移除常用联系人
@@ -145,10 +214,10 @@ public class AddressUserDetailActivity extends BaseActivity {
setResult(13);
mOftenContactByUserId = null;
ToastUtils.show("移除成功");
- mBtnOften.setText("添加常用联系人");
+ mTvAddContact.setText("添加好友");
}
});
- mBtnCall.setOnClickListener(v -> {
+ mLlCall.setOnClickListener(v -> {
if (TextUtils.isEmpty(mUserByUserId.getUserPhone())) {
ToastUtils.show("该联系人未录入联系电话");
} else {
@@ -158,32 +227,630 @@ public class AddressUserDetailActivity extends BaseActivity {
startActivity(intent);
}
});
- mIvChat.setOnClickListener(v -> ARouter.getInstance()
+ mLlMsg.setOnClickListener(v -> ARouter.getInstance()
.build(PathConfig.PATH_MODULE_CHAT_CHAT)
.withString("to", mUserByUserId.getUserId())
.withString("toName", mUserByUserId.getUserName())
.navigation());
+ initViews();
+ }
+
+ private void initViews() {
+ mMsgHandler = new MsgHandler();
+ //语音通话
+ mLlVoiceCall.setOnClickListener(v -> {
+ if (AppUtils.isSupportCall()) {
+ if (Math.abs(mClickTime - System.currentTimeMillis()) > 3000) {
+ //发送让对方登录的广播
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mClickTime = System.currentTimeMillis();
+ LoginMangerV2.getInstance().checkLogin("zh-1", "hc123456@", AddressUserDetailActivity.this);
+ mVoiceTime = System.currentTimeMillis();
+ isVideoCall = false;
+ }
+ } else {
+ ToastUtils.show("操作太过频繁");
+ }
+ } else {
+ ToastUtils.show("您的手机不支持该功能");
+ }
+ });
+
+ //视频通话
+ mLlVideoCall.setOnClickListener(v -> {
+ if (AppUtils.isSupportCall()) {
+ if (Math.abs(mClickTime - System.currentTimeMillis()) > 3000) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ mClickTime = System.currentTimeMillis();
+ LoginMangerV2.getInstance().checkLogin("zh-1", "hc123456@", AddressUserDetailActivity.this);
+ mVideoTime = System.currentTimeMillis();
+ isVideoCall = true;
+ }
+ } else {
+ ToastUtils.show("操作太过频繁");
+ }
+
+ } else {
+ ToastUtils.show("您的手机不支持该功能");
+ }
+ });
+ }
+
+ /**
+ * 发送让对方登录的消息
+ */
+ private void sendNotifyLogin() {
+ LogUtils.e("通知对方登录" + System.currentTimeMillis());
+ Intent intent = new Intent();
+ MessageBean msgBean = buildMsgBean("", mUserByUserId.getUserId(), UserLgUtils.getUserId(), UserLgUtils.getUserId(), MsgTypeStateEnum.MSG_TO_OTHER_VOICE, "通话消息", StatusCode.MSG_TYPE_NOTIFY_LOGIN);
+ intent.putExtra("bean", msgBean);
+ intent.setAction(PathConfig.ACTION_NOTIFY_CONTACT_LOGIN);
+ sendBroadcast(intent);
+ }
+
+
+ /**
+ * 构建
+ */
+ private MessageBean buildMsgBean(String id, String toId, String formId, String formName, int msgType, String msg, int type) {
+ MessageBean bean = new MessageBean();
+ String uuid = "";
+ if (!TextUtils.isEmpty(id)) {
+ uuid = id;
+ } else {
+ uuid = UUID.randomUUID().toString();
+ }
+ bean.setMsgWhat(System.currentTimeMillis());
+ bean.setId(uuid);
+ bean.setTo(toId);
+ bean.setFrom(formId);
+ bean.setSystem(false);
+ bean.setTimestamp(System.currentTimeMillis());
+ bean.setFromName(formName);
+ bean.setSendState(MsgTypeStateEnum.MSG_SEND_ING);//发送状态
+ bean.setBody(msg);
+ bean.setMsgType(msgType);
+ bean.setType(type);
+ return bean;
}
private void setDataToView() {
refreshView(STATE_LOAD_SUCCESS);
if (mOftenContactByUserId == null) {
//不是常用联系人
- mBtnOften.setText("添加常用联系人");
+ mTvAddContact.setText("添加好友");
} else {
//是常用联系人
- mBtnOften.setText("移除常用联系人");
+ mTvAddContact.setText("移除好友");
}
mTvUserName.setText(mUserByUserId.getUserName());
mTvPhone.setText("联系电话:" + mUserByUserId.getUserPhone());
mTvEmail.setText("邮箱:" + (TextUtils.isEmpty(mUserByUserId.getUserEmail()) ? "未录入" : mUserByUserId.getUserEmail()));
if (mDeptById != null) {
- mTvDept.setText("部门:" + (TextUtils.isEmpty(mDeptById.getDepartmentName()) ? "未录入" : mDeptById.getDepartmentName()));
+ mTvDeptDept.setText("部门:" + (TextUtils.isEmpty(mDeptById.getDepartmentName()) ? "未录入" : mDeptById.getDepartmentName()));
+ mTvDept.setText((TextUtils.isEmpty(mDeptById.getDepartmentName()) ? "未录入" : mDeptById.getDepartmentName()));
mTvDeptPhone.setText("部门电话:" + (TextUtils.isEmpty(mDeptById.getDepartmentTel()) ? "未录入" : mDeptById.getDepartmentTel()));
mTvDeptAddress.setText("部门地址:" + (TextUtils.isEmpty(mDeptById.getDepartmentAddress()) ? "未录入" : mDeptById.getDepartmentAddress()));
}
}
+
+ /**
+ * 注册广播
+ */
+ private void registerRec() {
+ IntentFilter filter = new IntentFilter();
+ mMsgReceiver = new MsgReceiver();
+ filter.addAction(PathConfig.ACTION_MSG_CHAT_REFRESH);//刷新信息状态
+ filter.addAction(PathConfig.ACTION_MSG_SOCKET_FAIL);//socket连接失败
+ filter.addAction(PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_SUCCESS);//对方登录成功
+ filter.addAction(PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_FAIL);//对方登录不成功
+ filter.addAction(PathConfig.ACTION_CALL_OTHER_NO_SUPPORT);//对方手机不支持通话
+ filter.addAction(PathConfig.ACTION_USER_OFFLINE);//用户不在线
+ registerReceiver(mMsgReceiver, filter);
+ LocalBroadcast.getInstance().registerBroadcast(this, mActions);
+ }
+
+ /**
+ * 注销广播
+ */
+ private void unRegister() {
+ if (mMsgReceiver != null) {
+ unregisterReceiver(mMsgReceiver);
+ }
+ LocalBroadcast.getInstance().unRegisterBroadcast(this, mActions);
+ }
+
+
+ public class MsgHandler extends Handler {
+ @Override
+ public void handleMessage(@androidx.annotation.NonNull Message msg) {
+ String id = (String) msg.obj;
+ if ("login".equals(id)) {
+ //语音视频通话消息 3秒钟对方为返回登录成功消息 定义为登录失败
+ //TODO 对方登录失败
+ ToastUtils.show("对方不在线");
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ }
+ }
+ }
+
+ private class MsgReceiver extends BroadcastReceiver {
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (!TextUtils.isEmpty(action)) {
+ switch (action) {
+ case PathConfig.ACTION_USER_OFFLINE:
+ ToastUtils.show("对方不在线,无法通话");
+ LoginMangerV2.getInstance().logout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ mMsgHandler.removeMessages(10086);
+ break;
+ case PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_SUCCESS:
+ //TODO 对方登录成功,获取返回的号码, 进行拨打电话
+ LogUtils.e("对方登录成功" + System.currentTimeMillis());
+ mToUserPhoneNumber = intent.getStringExtra("number");
+ doCall();
+ break;
+ case PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_FAIL://通话时对方登录失败
+ ToastUtils.show("对方不在线,请稍后重试");
+ LoginMangerV2.getInstance().logout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ break;
+ case PathConfig.ACTION_CALL_OTHER_NO_SUPPORT://对方手机不支持通话
+ ToastUtils.show("对方手机不支持通话");
+ LoginMangerV2.getInstance().logout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ break;
+ case PathConfig.ACTION_MSG_SOCKET_FAIL://Socket连接失败
+ break;
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public void onEvtCallStartResult(TsdkCall call) {
+ LogUtils.e("通话返回" + showCallLog(call));
+ this.mCall = call;
+ }
+
+ private String showCallLog(TsdkCall call) {
+ TsdkCallInfo info = call.getCallInfo();
+ StringBuilder sb = new StringBuilder();
+ sb.append(info.getCallId()).append("\n");
+ sb.append(info.getConfId()).append("\n");
+ sb.append(info.getIsFocus()).append("\n");
+ sb.append(info.getSipAccountId()).append("\n");
+ sb.append(info.getSsrcTableEnd()).append("\n");
+ sb.append(info.getCallState()).append("\n");
+ sb.append(info.getIsAutoAnswer()).append("\n");
+ sb.append(info.getPeerDisplayName()).append("\n");
+ sb.append(info.getPeerNumber()).append("\n");
+ sb.append(info.getReasonDescription()).append("\n");
+ return sb.toString();
+ }
+
+ @Override
+ public void onEvtCallEnded(TsdkCall call) {
+ LogUtils.e("通话结束" + call);
+ runOnUiThread(() -> {
+ sendNotifyLogout();
+ mFullScreenDialog.cancel();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ });
+ }
+
+ @Override
+ public void onEvtCallConnected(TsdkCall call) {
+ LogUtils.e("通话连接上" + call);
+ runOnUiThread(() -> mFullScreenDialog.cancel());
+ if (call.getCallInfo().getIsFocus() == 1) {
+ CallMgrV2.getInstance().handleCallConnected(call);
+ } else {
+ CallInfo callInfo = CallMgrV2.getInstance().getCallInfo(call);
+ Intent intent = new Intent(mActivity, SponsorMeetingActivity.class);
+ intent.putExtra(Constant.DIAL, Constant.DIAL);
+ intent.putExtra(Constant.CALL_INFO, callInfo);
+ intent.putExtra(Constant.CALLED_NAME, call.getCallInfo().getPeerDisplayName());
+ intent.putExtra(Constant.VOICE_CALL_NUM, call.getCallInfo().getPeerNumber());
+ startActivity(intent);
+ }
+ }
+
+ @Override
+ public void onFailed(int result, String msg) {
+ LogUtils.e("呼叫失败+=" + msg + result);
+ runOnUiThread(() -> {
+ sendNotifyLogout();
+ LoginMangerV2.getInstance().logout();
+ mFullScreenDialog.cancel();
+ });
+ }
+
+ @Override
+ public void onNumIsEmpty() {
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ sendNotifyLogout();
+ ToastUtils.show("对方暂无号码");
+ }
+
+ @Override
+ public void onNetError() {
+ sendNotifyLogout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ DialogUtil.tipsDialog(mActivity, getString(R.string.cloudLink_contacts_networkError),
+ getString(R.string.cloudLink_meeting_iKnow));
+ }
+
+ @Override
+ public void onStartCall() {
+ LogUtils.e("拨打开始" + System.currentTimeMillis());
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ View view = View.inflate(this, R.layout.start_call, null);
+ view.findViewById(R.id.iv_center_red_call).setVisibility(View.VISIBLE);
+ view.findViewById(R.id.tv_name).setVisibility(View.VISIBLE);
+ ImageView ivIcon = view.findViewById(R.id.bg_icon);
+// val index = FirstLetterUtil.getFirstLetter(stv_name.text?.toString()).toLowerCase()[0].toInt() * 10
+ ivIcon.setImageResource(R.drawable.profile_image_bg);
+// bg.setImageLevel(index)
+ TextView tvName = view.findViewById(R.id.tv_name);
+ tvName.setText(mUserByUserId.getUserName());
+ TextView method = view.findViewById(R.id.tv_method);
+ method.setText(getString(R.string.cloudLink_meeting_callings));
+ mFullScreenDialog.setCancelable(false);
+ view.findViewById(R.id.iv_center_red_call).setOnClickListener(v -> {
+ mFullScreenDialog.cancel();
+ if (null != mCall) {
+ CallMgrV2.getInstance().endCall(mCall.endCall());
+ }
+ });
+ mFullScreenDialog.setContentView(view);
+ if (mFullScreenDialog.isShowing()) {
+ return;
+ }
+ mFullScreenDialog.show();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+ LogUtils.e("广播返回" + broadcastName);
+ switch (broadcastName) {
+ case BroadcastConstant.ACTION_LOGIN_SUCCESS:
+ case BroadcastConstant.ACTION_FIRST_CHANGE_PWD:
+ handlerLoginSuccess(obj);
+ break;
+ case BroadcastConstant.ACTION_LOGIN_FAILED:
+ handlerLoginFailed(obj);
+ break;
+ case BroadcastConstant.ACTION_AUTH_FAILED:
+ handlerAuthFailed(obj);
+ break;
+ case BroadcastConstant.ACTION_CALL_END:
+ // 匿名入会离开会议
+ EncryptedSPTool.putBoolean(Constant.JOIN_MEETING_NOLOGIN_ING, false);
+ this.handlerCallEnd(obj);
+ break;
+ case BroadcastConstant.ACTION_GET_TEMP_USER_RESULT:
+ // 获取用于匿名方式加入会议的临时用户结果通知
+ TsdkCommonResult result = (TsdkCommonResult) obj;
+ switch (result.getResult()) {
+ case 0:
+ EncryptedSPTool.putBoolean(Constant.JOIN_MEETING_NOLOGIN_ING, true);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554498:
+ ToastUtils.show(R.string.cloudLink_meeting_joinMeeting_protocol_error);
+ break;
+ default:
+ ToastUtils.show(R.string.cloudLink_meeting_join_503);
+ break;
+ }
+// getView().closeConferenceShowLoading();
+ break;
+ case BroadcastConstant.ACTION_NET_WORK_IS_CONNECT:
+ //网络变化
+ boolean isConnect = (boolean) obj;
+// getView().displayNetChangeStatus(isConnect);
+ ToastUtils.show("网络变化");
+ break;
+ case BroadcastConstant.ACTION_CLOSE_ACTIVITY:
+ //关闭页面
+// getView().closeActivity();
+ break;
+ case BroadcastConstant.ACTION_CONF_CALL_FAILED:
+// getView().dismissLoading();
+ if (null != obj && (int) obj == TSDKErrorConstant.TSDK_AUTHENTICATION_ERROR_CODE) {
+ ToastUtils.show(R.string.cloudLink_login_auth_Fail);
+ } else if (null != obj && (int) obj == TSDKErrorConstant.TSDK_NET_ERROR_CODE) {
+ // 网络异常
+ ToastUtils.show(R.string.cloudLink_contacts_networkError);
+ } else {
+ ToastUtils.show(R.string.cloudLink_meeting_joinMeetingFail);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void handlerCallEnd(Object obj) {
+ runOnUiThread(() -> actionCallEnd(obj));
+ }
+
+ /**
+ * 处理认证失败
+ *
+ * @param obj 状态值
+ */
+ private void handlerAuthFailed(Object obj) {
+ int errCode = (int) obj;
+ String hintMsg;
+ switch (errCode) {
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554444:
+ // DNS解析异常
+ hintMsg = getString(R.string.cloudLink_dnsError);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554470:
+ // 网络异常
+ default:
+ hintMsg = getString(R.string.cloudLink_login_loginFail);
+ break;
+ }
+ if (!TextUtils.isEmpty(hintMsg)) {
+ CloudLinkDialog hintDialog = new CloudLinkDialog(mActivity);
+ hintDialog.setOnlyYes(true);
+ hintDialog.setYes(getString(R.string.cloudLink_sure), null,
+ hintDialog::dismiss);
+ hintDialog.setStr_message(hintMsg, null);
+ hintDialog.show();
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void actionCallEnd(Object obj) {
+ if (obj instanceof CallInfo) {
+ runOnUiThread(() -> {
+ LoginMangerV2.getInstance().logout();
+ boolean isNoStream = EncryptedSPTool.getBoolean(Constant.IS_NO_STREAM_DURATION);
+ LogUtils.e("没有流===" + isNoStream);
+ if (isNoStream) {
+ dealNoDuration();
+ return;
+ }
+ CallInfo callEnd = (CallInfo) obj;
+ if (callEnd != null) {
+ LogUtils.d("CallEnd()==reasonCode = " + callEnd.getReasonCode() + ";isFocus = " + callEnd.isFocus());
+ // 是否是会议
+ if (callEnd.isFocus()) {
+ if (isContactCall()) {
+ dealContactCallMeeting(callEnd.getReasonCode());
+ } else {
+// dealCallMeeting(callEnd.getReasonCode());
+ }
+ } else {
+ if (isContactCall()) {
+ dealContactCallNotMeeting(callEnd.getReasonCode());
+ } else {
+// dealCallNotMeeting(callEnd.getReasonCode());
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 通话相关会议相关通知响应
+ *
+ * @param reasonCode 响应码
+ */
+ public void dealContactCallMeeting(int reasonCode) {
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_userOffline));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_failed));
+ break;
+ default:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endConfSuccess));
+ break;
+ }
+ }
+
+ /**
+ * 通话相关非会议结束处理
+ *
+ * @param reasonCode 返回码
+ */
+ private void dealContactCallNotMeeting(int reasonCode) {
+ LogUtils.e(reasonCode + "================");
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331801:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endCall));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331781:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callHangUp));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_userOffline));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_call_failed));
+ break;
+ default:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callEnd));
+ sendNotifyLogout();
+ break;
+ }
+ }
+
+ /**
+ * 是否呼叫相关页面
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private boolean isContactCall() {
+ if (AppUtil.isActivityTop(ChatActivity.class, this)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ public void dealNoDuration() {
+ Observable.timer(ConstantsV2.DELAY_MILLIS_600, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(aLong -> {
+// TODO Intent it = new Intent(BaseMvpActivityV2.this, NoDurationActivityV2.class);
+// it.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+// startActivity(it);
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, false);
+ });
+ }
+
+ /**
+ * 处理登录失败
+ *
+ * @param obj 状态值
+ */
+ private void handlerLoginFailed(Object obj) {
+ String str = (String) obj;
+ int code = Integer.parseInt(str.split("-")[0]);
+ String index = str.split("-")[1];
+ String msgHint = "";
+ switch (code) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331749:
+ // 服务器返回禁止
+// if (LoginActivityV2.isFirstChangePwd) {
+// msgHint = getString(R.string.cloudLink_login_changePwdSuccessToLogin);
+// LoginActivityV2.isFirstChangePwd = false;
+// } else {
+ msgHint = getString(R.string.cloudLink_login_loginPassError);
+// }
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331659:
+ // 网络接入错误
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331754:
+ // 请求超时
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554441:
+ // 超时
+ msgHint = getString(R.string.cloudLink_login_loginTimeOut);
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331762:
+ // 临时失效
+ msgHint = getString(R.string.cloudLink_login_loginRepeat);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554448:
+ // 服务器异常
+ msgHint = getString(R.string.cloudLink_login_serviceError);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554449:
+ // 账号被锁定
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554467:
+ // 用户已被锁定
+ msgHint = getString(R.string.cloudLink_login_accountLocked);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554457:
+ // 老密码错误
+ if (index.equals(ConstantsV2.LOGIN_STATUS_CODE_0)) {
+ msgHint = getString(R.string.cloudLink_login_accountLocked);
+ }
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554454:
+ // 查询服务器地址失败
+ msgHint = getString(R.string.cloudLink_login_searchServerFail);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554466:
+ // 用户名或者密码错误
+ if (index.equals(ConstantsV2.LOGIN_STATUS_CODE_0)) {
+ msgHint = getString(R.string.cloudLink_mine_loginFailZero);
+ } else {
+ msgHint = getString(R.string.cloudLink_mine_loginFailIndex1) +
+ index + getString(R.string.cloudLink_mine_loginFailIndex2);
+ }
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554482:
+ // 账号未激活
+ msgHint = getString(R.string.cloudLink_login_err_32);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554444:
+ // DNS解析异常
+ msgHint = getString(R.string.cloudLink_dnsError);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554487:
+ // 账号已过期
+ msgHint = getString(R.string.cloudLink_login_error_2037);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554470:
+ // 网络异常
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331817:
+ // SIP TCP建立失败
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554446:
+ // 鉴权失败
+ default:
+ msgHint = getString(R.string.cloudLink_login_loginFail);
+ break;
+ }
+ if (!TextUtils.isEmpty(msgHint)) {
+ CloudLinkDialog dialogHint = new CloudLinkDialog(mActivity);
+ dialogHint.setOnlyYes(true);
+ dialogHint.setYes(getString(R.string.cloudLink_sure), null, dialogHint::dismiss);
+ dialogHint.setStr_message(msgHint, null);
+ dialogHint.show();
+ }
+ }
+
+ /**
+ * 处理登录成功
+ *
+ * @param obj 状态值
+ */
+ private void handlerLoginSuccess(Object obj) {
+ this.loginSuccess();
+ int number = Integer.parseInt((String) obj);
+ if (com.tengshisoft.chatmodule.hwclud.utils.UIUtil.isService3()) {
+ if (number == 0) {
+ ToastUtils.show(getString(R.string.cloudLink_login_pwd_expire));
+ } else if (number != ConstantsV2.LOGIN_STATUS_CODE_255) {
+ ToastUtils.show(getString(R.string.cloudLink_login_pwd_data)
+ + number + getString(R.string.cloudLink_login_pwd_data_2));
+ }
+ }
+ }
+
+
+ @Override
+ protected void onPause() {
+ unRegister();
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ registerRec();
+ super.onResume();
+ }
+
+
@Override
protected void clearSearch() {
@@ -193,4 +860,125 @@ public class AddressUserDetailActivity extends BaseActivity {
protected void doSearchByTitle() {
}
+
+ @Override
+ public void loginIng() {
+ runOnUiThread(() -> {
+ mDialog = UIUtil.initDialog(mActivity, "呼叫中....");
+ mDialog.show();
+ });
+ }
+
+ @Override
+ public void loginFail() {
+ runOnUiThread(() -> {
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ ToastUtils.show("呼叫失败,请稍后重试");
+ });
+ }
+
+ @Override
+ public void loginSuccess() {
+ LogUtils.e("登录成功,通知对方登录");
+ //登录成功通知对方登录
+ sendNotifyLogin();
+
+ }
+
+
+ /**
+ * 判断socket是否在运行
+ */
+ private boolean isRunning() {
+ return AppUtils.isRunningService(getApplicationContext(), getPackageName() + ":local");
+ }
+
+ /**
+ * 通知对方退出登录
+ */
+ private void sendNotifyLogout() {
+ if (isRunning()) {
+ MessageBean bean = buildMsgBean("", mUserByUserId.getUserId(), UserLgUtils.getUserId(), UserLgUtils.getUserId(), MsgTypeStateEnum.MSG_TO_OTHER_IMG, "退出登录", StatusCode.MSG_TYPE_NOTIFY_LOGOUT);
+ Intent intent = new Intent();
+ intent.setAction(PathConfig.ACTION_MSG_CHAT_SEND);
+ intent.putExtra("bean", bean);
+ sendBroadcast(intent);
+ }
+ }
+
+ private void doCall() {
+ LogUtils.e("广播返回拨打指令=" + System.currentTimeMillis());
+ if (!MeetingController.getInstance().isHaveNet()) {
+ DialogUtil.tipsDialog(this, getString(R.string.cloudLink_contacts_networkError),
+ getString(R.string.cloudLink_meeting_iKnow));
+ LoginMangerV2.getInstance().logout();
+ sendNotifyLogout();
+ return;
+ }
+ String terminal = LoginMangerV2.getInstance().getTerminal();
+ if (MeetingController.getInstance().isMinimize() || MeetingController.getInstance().isAux()) {
+ LogUtils.e("正在呼叫中");
+ ToastUtils.show(getString(R.string.cloudLink_meeting_calling));
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ return;
+ }
+ if (mToUserPhoneNumber.equals(terminal)) {
+ ToastUtils.show(getString(R.string.cloudLink_canNotCalledMyself));
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ return;
+ }
+ if (Math.abs(mVideoTime - mVoiceTime) > 1000) {
+ //开始拨打电话
+ if (TextUtils.isEmpty(mToUserPhoneNumber)) {
+ this.onNumIsEmpty();
+ return;
+ } else if (!MeetingController.getInstance().isHaveNet()) {
+ this.onNetError();
+ return;
+ } else {
+ this.onStartCall();
+ }
+ NetUtil.startCallOn(mToUserPhoneNumber, isVideoCall, mUserByUserId.getUserName(), new CallOnResponse() {
+ @Override
+ public void onEvtCallStartResult(@NotNull TsdkCall call) {
+ runOnUiThread(() -> AddressUserDetailActivity.this.onEvtCallStartResult(call));
+ }
+
+ @Override
+ public void onEvtCallEnded(@NotNull TsdkCall call) {
+ runOnUiThread(() -> AddressUserDetailActivity.this.onEvtCallEnded(call));
+ }
+
+ @Override
+ public void onEvtCallConnected(@NotNull TsdkCall call) {
+ runOnUiThread(() -> AddressUserDetailActivity.this.onEvtCallConnected(call));
+ }
+
+ @Override
+ public void onFailed(int resultCode, @NotNull String msg) {
+ runOnUiThread(() -> AddressUserDetailActivity.this.onFailed(resultCode, msg));
+ }
+ });
+ }
+ overridePendingTransition(R.anim.dialog_in_anim, 0);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mBind != null) {
+ mBind.unbind();
+ }
+ if (mMsgHandler != null) {
+ mMsgHandler.removeCallbacksAndMessages(null);
+ }
+ BaseAppContext.getInstance().setUsername("");
+ BaseAppContext.getInstance().setOtherUserId("");
+ super.onDestroy();
+ }
}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/ChatActivity.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/ChatActivity.java
index c68ac8f..ea9e794 100644
--- a/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/ChatActivity.java
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/ChatActivity.java
@@ -9,7 +9,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.drawable.AnimationDrawable;
-import android.media.MediaPlayer;
+import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
@@ -18,25 +18,42 @@ import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
+import android.widget.TextView;
import com.alibaba.android.arouter.facade.annotation.Route;
import com.alibaba.android.arouter.launcher.ARouter;
-import com.bumptech.glide.load.model.stream.BaseGlideUrlLoader;
-import com.google.android.material.tabs.TabLayout;
import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkCallInfo;
+import com.huawei.ecterminalsdk.models.TsdkCommonResult;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
import com.luck.picture.lib.PictureSelector;
import com.luck.picture.lib.config.PictureMimeType;
import com.luck.picture.lib.entity.LocalMedia;
import com.scwang.smart.refresh.layout.SmartRefreshLayout;
-import com.scwang.smart.refresh.layout.api.RefreshLayout;
-import com.scwang.smart.refresh.layout.listener.OnRefreshListener;
import com.tengshisoft.chatmodule.R;
import com.tengshisoft.chatmodule.R2;
import com.tengshisoft.chatmodule.adapter.MsgAdapter;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.TSDKErrorConstant;
import com.tengshisoft.chatmodule.holder.MsgAudioHolder;
+import com.tengshisoft.chatmodule.hwclud.api.CallOnResponse;
+import com.tengshisoft.chatmodule.hwclud.api.LoginView;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.LoginMangerV2;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.ui.CloudLinkDialog;
+import com.tengshisoft.chatmodule.hwclud.ui.DialogUtil;
+import com.tengshisoft.chatmodule.hwclud.ui.FullScreenDialog;
+import com.tengshisoft.chatmodule.hwclud.utils.AppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.NetUtil;
import com.tengshisoft.chatmodule.sockets.MsgTypeStateEnum;
import com.tenlionsoft.baselib.app.BaseAppContext;
-import com.tenlionsoft.baselib.constant.LionActions;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
import com.tenlionsoft.baselib.constant.PathConfig;
import com.tenlionsoft.baselib.constant.PermissionConstants;
import com.tenlionsoft.baselib.constant.StatusCode;
@@ -56,6 +73,7 @@ import com.tenlionsoft.baselib.core.widget.chat.StateButton;
import com.tenlionsoft.baselib.core.widget.chat.WrapContentHeightViewPager;
import com.tenlionsoft.baselib.core.widget.videorecord.MediaManager;
import com.tenlionsoft.baselib.utils.AppUtils;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
import com.tenlionsoft.baselib.utils.ExceptionHandler;
import com.tenlionsoft.baselib.utils.LogUtils;
import com.tenlionsoft.baselib.utils.PermissionUtils;
@@ -67,12 +85,12 @@ import org.json.JSONObject;
import java.io.File;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
import java.util.UUID;
+import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -83,7 +101,6 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
-import io.reactivex.rxjava3.functions.Function;
import io.reactivex.rxjava3.schedulers.Schedulers;
import me.rosuh.filepicker.config.FilePickerManager;
import okhttp3.MediaType;
@@ -97,7 +114,7 @@ import okhttp3.RequestBody;
* 描述: 文字聊天界面
*/
@Route(path = PathConfig.PATH_MODULE_CHAT_CHAT)
-public class ChatActivity extends BaseActivity {
+public class ChatActivity extends BaseActivity implements LoginView, LocalBroadcastReceiver {
@BindView(R2.id.rv_chat_list)
@@ -160,6 +177,21 @@ public class ChatActivity extends BaseActivity {
private int mTempCount = 0;
private ChatUiHelper mUiHelper;
private String mIsLoginTxt = "登录中";
+ private ProgressDialog mDialog;
+ private boolean isVideoCall = false;
+ private TsdkCall mCall;//当前呼叫的对象
+ private FullScreenDialog mFullScreenDialog;
+ private String mToUserPhoneNumber;
+ private long mVoiceTime;
+ private long mVideoTime;
+ private String[] mActions = new String[]{BroadcastConstant.ACTION_GET_TEMP_USER_RESULT,
+ BroadcastConstant.ACTION_CALL_END, BroadcastConstant.ACTION_LOGIN_SUCCESS,
+ BroadcastConstant.ACTION_LOGIN_FAILED, BroadcastConstant.ACTION_AUTH_FAILED,
+ BroadcastConstant.ACTION_FIRST_CHANGE_PWD,
+ BroadcastConstant.ACTION_CONF_CALL_FAILED,
+ BroadcastConstant.ACTION_CLOSE_ACTIVITY,
+ BroadcastConstant.ACTION_NET_WORK_IS_CONNECT};
+ private long mClickTime;
@Override
protected int setLayoutId() {
@@ -191,6 +223,7 @@ public class ChatActivity extends BaseActivity {
initChatUi();
registerBR();
checkPermissionIsGrant();
+ mFullScreenDialog = new FullScreenDialog(mActivity);
mMsgHandler = new MsgHandler();
}
@@ -298,7 +331,12 @@ public class ChatActivity extends BaseActivity {
filter.addAction(PathConfig.ACTION_MSG_CHAT_REFRESH);//刷新信息状态
filter.addAction(PathConfig.ACTION_MSG_CHAT);//收信息
filter.addAction(PathConfig.ACTION_MSG_SOCKET_FAIL);//socket连接失败
+ filter.addAction(PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_SUCCESS);//对方登录成功
+ filter.addAction(PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_FAIL);//对方登录不成功
+ filter.addAction(PathConfig.ACTION_CALL_OTHER_NO_SUPPORT);//对方手机不支持通话
+ filter.addAction(PathConfig.ACTION_USER_OFFLINE);//用户不在线
registerReceiver(mMsgReceiver, filter);
+ LocalBroadcast.getInstance().registerBroadcast(this, mActions);
}
@@ -373,14 +411,16 @@ public class ChatActivity extends BaseActivity {
//语音通话
mRlAudioCall.setOnClickListener(v -> {
if (AppUtils.isSupportCall()) {
- ARouter.getInstance()
- .build(PathConfig.PATH_MODULE_CHAT_CALL_SINGLE)
- .withString(LionActions.EXTRA_TARGET, mTo)
- .withBoolean(LionActions.EXTRA_MO, true)
- .withBoolean(LionActions.EXTRA_AUDIO_ONLY, false)
- .withBoolean(LionActions.EXTRA_FROM_FLOATING_VIEW, false)
- .withString(LionActions.EXTRA_USER_NAME, mToName)
- .navigation();
+ if (Math.abs(mClickTime - System.currentTimeMillis()) > 3000) {
+ //发送让对方登录的广播
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ LoginMangerV2.getInstance().checkLogin("zh-1", "hc123456@", ChatActivity.this);
+ mVoiceTime = System.currentTimeMillis();
+ isVideoCall = false;
+ }
+ } else {
+ ToastUtils.show("操作太过频繁");
+ }
} else {
ToastUtils.show("您的手机不支持该功能");
}
@@ -389,14 +429,15 @@ public class ChatActivity extends BaseActivity {
//视频通话
mRlVideoCall.setOnClickListener(v -> {
if (AppUtils.isSupportCall()) {
- ARouter.getInstance()
- .build(PathConfig.PATH_MODULE_CHAT_CALL_SINGLE)
- .withString(LionActions.EXTRA_TARGET, mTo)
- .withBoolean(LionActions.EXTRA_MO, true)
- .withBoolean(LionActions.EXTRA_AUDIO_ONLY, true)
- .withBoolean(LionActions.EXTRA_FROM_FLOATING_VIEW, false)
- .withString(LionActions.EXTRA_USER_NAME, mToName)
- .navigation();
+ if (Math.abs(mClickTime - System.currentTimeMillis()) > 3000) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ LoginMangerV2.getInstance().checkLogin("zh-1", "hc123456@", ChatActivity.this);
+ mVideoTime = System.currentTimeMillis();
+ isVideoCall = true;
+ }
+ } else {
+ ToastUtils.show("操作太过频繁");
+ }
} else {
ToastUtils.show("您的手机不支持该功能");
}
@@ -407,6 +448,107 @@ public class ChatActivity extends BaseActivity {
mMsgAdapter.setData(mBeanList);
}
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void actionCallEnd(Object obj) {
+ if (obj instanceof CallInfo) {
+ runOnUiThread(() -> {
+ LoginMangerV2.getInstance().logout();
+ boolean isNoStream = EncryptedSPTool.getBoolean(Constant.IS_NO_STREAM_DURATION);
+ if (isNoStream) {
+ dealNoDuration();
+ return;
+ }
+ CallInfo callEnd = (CallInfo) obj;
+ if (callEnd != null) {
+ LogUtils.e("CallEnd()" +
+ "reasonCode = " + callEnd.getReasonCode() + ";isFocus = " + callEnd.isFocus());
+ // 是否是会议
+ if (callEnd.isFocus()) {
+ if (isContactCall()) {
+ dealContactCallMeeting(callEnd.getReasonCode());
+ } else {
+// dealCallMeeting(callEnd.getReasonCode());
+ }
+ } else {
+ if (isContactCall()) {
+ dealContactCallNotMeeting(callEnd.getReasonCode());
+ } else {
+// dealCallNotMeeting(callEnd.getReasonCode());
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 通话相关会议相关通知响应
+ *
+ * @param reasonCode 响应码
+ */
+ public void dealContactCallMeeting(int reasonCode) {
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_userOffline));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_failed));
+ break;
+ default:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endConfSuccess));
+ break;
+ }
+ }
+
+ /**
+ * 通话相关非会议结束处理
+ *
+ * @param reasonCode 返回码
+ */
+ private void dealContactCallNotMeeting(int reasonCode) {
+ LogUtils.e(reasonCode + "================");
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331801:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endCall));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331781:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callHangUp));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_userOffline));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_call_failed));
+ break;
+ default:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callEnd));
+ sendNotifyLogout();
+ break;
+ }
+ }
+
+ /**
+ * 是否呼叫相关页面
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private boolean isContactCall() {
+ if (AppUtil.isActivityTop(ChatActivity.class, this)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ public void dealNoDuration() {
+ Observable.timer(ConstantsV2.DELAY_MILLIS_600, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(aLong -> {
+// TODO Intent it = new Intent(BaseMvpActivityV2.this, NoDurationActivityV2.class);
+// it.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+// startActivity(it);
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, false);
+ });
+ }
+
/**
* 上传音频
*/
@@ -804,22 +946,499 @@ public class ChatActivity extends BaseActivity {
}
}
+ @Override
+ public void loginIng() {
+ runOnUiThread(() -> {
+ mDialog = UIUtil.initDialog(mActivity, "呼叫中....");
+ mDialog.show();
+ });
+ }
+
+ @Override
+ public void loginFail() {
+ runOnUiThread(() -> {
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ ToastUtils.show("呼叫失败,请稍后重试");
+ });
+ }
+
+ @Override
+ public void loginSuccess() {
+ LogUtils.e("ChatActivity登录成功");
+ //登录成功通知对方登录
+ sendNotifyLogin();
+ }
+
+
+ private void doCall() {
+ if (!MeetingController.getInstance().isHaveNet()) {
+ DialogUtil.tipsDialog(this, getString(R.string.cloudLink_contacts_networkError),
+ getString(R.string.cloudLink_meeting_iKnow));
+ LoginMangerV2.getInstance().logout();
+ sendNotifyLogout();
+ return;
+ }
+ String terminal = LoginMangerV2.getInstance().getTerminal();
+ if (MeetingController.getInstance().isMinimize() || MeetingController.getInstance().isAux()) {
+ LogUtils.e("正在呼叫中");
+ ToastUtils.show(getString(R.string.cloudLink_meeting_calling));
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ return;
+ }
+ if (mToUserPhoneNumber.equals(terminal)) {
+ ToastUtils.show(getString(R.string.cloudLink_canNotCalledMyself));
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ return;
+ }
+ if (Math.abs(mVideoTime - mVoiceTime) > 1000) {
+ //开始拨打电话
+ if (TextUtils.isEmpty(mToUserPhoneNumber)) {
+ this.onNumIsEmpty();
+ return;
+ } else if (!MeetingController.getInstance().isHaveNet()) {
+ this.onNetError();
+ return;
+ } else {
+ this.onStartCall();
+ }
+ NetUtil.startCallOn(mToUserPhoneNumber, isVideoCall, mToName, new CallOnResponse() {
+ @Override
+ public void onEvtCallStartResult(@NotNull TsdkCall call) {
+ runOnUiThread(() -> ChatActivity.this.onEvtCallStartResult(call));
+ }
+
+ @Override
+ public void onEvtCallEnded(@NotNull TsdkCall call) {
+ runOnUiThread(() -> ChatActivity.this.onEvtCallEnded(call));
+ }
+
+ @Override
+ public void onEvtCallConnected(@NotNull TsdkCall call) {
+ runOnUiThread(() -> ChatActivity.this.onEvtCallConnected(call));
+ }
+
+ @Override
+ public void onFailed(int resultCode, @NotNull String msg) {
+ runOnUiThread(() -> ChatActivity.this.onFailed(resultCode, msg));
+ }
+ });
+ }
+ overridePendingTransition(R.anim.dialog_in_anim, 0);
+ }
+
+
+ @Override
+ public void onEvtCallStartResult(TsdkCall call) {
+ LogUtils.e("通话返回" + showCallLog(call));
+ this.mCall = call;
+ }
+
+ private String showCallLog(TsdkCall call) {
+ TsdkCallInfo info = call.getCallInfo();
+ StringBuilder sb = new StringBuilder();
+ sb.append(info.getCallId());
+ sb.append(info.getConfId());
+ sb.append(info.getIsFocus());
+ sb.append(info.getSipAccountId());
+ sb.append(info.getSsrcTableEnd());
+ sb.append(info.getCallState());
+ sb.append(info.getIsAutoAnswer());
+ sb.append(info.getPeerDisplayName());
+ sb.append(info.getPeerNumber());
+ sb.append(info.getReasonDescription());
+ return sb.toString();
+ }
+
+ @Override
+ public void onEvtCallEnded(TsdkCall call) {
+ LogUtils.e("通话结束" + call);
+ runOnUiThread(() -> {
+ sendNotifyLogout();
+ mFullScreenDialog.cancel();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ });
+ }
+
+ @Override
+ public void onEvtCallConnected(TsdkCall call) {
+ LogUtils.e("通话连接上" + call);
+ runOnUiThread(() -> mFullScreenDialog.cancel());
+ if (call.getCallInfo().getIsFocus() == 1) {
+ CallMgrV2.getInstance().handleCallConnected(call);
+ } else {
+ CallInfo callInfo = CallMgrV2.getInstance().getCallInfo(call);
+ Intent intent = new Intent(mActivity, SponsorMeetingActivity.class);
+ intent.putExtra(Constant.DIAL, Constant.DIAL);
+ intent.putExtra(Constant.CALL_INFO, callInfo);
+ intent.putExtra(Constant.CALLED_NAME, call.getCallInfo().getPeerDisplayName());
+ intent.putExtra(Constant.VOICE_CALL_NUM, call.getCallInfo().getPeerNumber());
+ startActivity(intent);
+ }
+ }
+
+ @Override
+ public void onFailed(int result, String msg) {
+ LogUtils.e("呼叫失败+=" + msg + result);
+ runOnUiThread(() -> {
+ sendNotifyLogout();
+ LoginMangerV2.getInstance().logout();
+ mFullScreenDialog.cancel();
+ });
+ }
+
+ @Override
+ public void onNumIsEmpty() {
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ sendNotifyLogout();
+ ToastUtils.show("对方暂无号码");
+ }
+
+ @Override
+ public void onNetError() {
+ sendNotifyLogout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ DialogUtil.tipsDialog(mActivity, getString(R.string.cloudLink_contacts_networkError),
+ getString(R.string.cloudLink_meeting_iKnow));
+ }
+
+ @Override
+ public void onStartCall() {
+ LogUtils.e("拨打开始");
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ View view = View.inflate(this, R.layout.start_call, null);
+ view.findViewById(R.id.iv_center_red_call).setVisibility(View.VISIBLE);
+ view.findViewById(R.id.tv_name).setVisibility(View.VISIBLE);
+ ImageView ivIcon = view.findViewById(R.id.bg_icon);
+// val index = FirstLetterUtil.getFirstLetter(stv_name.text?.toString()).toLowerCase()[0].toInt() * 10
+ ivIcon.setImageResource(R.drawable.profile_image_bg);
+// bg.setImageLevel(index)
+ TextView tvName = view.findViewById(R.id.tv_name);
+ tvName.setText(mToName);
+ TextView method = view.findViewById(R.id.tv_method);
+ method.setText(getString(R.string.cloudLink_meeting_callings));
+ mFullScreenDialog.setCancelable(false);
+ view.findViewById(R.id.iv_center_red_call).setOnClickListener(v -> {
+ mFullScreenDialog.cancel();
+ if (null != mCall) {
+ CallMgrV2.getInstance().endCall(mCall.endCall());
+ }
+ });
+ mFullScreenDialog.setContentView(view);
+ if (mFullScreenDialog.isShowing()) {
+ return;
+ }
+ mFullScreenDialog.show();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+ switch (broadcastName) {
+ case BroadcastConstant.ACTION_LOGIN_SUCCESS:
+ handlerLoginSuccess(obj);
+ break;
+ case BroadcastConstant.ACTION_LOGIN_FAILED:
+ handlerLoginFailed(obj);
+ break;
+ case BroadcastConstant.ACTION_AUTH_FAILED:
+ handlerAuthFailed(obj);
+ break;
+ case BroadcastConstant.ACTION_FIRST_CHANGE_PWD:
+ // 首次登录弹出修改密码框
+ handlerLoginSuccess(obj);
+// showChangePwDialog();
+ break;
+ case BroadcastConstant.ACTION_CALL_END:
+ // 匿名入会离开会议
+ EncryptedSPTool.putBoolean(Constant.JOIN_MEETING_NOLOGIN_ING, false);
+ this.handlerCallEnd(obj);
+ break;
+ case BroadcastConstant.ACTION_GET_TEMP_USER_RESULT:
+ // 获取用于匿名方式加入会议的临时用户结果通知
+ TsdkCommonResult result = (TsdkCommonResult) obj;
+ switch (result.getResult()) {
+ case 0:
+ EncryptedSPTool.putBoolean(Constant.JOIN_MEETING_NOLOGIN_ING, true);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554498:
+ ToastUtils.show(R.string.cloudLink_meeting_joinMeeting_protocol_error);
+ break;
+ default:
+ ToastUtils.show(R.string.cloudLink_meeting_join_503);
+ break;
+ }
+// getView().closeConferenceShowLoading();
+ break;
+ case BroadcastConstant.ACTION_NET_WORK_IS_CONNECT:
+ //网络变化
+ boolean isConnect = (boolean) obj;
+// getView().displayNetChangeStatus(isConnect);
+ ToastUtils.show("网络变化");
+ break;
+ case BroadcastConstant.ACTION_CLOSE_ACTIVITY:
+ //关闭页面
+// getView().closeActivity();
+ break;
+ case BroadcastConstant.ACTION_CONF_CALL_FAILED:
+// getView().dismissLoading();
+ if (null != obj && (int) obj == TSDKErrorConstant.TSDK_AUTHENTICATION_ERROR_CODE) {
+ ToastUtils.show(R.string.cloudLink_login_auth_Fail);
+ } else if (null != obj && (int) obj == TSDKErrorConstant.TSDK_NET_ERROR_CODE) {
+ // 网络异常
+ ToastUtils.show(R.string.cloudLink_contacts_networkError);
+ } else {
+ ToastUtils.show(R.string.cloudLink_meeting_joinMeetingFail);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void handlerCallEnd(Object obj) {
+ runOnUiThread(() -> actionCallEnd(obj));
+ }
+
+ /**
+ * 处理登录成功
+ *
+ * @param obj 状态值
+ */
+ private void handlerLoginSuccess(Object obj) {
+ this.loginSuccess();
+ int number = Integer.parseInt((String) obj);
+ if (com.tengshisoft.chatmodule.hwclud.utils.UIUtil.isService3()) {
+ if (number == 0) {
+ ToastUtils.show(getString(R.string.cloudLink_login_pwd_expire));
+ } else if (number != ConstantsV2.LOGIN_STATUS_CODE_255) {
+ ToastUtils.show(getString(R.string.cloudLink_login_pwd_data)
+ + number + getString(R.string.cloudLink_login_pwd_data_2));
+ }
+ }
+ }
+
+ /**
+ * 通知对方退出登录
+ */
+ private void sendNotifyLogout() {
+ if (isRunning()) {
+ MessageBean bean = buildMsgBean("", mTo, UserLgUtils.getUserId(), UserLgUtils.getUserId(), MsgTypeStateEnum.MSG_TO_OTHER_IMG, "退出登录", StatusCode.MSG_TYPE_NOTIFY_LOGOUT);
+ Intent intent = new Intent();
+ intent.setAction(PathConfig.ACTION_MSG_CHAT_SEND);
+ intent.putExtra("bean", bean);
+ sendBroadcast(intent);
+ }
+ }
+
+ public void showChangePwDialog() {
+// CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(this);
+// cloudLinkDialog.setOnlyYes(true);
+// cloudLinkDialog.setYes(getString(R.string.cloudLink_sure), null, () -> {
+// Intent intent = new Intent(mActivity,
+// FirstChangePwdActivityV2.class);
+// intent.putExtra(FirstChangePwdActivityV2.USER_NAME, etAccount.getText().toString());
+// intent.putExtra(FirstChangePwdActivityV2.PASS_WORD, etPassword.getText().toString());
+// startActivityForResult(intent, CODE_CHANGE_PW);
+// cloudLinkDialog.dismiss();
+// });
+// cloudLinkDialog.setStr_message(getString(R.string.cloudLink_login_firstChangePwd), null);
+// cloudLinkDialog.show();
+ }
+
+ /**
+ * 处理登录失败
+ *
+ * @param obj 状态值
+ */
+ private void handlerLoginFailed(Object obj) {
+ String str = (String) obj;
+ int code = Integer.parseInt(str.split("-")[0]);
+ String index = str.split("-")[1];
+ String msgHint = "";
+ switch (code) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331749:
+ // 服务器返回禁止
+// if (LoginActivityV2.isFirstChangePwd) {
+// msgHint = getString(R.string.cloudLink_login_changePwdSuccessToLogin);
+// LoginActivityV2.isFirstChangePwd = false;
+// } else {
+ msgHint = getString(R.string.cloudLink_login_loginPassError);
+// }
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331659:
+ // 网络接入错误
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331754:
+ // 请求超时
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554441:
+ // 超时
+ msgHint = getString(R.string.cloudLink_login_loginTimeOut);
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331762:
+ // 临时失效
+ msgHint = getString(R.string.cloudLink_login_loginRepeat);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554448:
+ // 服务器异常
+ msgHint = getString(R.string.cloudLink_login_serviceError);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554449:
+ // 账号被锁定
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554467:
+ // 用户已被锁定
+ msgHint = getString(R.string.cloudLink_login_accountLocked);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554457:
+ // 老密码错误
+ if (index.equals(ConstantsV2.LOGIN_STATUS_CODE_0)) {
+ msgHint = getString(R.string.cloudLink_login_accountLocked);
+ }
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554454:
+ // 查询服务器地址失败
+ msgHint = getString(R.string.cloudLink_login_searchServerFail);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554466:
+ // 用户名或者密码错误
+ if (index.equals(ConstantsV2.LOGIN_STATUS_CODE_0)) {
+ msgHint = getString(R.string.cloudLink_mine_loginFailZero);
+ } else {
+ msgHint = getString(R.string.cloudLink_mine_loginFailIndex1) +
+ index + getString(R.string.cloudLink_mine_loginFailIndex2);
+ }
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554482:
+ // 账号未激活
+ msgHint = getString(R.string.cloudLink_login_err_32);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554444:
+ // DNS解析异常
+ msgHint = getString(R.string.cloudLink_dnsError);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554487:
+ // 账号已过期
+ msgHint = getString(R.string.cloudLink_login_error_2037);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554470:
+ // 网络异常
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331817:
+ // SIP TCP建立失败
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554446:
+ // 鉴权失败
+ default:
+ msgHint = getString(R.string.cloudLink_login_loginFail);
+ break;
+ }
+ if (!TextUtils.isEmpty(msgHint)) {
+ CloudLinkDialog dialogHint = new CloudLinkDialog(mActivity);
+ dialogHint.setOnlyYes(true);
+ dialogHint.setYes(getString(R.string.cloudLink_sure), null, dialogHint::dismiss);
+ dialogHint.setStr_message(msgHint, null);
+ dialogHint.show();
+ }
+ }
+
+ /**
+ * 处理认证失败
+ *
+ * @param obj 状态值
+ */
+ private void handlerAuthFailed(Object obj) {
+ int errCode = (int) obj;
+ String hintMsg;
+ switch (errCode) {
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554444:
+ // DNS解析异常
+ hintMsg = getString(R.string.cloudLink_dnsError);
+ break;
+ case TSDKErrorConstant.LOGIN_ERROR_CODE_33554470:
+ // 网络异常
+ default:
+ hintMsg = getString(R.string.cloudLink_login_loginFail);
+ break;
+ }
+ if (!TextUtils.isEmpty(hintMsg)) {
+ CloudLinkDialog hintDialog = new CloudLinkDialog(mActivity);
+ hintDialog.setOnlyYes(true);
+ hintDialog.setYes(getString(R.string.cloudLink_sure), null,
+ hintDialog::dismiss);
+ hintDialog.setStr_message(hintMsg, null);
+ hintDialog.show();
+ }
+ }
+
+ /**
+ * 发送让对方登录的消息
+ */
+ private void sendNotifyLogin() {
+ Intent intent = new Intent();
+ MessageBean msgBean = buildMsgBean("", mTo, UserLgUtils.getUserId(), UserLgUtils.getUserId(), MsgTypeStateEnum.MSG_TO_OTHER_VOICE, "通话消息", StatusCode.MSG_TYPE_NOTIFY_LOGIN);
+ intent.putExtra("bean", msgBean);
+ intent.setAction(PathConfig.ACTION_NOTIFY_CONTACT_LOGIN);
+ sendBroadcast(intent);
+ }
+
private class MsgReceiver extends BroadcastReceiver {
+ @RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (PathConfig.ACTION_MSG_CHAT_REFRESH.equals(action)) {
- String id = intent.getStringExtra("id");
- //刷新消息状态
- refreshMsgBean(id, MsgTypeStateEnum.MSG_SEND_FAIL);
- } else if (PathConfig.ACTION_MSG_CHAT.equals(action)) {
- //收到信息
- String body = intent.getStringExtra(StatusCode.KEY_MSG_FROM);
- fromSocketMsg(body);
- } else if (PathConfig.ACTION_MSG_SOCKET_FAIL.equals(action)) {
- //socket连接失败
- //判断是否存在正在发送的信息,更改状态为发送失败
+ if (!TextUtils.isEmpty(action)) {
+ switch (action) {
+ case PathConfig.ACTION_MSG_CHAT_REFRESH://文本消息用户不在线
+ String id = intent.getStringExtra("id");
+ //刷新消息状态
+ refreshMsgBean(id, MsgTypeStateEnum.MSG_SEND_FAIL);
+ break;
+ case PathConfig.ACTION_USER_OFFLINE:
+ ToastUtils.show("对方不在线,无法通话");
+ LoginMangerV2.getInstance().logout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ mMsgHandler.removeMessages(10086);
+ break;
+ case PathConfig.ACTION_MSG_CHAT:
+ //收到信息
+ String body = intent.getStringExtra(StatusCode.KEY_MSG_FROM);
+ fromSocketMsg(body);
+ break;
+ case PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_SUCCESS:
+ //TODO 对方登录成功,获取返回的号码, 进行拨打电话
+ LogUtils.e("对方登录成功");
+ mToUserPhoneNumber = intent.getStringExtra("number");
+ doCall();
+ break;
+ case PathConfig.ACTION_NOTIFY_CONTACT_LOGIN_FAIL://通话时对方登录失败
+ ToastUtils.show("对方通话出现异常,请稍后重试");
+ LoginMangerV2.getInstance().logout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ break;
+ case PathConfig.ACTION_CALL_OTHER_NO_SUPPORT://对方手机不支持通话
+ ToastUtils.show("对方手机不支持通话");
+ LoginMangerV2.getInstance().logout();
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+ }
+ break;
+ case PathConfig.ACTION_MSG_SOCKET_FAIL://Socket连接失败
+ break;
+ }
}
}
}
@@ -828,13 +1447,22 @@ public class ChatActivity extends BaseActivity {
@Override
public void handleMessage(@NonNull Message msg) {
String id = (String) msg.obj;
- for (int i = 0; i < mBeanList.size(); i++) {
- if (mBeanList.get(i).getId().equals(id)) {
- mBeanList.get(i).setSendState(MsgTypeStateEnum.MSG_SEND_SUCCESS);
- saveDataToDatabase(mBeanList.get(i));
+ if ("login".equals(id)) {
+ //语音视频通话消息 3秒钟对方为返回登录成功消息 定义为登录失败
+ ToastUtils.show("对方不在线,无法通话");
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
}
+ } else {
+ //聊天消息
+ for (int i = 0; i < mBeanList.size(); i++) {
+ if (mBeanList.get(i).getId().equals(id)) {
+ mBeanList.get(i).setSendState(MsgTypeStateEnum.MSG_SEND_SUCCESS);
+ saveDataToDatabase(mBeanList.get(i));
+ }
+ }
+ mMsgAdapter.setData(mBeanList);
}
- mMsgAdapter.setData(mBeanList);
}
}
@@ -849,6 +1477,8 @@ public class ChatActivity extends BaseActivity {
if (mMsgHandler != null) {
mMsgHandler.removeCallbacksAndMessages(null);
}
+ LoginMangerV2.getInstance().logout();
+ LocalBroadcast.getInstance().unRegisterBroadcast(this, mActions);
BaseAppContext.getInstance().setUsername("");
BaseAppContext.getInstance().setOtherUserId("");
super.onDestroy();
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/InvitedPointCallActivity.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/InvitedPointCallActivity.java
new file mode 100644
index 0000000..08868f6
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/InvitedPointCallActivity.java
@@ -0,0 +1,1705 @@
+package com.tengshisoft.chatmodule.activity;
+
+import android.annotation.SuppressLint;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.allen.library.SuperTextView;
+import com.bigkoo.pickerview.builder.OptionsPickerBuilder;
+import com.bigkoo.pickerview.view.OptionsPickerView;
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkConfRole;
+import com.huawei.ecterminalsdk.base.TsdkLdapContactsInfo;
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute;
+import com.huawei.ecterminalsdk.base.TsdkNotifyHandUpAttendee;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtConfBaseInfoInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtSearchLdapContactsResult;
+import com.huawei.ecterminalsdk.base.TsdkPageCookieData;
+import com.huawei.ecterminalsdk.base.TsdkSearchLdapContactsResult;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.R2;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.ConferenceConstant;
+import com.tengshisoft.chatmodule.beans.ControlOperationsResults;
+import com.tengshisoft.chatmodule.beans.EmptyFactory;
+import com.tengshisoft.chatmodule.beans.IntentConstant;
+import com.tengshisoft.chatmodule.beans.MeetingTitleInfoBean;
+import com.tengshisoft.chatmodule.beans.TSDKErrorConstant;
+import com.tengshisoft.chatmodule.hwclud.controller.IMeetingSignalController;
+import com.tengshisoft.chatmodule.hwclud.controller.IMeetingTitleController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingSignalController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingTitleBarController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.SwitchAudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.ui.BaseConfContract;
+import com.tengshisoft.chatmodule.hwclud.ui.BaseMvpActivityV2;
+import com.tengshisoft.chatmodule.hwclud.ui.BasePresenterV2;
+import com.tengshisoft.chatmodule.hwclud.ui.CloudLinkDialog;
+import com.tengshisoft.chatmodule.hwclud.ui.DialogUtil;
+import com.tengshisoft.chatmodule.hwclud.ui.ListPopWindow;
+import com.tengshisoft.chatmodule.hwclud.ui.MeetingSubtitleView;
+import com.tengshisoft.chatmodule.hwclud.ui.VoiceConfPresenter;
+import com.tengshisoft.chatmodule.hwclud.utils.AppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.DateUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.FirstLetterUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.PhoneUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Platform;
+import com.tengshisoft.chatmodule.hwclud.utils.TimeSelectUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UiUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.mic.RecordAudioFactory;
+import com.tengshisoft.chatmodule.hwclud.utils.phone_state.PhoneStateFactory;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConfConstant;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.camera.core.impl.CameraFactory;
+import androidx.core.content.ContextCompat;
+import butterknife.BindView;
+import butterknife.OnClick;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.CALL_UPDATE_VOICE;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.CONF_VIDEO_COMING;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.POINT_CALL;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.POINT_CALLED;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.POINT_CONNECTED;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.POINT_VIDEOED;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.VOICE_ACCESS_CONF_ING;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.VOICE_CONF_COMING;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.VOICE_CONF_ING;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.VOICE_LIST_JOIN;
+import static com.tengshisoft.chatmodule.hwclud.utils.DeviceManager.isSilentState;
+
+/**
+ * 点对点呼叫页面
+ * 被邀请页面
+ *
+ * 语音会议页面
+ * 点对点呼叫
+ * 会议通知页面
+ */
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class InvitedPointCallActivity extends BaseMvpActivityV2 implements BaseConfContract.BaseConfView {
+
+ @BindView(R2.id.tv_name)
+ TextView tvName;
+ @BindView(R2.id.tv_method)
+ TextView tvMethod;
+ @BindView(R2.id.iv_left_red_called)
+ ImageView ivLeftRedCalled;
+ @BindView(R2.id.iv_right_green_called)
+ ImageView ivRightGreenCalled;
+ @BindView(R2.id.tv_speaker)
+ TextView tvSpeaker;
+ @BindView(R2.id.tv_mute)
+ TextView tvMute;
+ @BindView(R2.id.tv_participants)
+ TextView tvParticipants;
+ @BindView(R2.id.tv_more)
+ TextView tvMore;
+ @BindView(R2.id.tv_add)
+ TextView tvAdd;
+ @BindView(R2.id.tv_switch_voice)
+ TextView tvSwitchVoice;
+ @BindView(R2.id.call_tool)
+ LinearLayout callTool;
+ @BindView(R2.id.bg_icon)
+ ImageView fl_bg;
+ @BindView(R2.id.rl_back)
+ RelativeLayout rl_back;
+ @BindView(R2.id.ll_dial)
+ RelativeLayout llDial;
+ @BindView(R2.id.rl_meeting_subtitle)
+ MeetingSubtitleView mSubtitleView;
+
+ private VoiceConfPresenter mPresenter;
+ private int type;
+ protected int mConfToCallHandle;
+ private int mCallID;
+ long currentMillers = -1;
+ String Minute;
+ String seconds;
+ private Disposable subscribe;
+ private List memberList;
+ private CallInfo callInfo;
+ String CONF_ID = "";
+ String id = "";
+ private String Hours;
+ private boolean isFocus = false;
+ private static final String TAG = InvitedPointCallActivity.class.getSimpleName();
+ // 会控
+ private IMeetingSignalController signalController;
+ // .... 等等
+ TsdkLdapContactsInfo tlci;
+ private String username = ""; //通过id匹配通讯录获取到的name
+ public ArrayList pageCookie;
+ private boolean isJumpSpon = false;
+ private boolean isInMeeting = false; // 音频会议中或点呼语音接通
+ CloudLinkDialog LinkDialog;
+ /**
+ * 用来做标记位,因为从小窗口回来isMiniBack一直是true
+ */
+ private boolean flag = true;
+ IMeetingTitleController titleController;
+ private boolean isMiniback = false;
+ // 获取手机当前音量值
+ private int voiceNum;
+ boolean isConnect = false;
+
+ private int index = 0;
+ private boolean isFirstMic = false;
+ private boolean isInConf = false; // 是否在会议界面
+ private ListPopWindow mListPopWindow;
+ private String[] broadcastNames = new String[]{
+ Constant.ACTION_HEADSET_PLUG,
+ BroadcastConstant.ACTION_CALL_DEVICES_STATUS_CHANGE,
+ BroadcastConstant.ACTION_CALL_ROUTE_CHANGE,
+ BroadcastConstant.ACTION_CHECKINRESULT,
+ BroadcastConstant.ACTION_CONF_CALL_CONNECTED,
+ BroadcastConstant.ACTION_CALL_CONNECTED,
+ BroadcastConstant.ACTION_EVT_LOCK_SCREEN_NOTIFICATION,
+ BroadcastConstant.ACTION_NO_STREAM_DURATION,
+ // 网络状况变化
+ BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL,
+ BroadcastConstant.ACTION_BACK_TO_DESK,
+ BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI,
+ BroadcastConstant.ACTION_CALL_STATE_OFFHOOK,
+ BroadcastConstant.ACTION_CALL_STATE_RINGING,
+ BroadcastConstant.ACTION_CALL_STATE_IDLE,
+ BroadcastConstant.ACTION_CONF_BASE_INFO,
+ BroadcastConstant.ACTION_GET_SUBJECT_SUCCEED,
+ BroadcastConstant.ACTION_REQUEST_CHAIRMAN_RESULT, // 申请主席
+ BroadcastConstant.ACTION_HAS_HANDUP_MEMBER,
+ BroadcastConstant.ACTION_CALL_END,
+ BroadcastConstant.ACTION_CHANGE_SC,
+ BroadcastConstant.ACTION_MEETING_SUBTITLE_SWITCH,
+ BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_ENABLE,
+ BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_STATUS,
+ BroadcastConstant.ACTION_CONF_CALL_FAILED,
+ Constant.MEETING_DESTORY
+ };
+
+ /**
+ * 网络差,最后一次提示时间
+ */
+ private long mLastNetLevelToastTime;
+ private boolean mIsConfSubtitleEnable;
+ private boolean mIsSubtitleEnable;
+ private boolean isConfResultError = false;
+ LocalBroadcastReceiver receiver = (broadcastName, obj) -> {
+ switch (broadcastName) {
+ case Constant.MEETING_DESTORY:
+ // 处理会控失败后无callEnd事件,点击无法挂断。
+ if (AppUtil.isActivityTop(InvitedPointCallActivity.class, BaseAppContext.getInstance()) && !isConfResultError) {
+ finish();
+ }
+ break;
+ case BroadcastConstant.ACTION_CONF_CALL_FAILED:
+ // 建链失败,停止铃音
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ isConfResultError = true;
+ runOnUiThread(() -> showConfFailed((int) obj));
+ break;
+ case BroadcastConstant.ACTION_CALL_ROUTE_CHANGE:
+ runOnUiThread(() -> {
+ LogUtil.d(TAG, "ACTION_CALL_ROUTE_CHANGE" + obj);
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ });
+ break;
+ case BroadcastConstant.ACTION_CONF_CALL_CONNECTED:
+ case BroadcastConstant.ACTION_CALL_CONNECTED:
+ //修改入会流程,从handleCallConnected(TsdkCall call)跳转界面,参数改变做兼容处理。
+ if (obj != null) {
+ if (obj instanceof CallInfo) {
+ //点呼 音视频
+ finish();
+ } else if (obj instanceof TsdkConference) {
+ // 原会议会议
+ if (((TsdkConference) obj).getCall().getCallInfo().getIsVideoCall() != 1) {
+ return;
+ } else {
+ finish();
+ }
+ } else if (obj instanceof TsdkCall) {
+ //修改参数之后的语音逻辑
+ if (((TsdkCall) obj).getCallInfo().getIsFocus() == 1
+ && ((TsdkCall) obj).getCallInfo().getIsVideoCall() != 1) {
+ return;
+ } else {
+ finish();
+ }
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_EVT_LOCK_SCREEN_NOTIFICATION:
+ // 锁屏处理
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ LogUtil.zzz(TAG, "onReceive()", "lock screen");
+ break;
+ case BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL:
+ int netLevel = (int) obj;
+ LogUtil.zzz("网络状态 监听", netLevel);
+ if (titleController != null) {
+ titleController.updateNetworkQuality(netLevel, false);
+ }
+ DialogUtil.updateNetworkQuality(netLevel);
+ MeetingTitleBarController.mCurrentNetLevel = netLevel;
+ boolean isToast = (System.currentTimeMillis() - mLastNetLevelToastTime) > Constant.NET_LEVEL_MIN_TOAST_INTERVAL;
+ if (isToast && netLevel <= Constant.NET_LEVEL_MIN_TOAST) {
+ mLastNetLevelToastTime = System.currentTimeMillis();
+ runOnUiThread(() -> ToastUtils.show(getResources().getString(R.string.cloudLink_net_work_weak)));
+ }
+ break;
+ case BroadcastConstant.ACTION_NO_STREAM_DURATION: // 无码流事件
+ int time = (int) obj;
+ LogUtil.zzz("无码流--音頻:", time, "CallID:" + mCallID);
+ if (time >= 30) {
+ runOnUiThread(() -> {
+ if (!isFocus) { //点对点呼叫挂断
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, true);
+ CallMgrV2.getInstance().endCall(mCallID);
+ } else {
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, true);
+ mPresenter.leaveConf();
+ }
+ });
+ } else if (time % 10 == 0) {
+ runOnUiThread(() -> ToastUtils.show(getResources().getString(R.string.cloudLink_no_stream_duration_time)));
+ }
+ break;
+ case BroadcastConstant.ACTION_CHECKINRESULT:
+ runOnUiThread(() -> ToastUtils.show(getResources().getString(R.string.cloudLink_meeting_confCheckInSuccess)));
+ break;
+ case BroadcastConstant.ACTION_BACK_TO_DESK:
+ if (isConnect && MeetingController.getInstance().isJoinConfSuccess()) {
+ if (!MeetingController.getInstance().isAux()) {
+ minimize();
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI: // 有噪音
+ runOnUiThread(() -> {
+ closeVoiceUI((Boolean) obj);
+ });
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_IDLE:
+ if (isConnect && MeetingMgrV2.getInstance().getCurrentConference() != null) {
+ endMobileCall();
+ }
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_RINGING:
+ if (isConnect && MeetingMgrV2.getInstance().getCurrentConference() != null) {
+ receivedMobileCall(false);
+ }
+ break;
+ case BroadcastConstant.ACTION_CONF_BASE_INFO: // 加入会议结果,刷新会议ID
+ TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd = (TsdkOnEvtConfBaseInfoInd) obj;
+ updataBaseInfo(tsdkOnEvtConfBaseInfoInd);
+ break;
+ case BroadcastConstant.ACTION_GET_SUBJECT_SUCCEED:
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ String subject = (String) obj;
+ int index = FirstLetterUtil.getFirstLetter(subject).toLowerCase().charAt(0) * 10;
+ fl_bg.setImageResource(R.drawable.profile_image_bg);
+ fl_bg.setImageLevel(index);
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_REQUEST_CHAIRMAN_RESULT:
+ ControlOperationsResults.ChairmanResult requestChairmanResult = (ControlOperationsResults.ChairmanResult) obj;
+ if (requestChairmanResult.getResult() != 0) {
+ handleChairmanResult(Constant.ParticipantEvent.REQUEST_CHAIRMAN_FAILED, requestChairmanResult.getName(), requestChairmanResult.getResult());
+ return;
+ }
+ handleChairmanResult(Constant.ParticipantEvent.REQUEST_CHAIRMAN_SUCCESS, requestChairmanResult.getName(), requestChairmanResult.getResult());
+ MeetingController.getInstance().setChairman(true);
+ break;
+ case BroadcastConstant.ACTION_HAS_HANDUP_MEMBER:
+ if (obj instanceof TsdkNotifyHandUpAttendee) {
+ runOnUiThread(() -> {
+ if (((TsdkNotifyHandUpAttendee) obj).getStatusInfo().getIsSelf() != 1 && ((TsdkNotifyHandUpAttendee) obj).getStatusInfo().getIsHandup() == 1) {
+ ToastUtils.show(((TsdkNotifyHandUpAttendee) obj).getBaseInfo().getDisplayName() + getString(R.string.cloudLink_meeting_raiseHanding));
+ }
+ });
+ }
+ break;
+ case BroadcastConstant.ACTION_CALL_END:
+ finish();
+ break;
+ case BroadcastConstant.ACTION_CHANGE_SC:
+ runOnUiThread(() -> {
+ if (obj instanceof TsdkOnEvtDeviceStateNotify.Param) {
+ mPresenter.syncState((TsdkOnEvtDeviceStateNotify.Param) obj);
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_NEED_UPDATE_MUTE_STATE:
+ runOnUiThread(() -> {
+ Member member = (Member) obj;
+ if (MeetingController.getInstance().getMuteState() != member.isMute()) {
+ MeetingMgrV2.getInstance().muteAttendee(member, MeetingController.getInstance().getMuteState());
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_MEETING_SUBTITLE_SWITCH:
+ int result = (int) obj;
+ LogUtil.zzz(TAG, "ACTION_EVT_MEETING_SUBTITLE_STATUS result = " + result);
+ if (result == 0) {
+ mIsSubtitleEnable = !mIsSubtitleEnable;
+ }
+ runOnUiThread(this::updateSubtitleView);
+ break;
+ case BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_ENABLE:
+ int enable = (int) obj;
+ LogUtil.zzz(TAG, "ACTION_EVT_MEETING_SUBTITLE_ENABLE enable = " + enable);
+ mIsConfSubtitleEnable = enable == 1;
+ break;
+ case BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_STATUS:
+ if (!mIsConfSubtitleEnable) {
+ return;
+ }
+ int status = (int) obj;
+ LogUtil.zzz(TAG, "ACTION_EVT_MEETING_SUBTITLE_STATUS status = " + status);
+ boolean isSubtitleEnable = status == 1;
+ if (isSubtitleEnable == mIsSubtitleEnable) {
+ return;
+ }
+ mIsSubtitleEnable = isSubtitleEnable;
+ runOnUiThread(this::updateSubtitleView);
+ break;
+ default:
+ break;
+ }
+
+ };
+
+ private void updateSubtitleView() {
+ if (mSubtitleView != null) {
+ mSubtitleView.setSubtitleEnable(mIsSubtitleEnable);
+ }
+ }
+
+ private void showConfFailed(int result) {
+ String msg = "";
+ switch (result) {
+ case TSDKErrorConstant.TSDK_JOIN_CONF_CERTIFICATE_FAILED:
+ msg = getString(R.string.cloudLink_conf_certificate_failed);
+ break;
+ case TSDKErrorConstant.TSDK_JOIN_CONF_BFCP_FAILED:
+ msg = getString(R.string.cloudLink_conf_bfcp_failed);
+ break;
+ case TSDKErrorConstant.TSDK_JOIN_CONF_CONTROL_FAILED:
+ msg = getString(R.string.cloudLink_conf_control_failed);
+ break;
+ default:
+ break;
+ }
+ DialogUtil.tipsDialog(this, msg, getString(R.string.cloudLink_sure),
+ () -> finish());
+ }
+
+ /**
+ * 设置音频路由
+ *
+ * @param audioRoute 0 默认
+ * 1 扬声器
+ */
+ private void setAudioRoute(TsdkMobileAuidoRoute audioRoute) {
+ if (SwitchAudioRouteManager.Companion.getInstance().setAudioRoute(audioRoute)) {
+ SwitchAudioRouteManager.Companion.getInstance().setUserSettingAudioRoute(audioRoute);
+ SwitchAudioRouteManager.Companion.getInstance().setSettingAudioRoute(true);
+ }
+ }
+
+ @Override
+ public void finish() {
+ LogUtil.UIAction("pointCall:finish()");
+ super.finish();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_point_call;
+ }
+
+ @Override
+ protected void initIntent() {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED //锁屏显示
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD //解锁
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON //保持屏幕不息屏
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);//点亮屏幕
+ }
+
+ @Override
+ protected void initView() {
+ LogUtil.UIAction("pointCall:initView()");
+ EncryptedSPTool.putBoolean(Constant.JOIN_MEETING_NOLOGIN, false);
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ rl_back.setPadding(0, UIUtil.getStatusBarHeight(this), 0, 0);
+ mPresenter = new VoiceConfPresenter(this, this);
+ mPresenter.registerBroadcast();
+
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ setToolBar();
+ new Handler().postDelayed(() -> {
+ isMiniback = getIntent().getBooleanExtra("isMiniback", false);
+ if (isMiniback) {
+ initVoiceConfTitle();
+ callTool.setVisibility(View.VISIBLE);
+ oldTime = getIntent().getLongExtra("time", 0);
+ titleController.displayMeetingName(getIntent().getStringExtra("tvConfName"));
+ String tvConfName = getIntent().getStringExtra("tvConfName");
+ int index = FirstLetterUtil.getFirstLetter(tvConfName).toLowerCase().charAt(0) * 10;
+ fl_bg.setImageResource(R.drawable.profile_image_bg);
+ fl_bg.setImageLevel(index);
+ titleController.displayMeetingId(getIntent().getStringExtra("tvConfId"));
+ tvName.setText(getIntent().getStringExtra("tvName"));
+ updateView(Constant.UPDATE_CONF_CONNECT, false, MeetingMgrV2.getInstance().getCurrentConference());
+ if (PhoneUtil.isTelephonyCalling()) {
+ if (!isSilentState) {
+ updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ } else {
+ updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ }
+ }
+ TsdkConference tsdkConference = MeetingMgrV2.getInstance().getCurrentConference();
+ if (tsdkConference != null) {
+ String handle = tsdkConference.getHandle() + "";
+ MeetingMgrV2.getInstance().getConfNotification().onConfEventNotify(ConfConstant.CONF_EVENT.STATE_UPDATE, handle);
+ }
+
+ }, 200);
+
+ signalController = new MeetingSignalController();
+ signalController.initView(this);
+ signalController.setAuxChangeListener(new IMeetingSignalController.IAuxChangeListener() {
+ @Override
+ public boolean isAux() {
+ return false;
+ }
+
+ @Override
+ public boolean isAudio() {
+ return true;
+ }
+
+ @Override
+ public boolean isSvc() {
+ return false;
+ }
+ });
+ hideLoading(); //取消加载框
+ }
+
+ @Override
+ protected void initData() {
+ MeetingController.getInstance().setTempCallInfo(null);
+ CallInfo callInfo = (CallInfo) getIntent().getSerializableExtra("jumpToCall");
+ if (callInfo == null) {
+ type = getIntent().getIntExtra(Constant.VOICE_JOIN_TYPE, -1);
+ setData();
+ } else {
+ this.callInfo = callInfo;
+ type = POINT_CONNECTED;
+ isFocus = false;
+ updateView(CALL_UPDATE_VOICE, false, "");
+ tvParticipants.setVisibility(View.GONE);
+ callTool.setVisibility(View.VISIBLE);
+ ivLeftRedCalled.setVisibility(View.INVISIBLE);
+ ivRightGreenCalled.setVisibility(View.INVISIBLE);
+ mCallID = this.callInfo.getCallID();
+ LogUtil.zzz("音频", "initData callID", LogUtil.commonDisplay(mCallID + ""));
+ tvName.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ? callInfo.getPeerNumber() : callInfo.getPeerDisplayName());
+ }
+ voiceNum = CallMgrV2.getInstance().getMediaSpeakVolume();
+ mIsSubtitleEnable = getIntent().getBooleanExtra(Constant.MEETING_CAPTION_ENABLE, false);
+ mIsConfSubtitleEnable = getIntent().getBooleanExtra(Constant.MEETING_CONF_CAPTION_ENABLE, false);
+ boolean isSubtitleShowing = getIntent().getBooleanExtra(Constant.MEETING_CONF_CAPTION_SHOWING, false);
+ if (isSubtitleShowing) {
+ mSubtitleView.show();
+ }
+ }
+
+ @Override
+ protected BasePresenterV2 createPresenter() {
+ return null;
+ }
+
+ private void setData() {
+ Log.d(TAG, "setData: " + type);
+ switch (type) {
+ case VOICE_CONF_COMING://语音会议来电通知
+ playMusic();
+ this.callInfo = (CallInfo) getIntent().getSerializableExtra(Constant.CALL_INFO);
+ ivLeftRedCalled.setVisibility(View.VISIBLE);
+ ivRightGreenCalled.setVisibility(View.VISIBLE);
+ callTool.setVisibility(View.GONE);
+ tvName.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ?
+ callInfo.getPeerNumber() : UIUtil.splitString(callInfo.getPeerDisplayName()));
+ tvMethod.setText(R.string.cloudLink_meeting_wantToCallMeeting);
+ if ((null != this.callInfo.getConfID()) && (!this.callInfo.getConfID().equals(""))) {
+ mPresenter.setConfID(this.callInfo.getConfID());
+ mConfToCallHandle = Integer.parseInt(this.callInfo.getConfID());
+ }
+ mCallID = this.callInfo.getCallID();
+ LogUtil.zzz("音频", "语音会议来电通知 callID", LogUtil.commonDisplay(mCallID + ""));
+ callComing(callInfo);
+ break;
+ case CONF_VIDEO_COMING://视频会议来电通知
+ playMusic();
+ ivLeftRedCalled.setVisibility(View.VISIBLE);
+ ivRightGreenCalled.setVisibility(View.VISIBLE);
+ callTool.setVisibility(View.GONE);
+ ivRightGreenCalled.setImageResource(R.drawable.green_video);
+ tvSwitchVoice.setVisibility(View.VISIBLE);
+ callInfo = (CallInfo) getIntent().getSerializableExtra(Constant.CALL_INFO);
+ tvName.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ?
+ callInfo.getPeerNumber() : UIUtil.splitString(callInfo.getPeerDisplayName()));
+ tvMethod.setText(R.string.cloudLink_meeting_wantToVideoMeeting);
+ if ((null != callInfo.getConfID()) && (!callInfo.getConfID().equals(""))) {
+ mConfToCallHandle = Integer.parseInt(callInfo.getConfID());
+ }
+ mCallID = callInfo.getCallID();
+ LogUtil.zzz("音频", "视频会议来电通知 callID", LogUtil.commonDisplay(mCallID + ""));
+ callComing(callInfo);
+ break;
+
+ case VOICE_LIST_JOIN://语音会议加入
+ CallMgrV2.getInstance().answerCall(mCallID, false);
+ ToastUtils.show(getString(R.string.cloudLink_meeting_joinSuccess));
+ tvName.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ? callInfo.getPeerNumber() : callInfo.getPeerDisplayName());
+ break;
+ case VOICE_CONF_ING://真·语音会议进入页面
+ isInMeeting = true;
+ isFocus = true;
+ id = getIntent().getStringExtra(Constant.ID);
+ ivLeftRedCalled.setVisibility(View.INVISIBLE);
+ callTool.setVisibility(View.VISIBLE);
+ try {
+// titleController.displayMeetingName("ID: " +UIUtil.splitString(
+// this.callInfo.getPeerNumber().replace("*", " " + getString(R.string.cloudLink_meeting_password) + ":")));
+ if (id.contains("*")) {
+ titleController.displayMeetingId("ID: " + UIUtil.splitString(id.split("\\*")[0]));
+ } else {
+ titleController.displayMeetingId("ID: " + UIUtil.splitString(id));
+ }
+ } catch (NullPointerException e) {
+ LogUtil.e(LogUtil.CLOUNDLINK, "callInfo = null");
+ }
+ ivRightGreenCalled.setVisibility(View.INVISIBLE);
+ break;
+
+ case VOICE_ACCESS_CONF_ING://语音会议进入页面
+ isInMeeting = true;
+ isFocus = true;
+ CONF_ID = getIntent().getStringExtra(Constant.CONF_ID);
+ id = getIntent().getStringExtra(Constant.ID);
+ initVoiceConfTitle();
+ ivLeftRedCalled.setVisibility(View.INVISIBLE);
+ callTool.setVisibility(View.VISIBLE);
+ ivRightGreenCalled.setVisibility(View.INVISIBLE);
+ if (!TextUtils.isEmpty(id)) {
+// titleController.displayMeetingName("ID: " +UIUtil.splitString(id.replace("*", " " + getString(R.string.cloudLink_meeting_password) + ":")));
+ if (id.contains("*")) {
+ titleController.displayMeetingId("ID: " + UIUtil.splitString(id.split("\\*")[0]));
+ } else {
+ titleController.displayMeetingId("ID: " + UIUtil.splitString(id));
+ }
+ } else {
+ if (!TextUtils.isEmpty(CONF_ID)) {
+// titleController.displayMeetingName("ID: " +UIUtil.splitString(CONF_ID.replace("*", " " + getString(R.string.cloudLink_meeting_password) + ":")));
+ titleController.displayMeetingId("ID: " + UIUtil.splitString(CONF_ID));
+ }
+ }
+ mPresenter.setConfID(CONF_ID);
+ setTime();
+ break;
+
+ case POINT_CALL://点对点语音主叫
+ CallMgrV2.getInstance().startCall(getIntent().getStringExtra(Constant.VOICE_CALL_NUM), false,
+ TextUtils.isEmpty(getIntent().getStringExtra(Constant.CALLED_NAME)) ?
+ getIntent().getStringExtra(Constant.VOICE_CALL_NUM)
+ : getIntent().getStringExtra(Constant.CALLED_NAME));
+ tvParticipants.setVisibility(View.GONE);
+ tvName.setText(TextUtils.isEmpty(getIntent().getStringExtra(Constant.CALLED_NAME)) ?
+ getIntent().getStringExtra(Constant.VOICE_CALL_NUM)
+ : getIntent().getStringExtra(Constant.CALLED_NAME));
+ tvMethod.setText(R.string.cloudLink_meeting_calling);
+ break;
+ case POINT_CALLED://点对点语音被叫
+ playMusic();
+ callTool.setVisibility(View.GONE);
+ ivRightGreenCalled.setVisibility(View.VISIBLE);
+ ivLeftRedCalled.setVisibility(View.VISIBLE);
+ callInfo = (CallInfo) getIntent().getSerializableExtra(Constant.CALL_INFO);
+ tvName.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ? callInfo.getPeerNumber() : callInfo.getPeerDisplayName());
+ tvMethod.setText(R.string.cloudLink_meeting_wantToCall);
+ mCallID = callInfo.getCallID();
+ LogUtil.zzz("音频", "点对点语音被叫 callID", LogUtil.commonDisplay(mCallID + ""));
+ callComing(callInfo);
+ break;
+ case POINT_VIDEOED://点对点视频被叫
+ playMusic();
+ Log.d(TAG, "setData: 点对点视频被叫");
+ this.callInfo = (CallInfo) getIntent().getSerializableExtra(Constant.CALL_INFO);
+ tvName.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ? callInfo.getPeerNumber() : callInfo.getPeerDisplayName());
+ tvMethod.setText(R.string.cloudLink_meeting_wantToVideo);
+ ivLeftRedCalled.setVisibility(View.VISIBLE);
+ ivRightGreenCalled.setVisibility(View.VISIBLE);
+ callTool.setVisibility(View.GONE);
+ tvSwitchVoice.setVisibility(View.VISIBLE);
+ ivRightGreenCalled.setImageResource(R.drawable.video_accept);
+ mCallID = this.callInfo.getCallID();
+ LogUtil.zzz("音频", "点对点视频被叫 callID", LogUtil.commonDisplay(mCallID + ""));
+ callComing(callInfo);
+ break;
+ case POINT_CONNECTED://点对点语音已连接
+ isInMeeting = true;
+ isFocus = false;
+ updateView(CALL_UPDATE_VOICE, false, "");
+ tvParticipants.setVisibility(View.GONE);
+ callTool.setVisibility(View.VISIBLE);
+ ivLeftRedCalled.setVisibility(View.INVISIBLE);
+ ivRightGreenCalled.setVisibility(View.INVISIBLE);
+ mCallID = this.callInfo.getCallID();
+ LogUtil.zzz("音频", "点对点语音已连接 callID", LogUtil.commonDisplay(mCallID + ""));
+ setTime();
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * 来电
+ */
+ private void callComing(CallInfo callInfo) {
+ if (callInfo != null) {
+ String disName = TextUtils.isEmpty(callInfo.getPeerDisplayName()) ? callInfo.getPeerNumber() : callInfo.getPeerDisplayName();
+ LogUtil.zzz("callComing", "来电", "disName=" + LogUtil.commonDisplay(disName));
+ if (!TextUtils.isEmpty(disName)) {
+ int index = FirstLetterUtil.getFirstLetter(disName).toLowerCase().charAt(0) * 10;
+ fl_bg.setImageResource(R.drawable.profile_image_bg);
+ fl_bg.setImageLevel(index);
+ }
+ } else {
+ LogUtil.zzz("callComing", "来电", "callInfo= null");
+ }
+
+ }
+
+ long oldTime = 0;
+
+ private void setTime() {
+ if (subscribe != null) {
+ return;
+ }
+ subscribe = Observable.interval(0, 1, TimeUnit.SECONDS).subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(aLong -> {
+ aLong += oldTime;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+ if (aLong < 0) {
+ if (type == POINT_CONNECTED) {
+ tvMethod.setText(getString(R.string.cloudLink_meeting_callWaiting) + " 00:00:00");
+ } else {
+ tvMethod.setText(getString(R.string.cloudLink_meeting_confWaiting) + " 00:00:00");
+ }
+ } else {
+ minute = (int) (aLong / 60);
+ if (minute < 60) {
+ second = (int) (aLong % 60);
+ if (type == POINT_CONNECTED) {
+ tvMethod.setText(getString(R.string.cloudLink_meeting_callWaiting) + " 00:" + unitFormat(minute) + ":" + unitFormat(second));
+ } else {
+
+ tvMethod.setText(getString(R.string.cloudLink_meeting_confWaiting) + " 00:" + unitFormat(minute) + ":" + unitFormat(second));
+ }
+ } else {
+ hour = minute / 60;
+ if (hour > 99) {
+ if (type == POINT_CONNECTED) {
+ tvMethod.setText(getString(R.string.cloudLink_meeting_callWaiting) + "99:59:59");
+ } else {
+
+ tvMethod.setText(getString(R.string.cloudLink_meeting_confWaiting) + "99:59:59");
+ }
+ }
+ minute = minute % 60;
+ second = (int) (aLong - hour * 3600 - minute * 60);
+
+ if (type == POINT_CONNECTED) {
+ tvMethod.setText(getString(R.string.cloudLink_meeting_callWaiting) + " " + unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second));
+ } else {
+
+ tvMethod.setText(getString(R.string.cloudLink_meeting_confWaiting) + " " + unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second));
+ }
+ Hours = unitFormat(hour);
+ }
+ Minute = unitFormat(minute);
+ seconds = unitFormat(second);
+ }
+ });
+ }
+
+ public static String unitFormat(int i) {
+ String retStr = null;
+ if (i >= 0 && i < 10) {
+ retStr = "0" + i;
+ } else {
+ retStr = "" + i;
+ }
+ return retStr;
+ }
+
+ private void setToolBar() {
+ if (Build.VERSION.SDK_INT >= 21) {
+ View decorView = getWindow().getDecorView();
+ int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ decorView.setSystemUiVisibility(option);
+ getWindow().setNavigationBarColor(Color.BLACK);
+ getWindow().setStatusBarColor(Color.TRANSPARENT);
+ }
+ }
+
+ @OnClick({R2.id.tv_name, R2.id.tv_mute, R2.id.tv_speaker, R2.id.iv_right_green_called, R2.id.iv_left_red_called, R2.id.tv_participants,
+ R2.id.tv_more, R2.id.tv_switch_voice, R2.id.ll_dial})
+ public void viewClick(View view) {
+ int viewId = view.getId();
+ if (viewId == R.id.tv_mute) {
+ if (type == POINT_CONNECTED) {
+ mPresenter.muteCall(mCallID);
+ } else {
+ mPresenter.muteSelf();
+ }
+ } else if (viewId == R.id.tv_speaker) {
+ setAudioRoute(mPresenter.switchAudioRoute());
+ } else if (viewId == R.id.iv_left_red_called) {
+ LogUtil.userAction("挂断");
+ if (0 == mConfToCallHandle) {
+ CallMgrV2.getInstance().endCall(mCallID);
+ } else {
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ CallMgrV2.getInstance().endCall(mCallID);
+ MeetingMgrV2.getInstance().rejectConf();
+ }
+ } else if (viewId == R.id.iv_right_green_called) {
+ clickAnswer();
+ } else if (viewId == R.id.tv_participants) {
+ jumpParticipants(false);
+ } else if (viewId == R.id.tv_switch_voice) { // 语音接听按钮
+ LogUtil.userAction("点击转语音接听按钮");
+ checkPermission(() -> {
+ if (type == POINT_VIDEOED || type == POINT_CALLED) {
+ Intent intent2 = new Intent(InvitedPointCallActivity.this, SponsorMeetingActivity.class);
+ intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent2.addCategory(IntentConstant.DEFAULT_CATEGORY);
+ intent2.putExtra(Constant.CALL_INFO, callInfo);
+ intent2.putExtra(Constant.CALL_VIDEO_TO_VOICE, true);
+ intent2.putExtra(Constant.CALLED_NAME, tvName.getText().toString());
+ startActivity(intent2);
+ CallMgrV2.getInstance().answerCall(mCallID, false);
+ } else {
+ boolean isJoin = CallMgrV2.getInstance().answerCall(mCallID, false);
+ if (isJoin) {
+ initVoiceConfTitle();
+ tvSwitchVoice.setVisibility(GONE);
+ } else {
+ ToastUtils.show(getResources().getString(R.string.cloudLink_metting_callerror));
+ finish();
+ }
+ }
+ }, this::permissionReconfirm, RecordAudioFactory.class, PhoneStateFactory.class);
+ } else if (viewId == R.id.tv_more) {
+ more();
+ } else if (viewId == R.id.tv_name) {
+ watchText(tvName.getText().toString());
+ } else if (viewId == R.id.ll_dial) {
+ mPresenter.showDialogKeyboard();
+ }
+ }
+
+
+ /**
+ * 跳到与会者界面
+ *
+ * @param isAuto 是否自动打开邀请界面
+ */
+ private void jumpParticipants(boolean isAuto) {
+// Intent intent = new Intent(this, ParticipantsActivityV2.class);
+// intent.putExtra(Constant.IS_VIDEO_CONF, false);
+// intent.putExtra(Constant.CONF_ID, CONF_ID);
+// intent.putExtra(Constant.IS_AUTO_JUMP, isAuto);
+// startActivityForResult(intent, 333);
+ }
+
+ /**
+ * 更多按钮
+ */
+ private Boolean isHandUp = true;
+
+ private void more() {
+ releasePopping();
+ if (MeetingController.getInstance().isChairman()) {
+ List list = new ArrayList<>();
+ List clickListeners = checkSupportSubtitle(list);
+ list.add(getString(R.string.cloudLink_meeting_releaseChairman));
+ clickListeners.add(v -> mPresenter.releaseChairman());
+ list.add(MeetingController.getInstance().isMeetingIsLock() ? getResources().getString(R.string.cloudLink_meeting_unlock)
+ : getResources().getString(R.string.cloudLink_meeting_lock));
+ clickListeners.add(v -> mPresenter.meetingLock());
+ if (!TextUtils.isEmpty(MeetingController.getInstance().getEndTime())) {
+ list.add(getString(R.string.cloudLink_meeting_extendConf));
+ clickListeners.add(v -> mPresenter.postponeConf());
+ }
+ list.add(getString(R.string.cloudLink_meeting_invitation));
+ clickListeners.add(v -> jumpParticipants(true));
+ mListPopWindow = new ListPopWindow(this, list, mIsSubtitleEnable, clickListeners.toArray(new View.OnClickListener[0]));
+ mListPopWindow.show();
+ } else {
+ List list = new ArrayList<>();
+ List clickListeners = checkSupportSubtitle(list);
+ Member member = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (member == null) {
+ return;
+ }
+ if (UIUtil.isService3()) {
+ isHandUp = !(member.isHandUp() || member.isReqTalk());
+ } else {
+ isHandUp = !(member.isHandUp());
+ }
+ if (isHandUp) {
+ list.add(getString(R.string.cloudLink_meeting_raiseHand));
+ } else {
+ list.add(getString(R.string.cloudLink_meeting_cancelHandup));
+ }
+ clickListeners.add(v -> {
+ if (MeetingController.getInstance().isHasChairman()) {
+ mPresenter.handUpSelf(isHandUp);
+ }
+ });
+ list.add(getString(R.string.cloudLink_meeting_applicationChairman));
+ clickListeners.add(v -> {
+ if (!MeetingController.getInstance().isHasChairman()) {
+ MeetingController.getInstance().isShowDialog = true;
+ String vmrId = EncryptedSPTool.getString(Constant.VMR_ACCESSNUMBER) + EncryptedSPTool.getString(Constant.VMR_CONF_ID);
+ if (!UIUtil.isService3() && !TextUtils.isEmpty(vmrId) && vmrId.equals(EncryptedSPTool.getString(Constant.IS_VMR_2_ID))) { // 2.0vmr自动申请
+ mPresenter.requestChairman(EncryptedSPTool.getString(Constant.VMR_CHAIRMANPWD));
+ } else if (UIUtil.isService3() && vmrId.equals(EncryptedSPTool.getString(Constant.IS_VMR_3_ID))) {
+ mPresenter.requestChairman(MeetingController.getInstance().getChairman3Pwd());
+ } else {
+ mPresenter.requestChairman("");
+ }
+ }
+ });
+ mListPopWindow = new ListPopWindow(this, list, mIsSubtitleEnable, clickListeners.toArray(new View.OnClickListener[0]));
+ mListPopWindow.show();
+ }
+ }
+
+ private void releasePopping() {
+ if (mListPopWindow != null) {
+ if (mListPopWindow.isShowing()) {
+ mListPopWindow.dismiss();
+ }
+ mListPopWindow = null;
+ }
+ }
+
+ private List checkSupportSubtitle(List list) {
+ List clickListeners = new ArrayList<>();
+ if (mIsConfSubtitleEnable) {
+ list.add(getString(mSubtitleView.isEnable() ?
+ R.string.cloudLink_meeting_disable_participant_caption : R.string.cloudLink_meeting_enable_participant_caption));
+ clickListeners.add(v -> {
+ if (v instanceof SuperTextView) {
+ boolean isEnable = ((SuperTextView) v).getLeftString().equals(getString(
+ R.string.cloudLink_meeting_enable_participant_caption));
+ if (isEnable) {
+ mSubtitleView.show();
+ } else {
+ mSubtitleView.hide();
+ }
+ EncryptedSPTool.putBoolean("App_isFirstPlaceSubtitle", false);
+ }
+ });
+ }
+ if (mIsConfSubtitleEnable && MeetingController.getInstance().isChairman()) {
+ list.add(getString(mIsSubtitleEnable ?
+ R.string.cloudLink_meeting_disable_caption : R.string.cloudLink_meeting_enable_caption));
+ clickListeners.add(v -> {
+ mPresenter.setConfSubtitle(mIsSubtitleEnable);
+ EncryptedSPTool.putBoolean("App_isFirstSubtitle", false);
+ });
+ }
+ return clickListeners;
+ }
+
+
+ /**
+ * 点击接听按钮
+ */
+ private void clickAnswer() {
+ tvSwitchVoice.setVisibility(View.GONE);
+ LogUtil.userAction("点击接听按钮");
+ boolean needCamera = type == POINT_VIDEOED || type == CONF_VIDEO_COMING;
+ checkPermission(() -> {
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ if (type == POINT_VIDEOED || type == POINT_CALLED) {
+ Intent intent = new Intent(InvitedPointCallActivity.this, SponsorMeetingActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.addCategory(IntentConstant.DEFAULT_CATEGORY);
+ intent.putExtra(Constant.CALL_INFO, callInfo);
+ intent.putExtra(Constant.CALLED_NAME, tvName.getText().toString());
+ LogUtil.d("TAG", "跳转界面1");
+ startActivity(intent);
+ CallMgrV2.getInstance().answerCall(mCallID, true);
+ } else if (type == CONF_VIDEO_COMING) {
+ LogUtil.d("TAG", "跳转界面2");
+ boolean isJoin = CallMgrV2.getInstance().answerCall(mCallID, true);
+ if (isJoin) {
+ showLoading(getString(R.string.cloudLink_meeting_joinJonfIng));
+ isJumpSpon = true;
+ } else {
+ ToastUtils.show(getResources().getString(R.string.cloudLink_metting_callerror));
+ finish();
+ }
+ } else {
+ initVoiceConfTitle();
+ boolean isJoin = CallMgrV2.getInstance().answerCall(mCallID, false);
+ if (isJoin) {
+ showLoading(getString(R.string.cloudLink_meeting_joinJonfIng));
+ isJumpSpon = false;
+ } else {
+ ToastUtils.show(getResources().getString(R.string.cloudLink_metting_callerror));
+ finish();
+ }
+ }
+ }, this::permissionReconfirm, RecordAudioFactory.class, needCamera ? CameraFactory.class : EmptyFactory.class, PhoneStateFactory.class);
+ }
+
+ /**
+ * 初始化音频会议布局
+ */
+ private void initVoiceConfTitle() {
+ titleController = new MeetingTitleBarController(this);
+ titleController.inflaterRoot(rl_back);
+ if (!isMiniback) {
+ titleController.setTitleInfoVisibility(GONE);
+ }
+ titleController.updateNetworkQuality(MeetingTitleBarController.mCurrentNetLevel, true);
+ titleController.setTitleClickListener(new IMeetingTitleController.TitleClickListener() {
+
+ @Override
+ public void titleInfoClick() {
+ MeetingTitleInfoBean bean = new MeetingTitleInfoBean();
+ bean.setSubject(MeetingMgrV2.getInstance().getSubject());
+ bean.setConfID(MeetingController.getInstance().getConfId());
+ bean.setTime(DateUtil.fromMeetingTime(MeetingController.getInstance().getStartTime(),
+ MeetingController.getInstance().getEndTime()));
+ bean.setChairmanPwd(MeetingController.getInstance().getChairmanPwd());
+ bean.setGuestPwd(MeetingController.getInstance().getGuestPwd());
+ bean.setScheduleUserName(MeetingController.getInstance().getScheduleUserName());
+ DialogUtil.meetingTitleInfoDialog(InvitedPointCallActivity.this, BaseConfContract.LAYOUT_CONF_VOICE, false, bean, v -> {
+ mPresenter.showBitStream();
+ });
+ }
+
+ @Override
+ public void minimizeClick() {
+ minimize();
+
+ }
+
+ @Override
+ public void exit() {
+ if (!isFocus) {//点对点呼叫挂断
+ CallMgrV2.getInstance().endCall(mCallID);
+ } else {
+ mPresenter.showLandLeaveConfBottomSheetDialog();
+ }
+ }
+
+ @Override
+ public void timerCall(int hour, int minute, int second) {
+
+ }
+ });
+ }
+
+
+ /**
+ * 小窗口播放
+ */
+ Intent MinimizeService;
+
+ @SuppressLint("NewApi")
+ private void minimize() {
+ if (Settings.canDrawOverlays(this)) {
+ LogUtil.userAction("点击最小化");
+ MinimizeService = new Intent(this, com.tengshisoft.chatmodule.hwclud.serivce.MinimizeService.class);
+ MinimizeService.putExtra("type", ConferenceConstant.VOICE_CALL);
+ MinimizeService.putExtra("isConf", true);
+ long time = 0;
+ time += seconds == null ? 0 : Long.parseLong(seconds);
+ time += Minute == null ? 0 : Long.parseLong(Minute) * 60;
+ time += Hours == null ? 0 : Long.parseLong(Hours) * 3600;
+
+ MinimizeService.putExtra("time", time);
+ MinimizeService.putExtra("tvConfId", titleController.getMeetingId());
+ MinimizeService.putExtra("tvConfName", titleController.getMeetingName());
+ MinimizeService.putExtra("tvName", tvName.getText().toString());
+ MinimizeService.putExtra(Constant.MEETING_CAPTION_ENABLE, mIsSubtitleEnable);
+ MinimizeService.putExtra(Constant.MEETING_CONF_CAPTION_ENABLE, mIsConfSubtitleEnable);
+ MinimizeService.putExtra(Constant.MEETING_CONF_CAPTION_SHOWING, mSubtitleView.isEnable());
+ startService(MinimizeService);
+ finish();
+ } else {
+ showOpenPermissionDialog();
+ }
+ }
+
+ @Override
+ protected void onRestart() {
+ super.onRestart();
+ LogUtil.i("zzz", "onRestart");
+ }
+
+ @Override
+ protected void onPause() {
+ isInConf = false;
+ super.onPause();
+ }
+
+ @Override
+ protected void onResume() {
+ isInConf = true;
+ super.onResume();
+ }
+
+ @Override
+ public void watchText(String mobile) {
+ if (!UIUtil.isEmpty(mobile)) {
+ Intent intent = new Intent(this, WatchAllTextActivityV2.class);
+ intent.putExtra(Constant.ALL_TEXT, mobile);
+ startActivityForResult(intent, 222);
+ }
+ }
+
+ @Override
+ public void updateTsDkMobileAudioRoute(TsdkMobileAuidoRoute audioRoute) {
+ Drawable top;
+ if (audioRoute == TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_speaker);
+ tvSpeaker.setText(getResources().getString(R.string.cloudLink_meeting_speaker));
+ } else if (audioRoute == TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_BLUETOOTH) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_baseline_bluetooth_connected_24);
+ tvSpeaker.setText(getResources().getString(R.string.cloudLink_bluetooth));
+ } else {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_hearing_24);
+ tvSpeaker.setText(getResources().getString(R.string.cloudLink_listen));
+ }
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ tvSpeaker.setCompoundDrawables(null, top, null, null);
+ tvSpeaker.postInvalidate();
+ }
+
+ private int duration;
+
+ @Override
+ public void postponeConf() {
+ TimeSelectUtils utils = new TimeSelectUtils();
+ List minute1 = utils.getMinute2();
+ OptionsPickerView pvTime = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> {
+ if (TimeSelectUtils.getHour().get(options1) == 0 && minute1.get(options2) == 0) {
+ showCustomToast(R.string.cloudLink_meeting_extendConfTimeErr);
+ return;
+ }
+ duration = minute1.get(options1);
+ MeetingController.getInstance().duration = duration;
+ MeetingMgrV2.getInstance().postpone(duration);
+
+ })
+ .setLabels(" " + getString(R.string.cloudLink_min), "", "")
+ .isCenterLabel(false)
+ .setCancelText(this.getResources().getString(R.string.cloudLink_meeting_selectConfAddTime))
+ .setSubmitText(getString(R.string.cloudLink_sure))
+ .setTextColorCenter(this.getResources().getColor(R.color.saveButtonBackground))
+ .setCancelColor(this.getResources().getColor(R.color.contact_text))
+ .isDialog(true)
+ .build();
+ pvTime.setPicker(minute1);
+ pvTime.show();
+ }
+
+ @Override
+ public void handleChairmanResult(Constant.ParticipantEvent type, String name, int result) {
+ runOnUiThread(() -> {
+ switch (type) {
+ case RELEASE_CHAIRMAN_FAILED:
+ if (isInConf) {
+ showCustomToast(R.string.cloudLink_meeting_releaseChairmanFailed);
+ MeetingController.getInstance().setChairman(true);
+ }
+ break;
+ case RELEASE_CHAIRMAN_SUCCESS:
+ MeetingController.getInstance().setChairman(false);
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, "");
+ break;
+ case REQUEST_CHAIRMAN_FAILED:
+ MeetingController.getInstance().setChairman(false);
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, "");
+ if (result == 67109017 && MeetingController.getInstance().isShowDialog) {
+ showDialog();
+ } else if (result == 67109022) {
+ showCustomToast(R.string.cloudLink_meeting_requestChairmanFailed);
+ } else if (result == 67109023) {
+ showCustomToast(R.string.cloudLink_meeting_requestChairmanPasswordFailed);
+ } else {
+ if (MeetingController.getInstance().isShowDialog) {
+ showCustomToast(R.string.cloudLink_meeting_releaseChairmanFailedPsw);
+ }
+ }
+ break;
+ case REQUEST_CHAIRMAN_SUCCESS:
+ MeetingController.getInstance().setChairman(true);
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+ showCustomToast(getString(R.string.cloudLink_meeting_requestChairmanSuccess));
+ break;
+ default:
+ break;
+ }
+ });
+ }
+
+ @Override
+ public void auxFailed(int code) {
+
+ }
+
+ @Override
+ public void updateViewEnable() {
+
+ }
+
+ private void showDialog() {
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ return;
+ }
+ LinkDialog = new CloudLinkDialog(InvitedPointCallActivity.this);
+ LinkDialog.setStr_message(getString(R.string.cloudLink_meeting_chairmanpwd), Gravity.START);
+ LinkDialog.editText_message(true, getString(R.string.cloudLink_login_inputPwd));
+ LinkDialog.setYes(getString(R.string.cloudLink_sure), null, () -> {
+ mPresenter.requestChairman(TextUtils.isEmpty(LinkDialog.getEditTextMessage()) ? "1" : LinkDialog.getEditTextMessage());
+ LinkDialog.dismiss();
+ });
+ LinkDialog.setNo(getString(R.string.cloudLink_cancel), null, () -> LinkDialog.dismiss());
+ LinkDialog.show();
+ }
+
+ @Override
+ public void showCustomToast(int resID) {
+ UiUtils.runUI(() -> {
+ ToastUtils.show(getString(resID));
+ });
+ }
+
+ @Override
+ public void showCustomToast(String resID) {
+ runOnUiThread(() -> ToastUtils.show(resID));
+ }
+
+ @Override
+ public void leaveConfSuccess() {
+ finish();
+ }
+
+ private Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (!getIntent().getBooleanExtra("isMiniback", false)) {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (MeetingController.getInstance().isSponsorConf()) { // 发起,预约,创建等
+
+ boolean isAudio = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ if (!isFirstMic && isAudio) {
+ MeetingMgrV2.getInstance().muteAttendee(self, false);
+ } else {
+ MeetingMgrV2.getInstance().muteAttendee(self, true);
+ }
+ } else {
+ MeetingMgrV2.getInstance().muteAttendee(self, true);
+ }
+ MeetingController.getInstance().setSponsorConf(false);
+ if (PhoneUtil.isTelephonyCalling() &&
+ MeetingMgrV2.getInstance().getCurrentConference() != null) {
+ receivedMobileCall(true);
+ }
+ }
+ }
+ };
+
+ /**
+ * 抗啸叫自动关闭麦克风UI && 给用户提示
+ */
+ private void closeVoiceUI(boolean b) {
+ if (b) {
+ if (EncryptedSPTool.getBoolean(Constant.HOWL_AUTO_MUTE, true)) {
+ Drawable top = ContextCompat.getDrawable(InvitedPointCallActivity.this, R.drawable.ic_mic_close);
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ tvMute.setCompoundDrawables(null, top, null, null);
+ MeetingController.getInstance().isVoiceOpen = true;
+ CallFunc.getInstance().setMuteStatus(true);
+ ToastUtils.show(getString(R.string.cloudLink_mine_howlautomutetoast));
+ }
+ } else {
+ ToastUtils.show(getString(R.string.cloudLink_mine_howlautomutetoastna));
+ }
+ }
+
+ @Override
+ public void updateView(int what, boolean isShow, Object obj) {
+ LogUtil.zzz("updateView", "what=" + what + ",isShow=" + isShow);
+ runOnUiThread(() -> {
+ switch (what) {
+ case CALL_UPDATE_VOICE:
+ case Constant.UPDATE_VOICE:
+ LogUtil.zzz("updateView", "what=" + what + ",isShow=" + isShow);
+ Drawable top;
+ if (!isShow) {
+ top = getResources().getDrawable(R.drawable.ic_mic);
+ } else {
+ top = getResources().getDrawable(R.drawable.ic_mic_close);
+ }
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ tvMute.setCompoundDrawables(null, top, null, null);
+ // 执行本地设置的麦克风状态 规避方法 SDK第一次会默认设置麦克风
+ if (index == 0 && MeetingController.getInstance().conferenceRight && MeetingController.getInstance().isOnBaseInfoDo) {
+ isFirstMic = isShow;
+ index++;
+ }
+ break;
+ case Constant.CALL_VOICE_GOING:
+ CallInfo callInfo = (CallInfo) obj;
+ this.callInfo = callInfo;
+ this.mCallID = callInfo.getCallID();
+ LogUtil.zzz("音频", "CALL_VOICE_GOING callID", LogUtil.commonDisplay(mCallID + ""));
+ break;
+ case Constant.UPDATE_CALL_CONNECT:
+ isConnect = true;
+ type = POINT_CONNECTED;
+ this.callInfo = (CallInfo) obj;
+ setData();
+ break;
+ case Constant.UPDATE_CONF_CONNECT:
+ confConnect(obj);
+ break;
+ case Constant.RECEIVED_MOBILE_CALL:
+ if (!isShow) {
+ top = getResources().getDrawable(R.drawable.ic_mic);
+ } else {
+ top = getResources().getDrawable(R.drawable.ic_mic_close_gray);
+ }
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ tvMute.setCompoundDrawables(null, top, null, null);
+ tvMute.setEnabled(false);
+ tvSpeaker.setEnabled(false);
+ break;
+ case Constant.END_MOBILE_CALL:
+ if (!isShow) {
+ top = getResources().getDrawable(R.drawable.ic_mic);
+ } else {
+ top = getResources().getDrawable(R.drawable.ic_mic_close);
+ }
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ tvMute.setCompoundDrawables(null, top, null, null);
+ tvMute.setEnabled(true);
+ tvSpeaker.setEnabled(true);
+ break;
+ default:
+ break;
+ }
+ });
+ }
+
+
+ /**
+ * 语音会议连接成功
+ */
+ private void confConnect(Object obj) {
+ llDial.setVisibility(View.VISIBLE);
+ isConnect = true;
+ //会议链接后再注册home监听
+ hideLoading();
+ if (isJumpSpon) {
+ return;
+ }
+ if (obj instanceof TsdkCall) {
+ TsdkCall call = (TsdkCall) obj;
+ type = VOICE_CONF_ING;
+ mPresenter.setConfID(String.valueOf(call.getCallInfo().getConfId()));
+ } else if (obj instanceof TsdkConference) {
+ TsdkConference tsdkConference = (TsdkConference) obj;
+ type = VOICE_CONF_ING;
+ mPresenter.setConfID(String.valueOf(tsdkConference.getHandle()));
+ }
+ setTime();
+ setData();
+ }
+
+ private int perNum = 0;
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void refreshMemberList(List members) {
+ Platform.runUi(() -> {
+ if (!isConnect) {
+ return;
+ }
+ if (members == null) {
+ return;
+ }
+ if (members.size() == 0) {
+ return;
+ }
+ MeetingController.getInstance().setChairman(false);
+ LogUtil.zzz(TAG, "refreshMemberList memberList.size == " + members.size());
+ memberList = members;
+ perNum = 0;
+ for (Member member : memberList) {
+ if (member.getStatus() == ConfConstant.ParticipantStatus.IN_CONF) {
+ perNum++;
+ }
+ if (member.isSelf()) {
+ updateView(Constant.UPDATE_VOICE, member.isMute(), "");
+ if (!MeetingController.getInstance().hasSyncVoiceState && MeetingController.getInstance().isOnBaseInfoDo) {
+ boolean isAudio = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ if (!MeetingController.getInstance().hasReqChairMan && isAudio == member.isMute()) {
+ handler.post(runnable);
+ } else if (PhoneUtil.isTelephonyCalling() &&
+ MeetingMgrV2.getInstance().getCurrentConference() != null) {
+ receivedMobileCall(true);
+ } else {
+ MeetingController.getInstance().setSponsorConf(false);
+ }
+ MeetingController.getInstance().hasSyncVoiceState = true;
+ }
+ if (PhoneUtil.isTelephonyCalling()) {
+ if (!isSilentState) {
+ updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ } else {
+ updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ }
+ tvParticipants.setVisibility(View.VISIBLE);
+ currentMillers = -1;
+ mPresenter.dismissDialogKeyboard();
+ if (member.getRole() == TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN) {
+ MeetingController.getInstance().setChairman(true);
+ }
+ }
+ }
+ if (!TextUtils.isEmpty(MeetingMgrV2.getInstance().getSubject())) {
+ tvName.setText(MeetingMgrV2.getInstance().getSubject() + "(" + perNum + ")");
+ }
+ });
+ }
+
+ @Override
+ public void confEnd() {
+ Log.e("测试", "InvitedPointCallActivity:结束会议");
+ hideLoading();
+// EncryptedSPTool.putInt(Constant.Audio_State_1, -1);
+ finish();
+ }
+
+ @Override
+ public void toBackStartService() {
+
+ }
+
+ @Override
+ public void showStreamDialog(boolean isAudio) {
+ if (signalController == null) {
+ return;
+ }
+ signalController.showDialog(isAudio, false);
+ }
+
+ @Override
+ public void showNetworkError() {
+ }
+
+ @Override
+ public void showLoading() {
+ }
+
+
+ @Override
+ public void hideLoading() {
+ super.hideLoading();
+ }
+
+ @Override
+ protected void onDestroy() {
+ //注销监听
+ LogUtil.zzz("onDestroy");
+ releasePopping();
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+// unRegisterAudioDeviceAndroid();
+ if (subscribe != null && !subscribe.isDisposed()) {
+ subscribe.dispose();
+ }
+ mPresenter.detachView();
+ EncryptedSPTool.putBoolean("isqueryMeetingList", true);
+ super.onDestroy();
+ }
+
+
+ @Override
+ public void onBackPressed() {
+ if (!isFocus) {
+ CallMgrV2.getInstance().endCall(mCallID);
+ } else {
+ mPresenter.showLandLeaveConfBottomSheetDialog();
+ }
+ }
+
+ private void searchUsername(TextView tv, String usercode, TsdkOnEvtSearchLdapContactsResult.Param param) {
+
+ if (param.getResult() == 1) {
+ TsdkSearchLdapContactsResult searchResultData = param.getSearchResultData();
+ if (searchResultData != null) {
+ int currentCount = searchResultData.getCurrentCount();
+ if (currentCount != 0) {
+ ArrayList ldapContactList = searchResultData.getLdapContactList();
+ for (TsdkLdapContactsInfo contactsInfo : ldapContactList) {
+ if (contactsInfo.getUcAcc().equals(usercode)) {
+ tlci = contactsInfo;
+ }
+ }
+ if (tlci != null) {
+ username = tlci.getName();
+ }
+ if (!TextUtils.isEmpty(username)) {
+ this.runOnUiThread(() -> tv.setText(username));
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KEYCODE_VOLUME_DOWN:
+ if (isInMeeting) {
+ if (null != TsdkManager.getInstance()) {
+ if (voiceNum >= 10) {
+ voiceNum = voiceNum - 10;
+ TsdkManager.getInstance().getCallManager().setSpeakVolume(voiceNum);
+ } else {
+ TsdkManager.getInstance().getCallManager().setSpeakVolume(0);
+ }
+ }
+ } else {
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ }
+ break;
+ case KEYCODE_VOLUME_UP:
+ if (isInMeeting) {
+ if (null != TsdkManager.getInstance()) {
+ if (voiceNum <= 90) {
+ voiceNum = voiceNum + 10;
+ } else {
+ voiceNum = 100;
+ }
+ TsdkManager.getInstance().getCallManager().setSpeakVolume(voiceNum);
+ }
+ } else {
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ }
+ break;
+ default:
+ break;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+// if (requestCode == 333 && resultCode == RESULT_OK) {
+// boolean reqChairManSuccess = data.getBooleanExtra(Constant.REQUEST_CHAIRMAN_SUCCESS, false);
+// if (reqChairManSuccess) {
+// Log.d(TAG, "申请主席成功onActivityResult: "+getResources().getString(R.string.cloudLink_meeting_requestChairmanSuccess));
+// runOnUiThread(() -> ToastUtil.toast(getResources().getString(R.string.cloudLink_meeting_requestChairmanSuccess)));
+// }
+// }
+ }
+
+
+ private void receivedMobileCall(boolean isInit) {
+ LogUtil.zzz("receivedMobileCall_invitedPointCall");
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(false);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ if (isInit) {
+ isSilentState = !EncryptedSPTool.getBoolean(EncryptedSPTool.getString(
+ Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ } else {
+ isSilentState = self.isMute();
+ }
+ if (!isSilentState) {
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, !isSilentState);
+ if (result == 0) {
+ MeetingController.getInstance().isVoiceOpen = !isSilentState;
+ updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ }
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ }
+
+ private void endMobileCall() {
+ LogUtil.zzz("endMobileCall_invitedPointCall");
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(true);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ if (!isSilentState) {
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, isSilentState);
+ if (result == 0) {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ updateView(Constant.END_MOBILE_CALL, isSilentState, "");
+ }
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ updateView(Constant.END_MOBILE_CALL, isSilentState, "");
+ }
+ }
+
+ @SuppressLint("SetTextI18n")
+ private void updataBaseInfo(TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd) {
+
+ if (tsdkOnEvtConfBaseInfoInd == null) {
+ LogUtil.zzz("tsdkOnEvtConfBaseInfoInd is null");
+ return;
+ }
+ if (tsdkOnEvtConfBaseInfoInd.param == null) {
+ LogUtil.zzz("tsdkOnEvtConfBaseInfoInd.param is null");
+ return;
+ }
+ if (tsdkOnEvtConfBaseInfoInd.param.confBaseInfo == null) {
+ LogUtil.zzz("tsdkOnEvtConfBaseInfoInd.param.confBaseInfo is null");
+ return;
+ }
+
+ String callID = tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getAccessNumber();
+ String message = "ID: " + UIUtil.splitString(callID);
+
+ if (null != tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestPwd() && !TextUtils.isEmpty(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestPwd())) {
+ MeetingController.getInstance().setGuestPwd(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestPwd());
+ }
+
+ UIUtil.runUI(() -> {
+ if (titleController != null) {
+ titleController.setTitleInfoVisibility(VISIBLE);
+ titleController.displayMeetingId(message);
+ titleController.displayMeetingName(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getSubject());
+ tvName.setText(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getSubject() + "(" + perNum + ")");
+ MeetingController.getInstance().setJoinConfSuccess(true);
+ isConnect = true;
+ }
+ callTool.setVisibility(VISIBLE);
+ });
+ }
+
+ void playMusic() {
+ String mFilePath = Environment.getExternalStorageDirectory() + File.separator + CallFunc.RINGING_FILE;
+ CallMgrV2.getInstance().startPlayRingingTone(mFilePath);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/SponsorMeetingActivity.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/SponsorMeetingActivity.java
new file mode 100644
index 0000000..9096b87
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/SponsorMeetingActivity.java
@@ -0,0 +1,3798 @@
+package com.tengshisoft.chatmodule.activity;
+
+import android.annotation.SuppressLint;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.graphics.drawable.Drawable;
+import android.media.projection.MediaProjectionManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.bigkoo.pickerview.builder.OptionsPickerBuilder;
+import com.bigkoo.pickerview.view.OptionsPickerView;
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkConfRole;
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute;
+import com.huawei.ecterminalsdk.base.TsdkNotifyHandUpAttendee;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtConfBaseInfoInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.huawei.ecterminalsdk.base.TsdkonEvtAuditDir;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.huawei.videoengine.HarmonyCaptureScreen;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.R2;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.ConferenceConstant;
+import com.tengshisoft.chatmodule.beans.ControlOperationsResults;
+import com.tengshisoft.chatmodule.beans.MeetingTitleInfoBean;
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.beans.SponsorMeetingConstant;
+import com.tengshisoft.chatmodule.beans.TSDKErrorConstant;
+import com.tengshisoft.chatmodule.hwclud.controller.IMeetingSignalController;
+import com.tengshisoft.chatmodule.hwclud.controller.IMeetingTitleController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingSignalController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingTitleBarController;
+import com.tengshisoft.chatmodule.hwclud.controller.TsdkConferenceLiveData;
+import com.tengshisoft.chatmodule.hwclud.listener.NoDoubleClickListener;
+import com.tengshisoft.chatmodule.hwclud.listener.OnDragTouchListener;
+import com.tengshisoft.chatmodule.hwclud.manager.AudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.SwitchAudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.serivce.AuxSendService;
+import com.tengshisoft.chatmodule.hwclud.ui.BaseConfContract;
+import com.tengshisoft.chatmodule.hwclud.ui.BaseMvpActivityV2;
+import com.tengshisoft.chatmodule.hwclud.ui.BasePresenterV2;
+import com.tengshisoft.chatmodule.hwclud.ui.CloudLinkDialog;
+import com.tengshisoft.chatmodule.hwclud.ui.CustomViewPager;
+import com.tengshisoft.chatmodule.hwclud.ui.DialogUtil;
+import com.tengshisoft.chatmodule.hwclud.ui.FloatNormalView;
+import com.tengshisoft.chatmodule.hwclud.ui.LazyloadFragment;
+import com.tengshisoft.chatmodule.hwclud.ui.MeetingSubtitleView;
+import com.tengshisoft.chatmodule.hwclud.ui.MySponsorPageAdapter;
+import com.tengshisoft.chatmodule.hwclud.ui.MyWindowManager;
+import com.tengshisoft.chatmodule.hwclud.ui.SponsorMeetingContract;
+import com.tengshisoft.chatmodule.hwclud.ui.SponsorMeetingPresenter;
+import com.tengshisoft.chatmodule.hwclud.ui.SponsorMorePopWindow;
+import com.tengshisoft.chatmodule.hwclud.utils.AppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.DateUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.FirstLetterUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.ListTools;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.MYSPUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.MathUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.PhoneUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.ScreenUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.SystemUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.TimeSelectUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.TimerUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UiUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.camera.CameraFactory;
+import com.tengshisoft.chatmodule.hwclud.utils.phone_state.PhoneStateFactory;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConfConstant;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.core.content.ContextCompat;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+import androidx.viewpager.widget.ViewPager;
+import butterknife.BindView;
+import butterknife.OnClick;
+import razerdp.widget.QuickPopup;
+
+import static android.view.View.VISIBLE;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.CALL_UPDATE_VOICE;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.IS_SHOW_PARTICIPANTS;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.IS_VMR_2_ID;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.WATCH_MYSELF_CAMERA_STATE;
+import static com.tengshisoft.chatmodule.hwclud.utils.DateUtils.unitFormat;
+import static com.tengshisoft.chatmodule.hwclud.utils.DeviceManager.isSilentState;
+import static com.tenlionsoft.baselib.constant.ConfConstant.ParticipantStatus.IN_CONF;
+
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class SponsorMeetingActivity extends BaseMvpActivityV2 implements SponsorMeetingContract.SponsorMeetingView, ViewPager.OnPageChangeListener, FloatNormalView.OnFloatClickListener {
+
+ @BindView(R2.id.vedio_vp)
+ CustomViewPager vedioVp;
+ @BindView(R2.id.tv_mute)
+ TextView tvMute;
+ @BindView(R2.id.tv_speaker)
+ TextView tvSpeaker;
+ @BindView(R2.id.tv_video)
+ TextView tvVideo;
+ @BindView(R2.id.tv_share)
+ TextView tvShare;
+ @BindView(R2.id.tv_participants)
+ TextView tvParticipants;
+ @BindView(R2.id.tv_more)
+ TextView tvMore;
+ @BindView(R2.id.tv_tip)
+ TextView tvTip;
+ @BindView(R2.id.local_video)
+ FrameLayout localVideo;
+ @BindView(R2.id.rb1)
+ RadioButton rb1;
+ @BindView(R2.id.rb2)
+ RadioButton rb2;
+ @BindView(R2.id.rb3)
+ RadioButton rb3;
+ @BindView(R2.id.rg)
+ RadioGroup rg;
+ @BindView(R2.id.iv_voice_setting)
+ ImageView ivVoiceSetting;
+ @BindView(R2.id.cl_video)
+ ConstraintLayout clVideo;
+ @BindView(R2.id.rl_voice)
+ RelativeLayout rlVoice; // 点对点呼叫布局
+ @BindView(R2.id.rl_footer)
+ ConstraintLayout rlFooter;
+ @BindView(R2.id.frbg)
+ FrameLayout frbg;
+ @BindView(R2.id.iv_keyboard)
+ ImageView iv_keyboard;
+ @BindView(R2.id.tv_name)
+ TextView tv_name;
+ @BindView(R2.id.tv_confId)
+ TextView tv_confId;
+ @BindView(R2.id.tv_method)
+ TextView tv_method;
+ @BindView(R2.id.tv_mute_voice)
+ TextView tv_mute_voice;
+ @BindView(R2.id.tv_speaker_voice)
+ TextView tv_speaker_voice;
+ @BindView(R2.id.btn_voiceToVideo)
+ TextView btn_voiceToVideo;
+ //单向直播
+ @BindView(R2.id.rl_footer_audit_dir)
+ ConstraintLayout rl_footer_audit_dir;
+ @BindView(R2.id.rlBottomView)
+ FrameLayout rlBottomView;
+ @BindView(R2.id.call_tool)
+ LinearLayout linCallTool;
+ @BindView(R2.id.lin_calling_tool)
+ LinearLayout linCallingTool;
+ @BindView(R2.id.img_voice_minimize)
+ ImageView mini;
+ @BindView(R2.id.img_Meeting_Signal)
+ ImageView ivMeetingSignal;
+ @BindView(R2.id.bg_icon)
+ ImageView bg_icon;
+
+
+ @BindView(R2.id.tv_voice_displayName)
+ TextView tvVoiceDisplayName;
+ @BindView(R2.id.tv_voice_userNumber)
+ TextView tvVoiceUserNumber;
+ @BindView(R2.id.rl_voice_title)
+ RelativeLayout rlVoiceTitle;
+
+ @BindView(R2.id.hide_video_view)
+ FrameLayout hideVideoView;
+
+ @BindView(R2.id.rl_meeting_subtitle)
+ MeetingSubtitleView mSubtitleView;
+
+ @BindView(R2.id.conf_control)
+ FrameLayout mConfControl;
+
+ // 从小窗口返回
+ private String VMRPwd = "";
+
+ //--->判断是否重新打开
+ private String confID;
+ private boolean isVoice2Video;
+
+ private int mOrientation = 1;
+ private ScheduledExecutorService scheduledExecutorService;
+ private MyScheduleThread myScheduleThread;
+ private boolean isFirstStart = true;
+ private boolean isPressTouch = false;
+ private boolean isShowBar = true;
+ private QuickPopup pop;
+ private List svcMemberList = new ArrayList<>();
+ private boolean isConfConnect = false;
+ private CallInfo callInfo;
+ private boolean isConf = false;
+ private boolean isCallConnect = false;
+ private static final int OVERLAY_PERMISSION_REQ_CODE = 1002;
+ private static final int CAPTURE_PERMISSION_REQ_CODE = 1003;
+ private volatile boolean isAuxData = false;
+ private boolean isRemote = true;
+ private TsdkConference tsdkConference; //会议加入是否成功 可获取 TsdkCallInfo 以判断会议类型
+ MyWindowManager myWindowManager; //悬浮窗对象
+ private FrameLayout rootFloatView; //悬浮窗父布局
+ private boolean AuxStateChange = false; //副流状态改变InvitedPointCallActivity
+ //只针对点对点通信过程
+ private boolean showLocalVideo = true;
+ int currentMillers = 0; //计时器从0开始
+ private List fragments;
+ private MySponsorPageAdapter adapter; //ViewPager的适配器
+ public int tempRedusChecked = 0; //记录ViewPager翻到哪一页了
+ private SponsorMeetingPresenter mPresenter;
+ private int svcPages = 0;
+ private boolean isSVCMeeting = false;
+
+ private MyTsdkCallInfo myTsdkCallInfo;
+ private Intent mAuxIntent;
+ private String meetingTitle = "";
+ private String meetingId = "";//会议id 用于二次拨号
+ LocalBroadcastManager localBroadcastManager;
+ private static final String TAG = SponsorMeetingActivity.class.getSimpleName();
+ LocalReceiver localReceiver;
+ private Member watch_member;//选看成员
+ private boolean cancle_broading = false;//被动取消广播
+ private boolean callConnection = false;//点对点是否已连接
+ private boolean flag = true;
+ private CloudLinkDialog shareCloudLinkDialog; // 抢共享时询问对话框
+ private SponsorMorePopWindow popWindow;
+ CloudLinkDialog LinkDialog;
+ // 会控
+ private IMeetingSignalController signalController;
+ private IMeetingTitleController titleController;
+ // 获取手机当前音量值
+ private int voiceNum;
+ private boolean manualDrag = false;//是否用户滑动
+ // .... 等等
+ private String[] broadcastNames = new String[]{
+ BroadcastConstant.ACTION_CALL_ROUTE_CHANGE,
+ BroadcastConstant.ACTION_CHECKINRESULT,
+ BroadcastConstant.ACTION_CALL_DEVICES_STATUS_CHANGE,
+ Constant.SPONSOR_MEETING_WATCH_MEMBER, //选看
+ BroadcastConstant.ACTION_CALL_MEDIA_CONNECTED,//点对点已连接
+ BroadcastConstant.ACTION_NO_STREAM_DURATION,//无码流事件
+ Constant.SPONSOR_MEETING_BROAD_MEMBER,// 广播
+ Constant.SPONSOR_MEETING_ROLL_MEMBER,// 点名
+ BroadcastConstant.ACTION_REQUEST_CHAIRMAN_RESULT,
+ BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL,// 网络状态变化
+ BroadcastConstant.ACTION_JOIN_CONF_SUCCESS_FINISH_SPONSOR, // 3.0 语音密码 进会后 关闭本页面
+ BroadcastConstant.ACTION_BACK_TO_DESK,
+ BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE,
+ BroadcastConstant.ACTION_CONF_BASE_INFO,
+ BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI,
+ BroadcastConstant.ACTION_CALL_STATE_RINGING,
+ BroadcastConstant.ACTION_CALL_STATE_IDLE,
+ BroadcastConstant.ACTION_CALL_STATE_OFFHOOK,
+ BroadcastConstant.ACTION_CALL_END,
+ BroadcastConstant.ACTION_HAS_HANDUP_MEMBER,
+ BroadcastConstant.ACTION_ADD_LOCAL_VIEW,
+ BroadcastConstant.ACTION_RELEASE_CHAIRMAN_RESULT,
+ BroadcastConstant.ACTION_CHANGE_SC,
+ BroadcastConstant.ACTION_MEETING_SUBTITLE_SWITCH,
+ BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_ENABLE,
+ BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_STATUS,
+ BroadcastConstant.ACTION_EVT_LOCK_SCREEN_NOTIFICATION,//手机锁屏
+ BroadcastConstant.ACTION_CONF_CALL_FAILED, //会控失败
+ BroadcastConstant.ACTION_SESSION_MODIFIED_REFRESH
+ };
+ /**
+ * 网络差,最后一次提示时间
+ */
+ private long mLastNetLevelToastTime;
+ private boolean mIsConfSubtitleEnable;
+ private boolean mIsSubtitleEnable;
+ @SuppressLint("NewApi")
+ LocalBroadcastReceiver receiver = (broadcastName, obj) -> {
+ LogUtil.zzz(TAG, "BroadcastReceiver:" + broadcastName);
+
+ switch (broadcastName) {
+ // 会控建立失败
+ case BroadcastConstant.ACTION_CONF_CALL_FAILED:
+ runOnUiThread(() -> showConfFailed((int) obj));
+ break;
+ case BroadcastConstant.ACTION_EVT_LOCK_SCREEN_NOTIFICATION:
+ // 锁屏状态关闭摄像头
+ if (EncryptedSPTool.getBoolean(Constant.IS_CLOSE_VIDEO) && !IS_SHOW_PARTICIPANTS) {
+ if (UIUtil.isScreenOff()) {
+ mPresenter.closeOrOpenCamera(true,
+ MeetingController.getInstance().cameraIndex ? 0 : 1);
+ LogUtil.zzz(TAG, "onReceive()", "The screen is off");
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_CALL_END:
+ LogUtil.zzz(TAG, "ACTION_CALL_END: 会议已结束");
+ runOnUiThread(() -> {
+ finish();
+ });
+ break;
+ case BroadcastConstant.ACTION_JOIN_CONF_SUCCESS_FINISH_SPONSOR:
+ finish();
+ break;
+ case BroadcastConstant.ACTION_CALL_ROUTE_CHANGE:
+ runOnUiThread(() -> {
+ LogUtil.d(TAG, "ACTION_CALL_ROUTE_CHANGE" + obj);
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ });
+ break;
+ case Constant.SPONSOR_MEETING_WATCH_MEMBER: //选看 && 取消选看
+ if (obj instanceof Member) {
+ Member member = (Member) obj;
+ LogUtil.zzz("选看 number == " + LogUtil.commonDisplay(member.getNumber()));
+ MeetingController.getInstance().setWatchMember(true);
+ MeetingController.getInstance().setWatchingMember(member);
+ BaseAppContext.getInstance().setMember(member);
+ MeetingController.getInstance().setIsPassiveWatchingMember(null);
+ watchAttendee(member, true);
+
+ if (isAuxData) {
+ if (tempRedusChecked != 0) {
+ onPageSelected(1);
+ }
+ } else {
+ onPageSelected(0);
+ }
+ //选看成功三端统一不做提示
+// ToastUtil.toast(getString(R.string.cloudLink_meeting_chooseToSeeSuccess));
+ } else {
+ LogUtil.zzz("SPONSOR_MEETING_WATCH_MEMBER", "取消选看");
+ MeetingController.getInstance().setWatchMember(false);
+ MeetingController.getInstance().setWatchingMember(null);
+ watchAttendee(null, true);
+ }
+ break;
+ case Constant.SPONSOR_MEETING_BROAD_MEMBER: //广播 && 取消广播
+ if (isSVCMeeting) {
+ if (obj instanceof Member) {
+ Member member = (Member) obj;
+ LogUtil.zzz("广播 number == " + LogUtil.commonDisplay(member.getNumber()));
+ watch_member = member;
+ } else {
+ LogUtil.zzz("取消广播");
+ watch_member = null;
+ }
+ scrolltomain();
+ } else {
+ if (svcMemberList.size() == 2) {
+ for (Member member : svcMemberList) {
+ if (!member.isSelf()) {
+ LogUtil.zzz("AVC2人取消广播");
+ watchAttendee(member, false);
+ }
+ }
+ }
+ }
+ break;
+ case Constant.SPONSOR_MEETING_ROLL_MEMBER://点名 && 取消点名
+ if (obj instanceof Member) {
+ Member member = (Member) obj;
+ watchAttendee(member, true);
+ } else {
+ watchAttendee(null, true);
+ }
+ break;
+ case BroadcastConstant.ACTION_CALL_MEDIA_CONNECTED://点对点音视频通道建立
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ CallInfo info = (CallInfo) obj;
+ Log.d(TAG, "CALL_MEDIA_CONNECTED: " + info.isVideoCall());
+ if (callInfo == null) {
+ LogUtil.e(TAG, "runOnUiThread callInfo is null");
+ return;
+ }
+
+ // 匿名链接入会不走updateView中的麦克风回调,需要根据状态设置
+ if (EncryptedSPTool.getBoolean(Constant.IS_LOGOUT)) {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ MeetingMgrV2.getInstance().muteAttendee(self, self.isMute());
+ }
+
+ //判断是否是音频布局
+ if (rlVoice.getVisibility() == VISIBLE) {
+ closeOrOpenFloat(true);
+ } else {
+ if (!MeetingController.getInstance().isVMR && !(MeetingMgrV2.getInstance().isFlag() && isAuxData && tempRedusChecked == 0)) {
+ closeOrOpenFloat(false);
+ }
+ }
+ if (!MeetingMgrV2.getInstance().isFlag()) {
+ rlFooter.setVisibility(VISIBLE);
+ MeetingController.getInstance().meetingBottomChanged(VISIBLE);
+ } else {
+ if (MeetingController.getInstance().isAuditDirSpeak && !(MeetingMgrV2.getInstance().isFlag() && isAuxData && tempRedusChecked == 0)) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ }
+ if (callInfo != null && !isVoice2Video) {
+ mPresenter.closeOrOpenCamera(!MeetingController.getInstance().isVideoOpen, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ titleController.setTitleVisibility(VISIBLE);
+ if (MeetingController.getInstance().isCallComing() || MeetingController.getInstance().isCallOutGoing()) {
+ titleController.startTimer();
+ }
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_REQUEST_CHAIRMAN_RESULT:
+ ControlOperationsResults.ChairmanResult requestChairmanResult = (ControlOperationsResults.ChairmanResult) obj;
+ if (requestChairmanResult.getResult() != 0) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ handleChairmanResult(Constant.ParticipantEvent.REQUEST_CHAIRMAN_FAILED, requestChairmanResult.getName(), requestChairmanResult.getResult());
+ }
+ return;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ handleChairmanResult(Constant.ParticipantEvent.REQUEST_CHAIRMAN_SUCCESS, requestChairmanResult.getName(), requestChairmanResult.getResult());
+ }
+ MeetingController.getInstance().setChairman(true);
+ break;
+ case BroadcastConstant.ACTION_RELEASE_CHAIRMAN_RESULT:
+ ControlOperationsResults.ChairmanResult releasechairmanResult = (ControlOperationsResults.ChairmanResult) obj;
+ if (releasechairmanResult.getResult() != 0) {
+ MeetingController.getInstance().setChairman(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ handleChairmanResult(Constant.ParticipantEvent.RELEASE_CHAIRMAN_FAILED, releasechairmanResult.getName(), releasechairmanResult.getResult());
+ }
+ return;
+ }
+ MeetingController.getInstance().setChairman(false);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ handleChairmanResult(Constant.ParticipantEvent.RELEASE_CHAIRMAN_SUCCESS, releasechairmanResult.getName(), releasechairmanResult.getResult());
+ }
+ break;
+ case BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL:
+ int netLevel = (int) obj;
+ LogUtil.zzz("网络状态 监听", netLevel);
+ if (titleController != null) {
+ titleController.updateNetworkQuality(netLevel, false);
+ }
+ DialogUtil.updateNetworkQuality(netLevel);
+ updateNetworkQuality(netLevel, false);
+ MeetingTitleBarController.mCurrentNetLevel = netLevel;
+ boolean isToast = (System.currentTimeMillis() - mLastNetLevelToastTime) > Constant.NET_LEVEL_MIN_TOAST_INTERVAL;
+ if (isToast && netLevel <= Constant.NET_LEVEL_MIN_TOAST) {
+ mLastNetLevelToastTime = System.currentTimeMillis();
+ runOnUiThread(() -> ToastUtils.show(getResources().getString(R.string.cloudLink_net_work_weak)));
+ if (myTsdkCallInfo != null && !TextUtils.isEmpty(myTsdkCallInfo.getSubject())) {
+ titleController.setTitleInfoVisibility(VISIBLE);
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_NO_STREAM_DURATION: // 无码流事件
+ int time = (int) obj;
+ LogUtil.zzz("无码流--视频:", time);
+ if (time >= 30) {
+ runOnUiThread(() -> {
+ if (isConf) {
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, true);
+ mPresenter.leaveConf();
+ } else {
+ if (callInfo != null) {
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, true);
+ CallMgrV2.getInstance().endCall(callInfo.getCallID());
+ }
+ }
+ MeetingController.getInstance().isVideoOpen = true;
+ });
+ } else if (time % 10 == 0) {
+ runOnUiThread(() -> ToastUtils.show(getResources().getString(R.string.cloudLink_no_stream_duration_time)));
+ }
+ break;
+ case BroadcastConstant.ACTION_CHECKINRESULT:
+ runOnUiThread(() -> ToastUtils.show(getResources().getString(R.string.cloudLink_meeting_confCheckInSuccess)));
+ break;
+ case BroadcastConstant.ACTION_BACK_TO_DESK:
+ if (!MeetingController.getInstance().isAux() && !UIUtil.isScreenOff()) {
+ if (!Settings.canDrawOverlays(this)) {
+ mandatoryCloseCamera();
+ }
+ minimize(rlVoice.getVisibility() == VISIBLE ? ConferenceConstant.VIDEO_VOICE_LAYOUT_CALL : ConferenceConstant.VIDEO_CALL, false, true);
+ }
+ break;
+ case BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE:
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ isAuxData = (Boolean) obj; //true 收到辅流 false 停止发送辅流
+ tempRedusChecked = 0;
+ rb1.setChecked(true);
+ rb2.setChecked(false);
+ rb3.setChecked(false);
+ LogUtil.zzz(TAG, "是否有辅流 isAuxData= " + isAuxData);
+ if (isAuxData) {
+ VideoMgr.getInstance().clearLocGroup();
+ closeOrOpenFloat(true);
+ adapter = null;
+ initData();
+ TimerUtil.delay(10, () -> {
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
+ });
+ } else {
+ tvShare.setText(R.string.cloudLink_meeting_share);
+ if (shareCloudLinkDialog != null && shareCloudLinkDialog.isShowing()) {
+ shareCloudLinkDialog.dismiss();
+ }
+ isCallConnect = true;
+ adapter = null;
+ initData();
+ if (MeetingController.getInstance().getTempFloat() != 2 && svcMemberList != null && svcMemberList.size() > 1 && MeetingController.getInstance().isInMeetingPage) {
+ if (MeetingMgrV2.getInstance().isFlag()) {
+ if (MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ } else {
+ closeOrOpenFloat(false);
+ }
+ } else {
+ if (MeetingMgrV2.getInstance().isFlag() && MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ }
+ }
+ finishActivity(CAPTURE_PERMISSION_REQ_CODE);
+ scrollToFirstPage();
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_CONF_BASE_INFO:
+ TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd = (TsdkOnEvtConfBaseInfoInd) obj;
+ updataBaseInfo(tsdkOnEvtConfBaseInfoInd);
+ break;
+ case BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI: // 有噪音
+ runOnUiThread(() -> closeVoiceUI((Boolean) obj));
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_IDLE:
+ if (isConf) {
+ mPresenter.endMobileCall(true);
+ } else {
+ if (callInfo != null) {
+ mPresenter.endMobileCall(false);
+ } else {
+ LogUtil.e(TAG, "tv_mute callInfo is null");
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_RINGING:
+ if (isConf) {
+ mPresenter.receivedMobileCall(true, false);
+ } else {
+ if (callInfo != null) {
+ mPresenter.receivedMobileCall(false, false);
+ } else {
+ LogUtil.e(TAG, "tv_mute callInfo is null");
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_HAS_HANDUP_MEMBER:
+ if (obj instanceof TsdkNotifyHandUpAttendee) {
+ runOnUiThread(() -> {
+ if (((TsdkNotifyHandUpAttendee) obj).getStatusInfo().getIsSelf() != 1 && ((TsdkNotifyHandUpAttendee) obj).getStatusInfo().getIsHandup() == 1) {
+ ToastUtils.show(((TsdkNotifyHandUpAttendee) obj).getBaseInfo().getDisplayName() + getString(R.string.cloudLink_meeting_raiseHanding));
+ }
+ });
+ }
+ break;
+ case BroadcastConstant.ACTION_CHANGE_SC:
+ runOnUiThread(() -> {
+ if (obj instanceof TsdkOnEvtDeviceStateNotify.Param) {
+/* //SC倒换后刷新选看
+ Member cacheMember=App.getInstance().getMember();
+ MeetingController.getInstance().setWatchMember(true);
+ MeetingController.getInstance().setWatchingMember(cacheMember);
+ watchAttendee(cacheMember, true);
+ if (isAuxData) {
+ if (tempRedusChecked!=0){
+ onPageSelected(1);
+ }
+ } else {
+ onPageSelected(0);
+ }
+ LogUtil.zzz("sc_change " ,"refresh");*/
+ mPresenter.syncState((TsdkOnEvtDeviceStateNotify.Param) obj);
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_NEED_UPDATE_MUTE_STATE:
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Member member = (Member) obj;
+ CallMgrV2.getInstance().muteMic(MeetingController.getInstance().getCallId(), false);
+ if (MeetingController.getInstance().getMuteState() == member.isMute()) {
+ MeetingMgrV2.getInstance().muteAttendee(member, !MeetingController.getInstance().getMuteState());
+ }
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_SC:
+
+ break;
+ case BroadcastConstant.ACTION_ADD_LOCAL_VIEW:
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ MeetingController.getInstance().setHasExecutedRefreshView(false);
+ LogUtil.zzz("LocalHideView", "case BroadcastConstant.ACTION_ADD_LOCAL_VIEW 刷新本地画面");
+ mPresenter.setHideViewContainer(hideVideoView);
+ }
+ });
+
+ break;
+ case BroadcastConstant.ACTION_MEETING_SUBTITLE_SWITCH:
+ int result = (int) obj;
+ LogUtil.zzz(TAG, "ACTION_MEETING_SUBTITLE_SWITCH result = " + result);
+ if (result == 0) {
+ mIsSubtitleEnable = !mIsSubtitleEnable;
+ runOnUiThread(this::refreshMorePopView);
+ }
+ break;
+ case BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_ENABLE:
+ int enable = (int) obj;
+ LogUtil.zzz(TAG, "ACTION_EVT_MEETING_SUBTITLE_ENABLE enable = " + enable);
+ mIsConfSubtitleEnable = enable == 1;
+ runOnUiThread(this::refreshMorePopView);
+ break;
+ case BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_STATUS:
+ if (!mIsConfSubtitleEnable) {
+ return;
+ }
+ int status = (int) obj;
+ LogUtil.zzz(TAG, "ACTION_EVT_MEETING_SUBTITLE_STATUS status = " + status);
+ boolean isSubtitleEnable = status == 1;
+ if (isSubtitleEnable == mIsSubtitleEnable) {
+ return;
+ }
+ mIsSubtitleEnable = isSubtitleEnable;
+ runOnUiThread(this::refreshMorePopView);
+ break;
+ // 会话类型修改后,刷新窗口选看
+ case BroadcastConstant.ACTION_SESSION_MODIFIED_REFRESH:
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ adapter = null;
+ initData();
+ }
+ });
+ break;
+ default:
+ break;
+ }
+ };
+
+ private void showConfFailed(int result) {
+ String msg = "";
+ switch (result) {
+ case TSDKErrorConstant.TSDK_JOIN_CONF_CERTIFICATE_FAILED:
+ msg = getString(R.string.cloudLink_conf_certificate_failed);
+ break;
+ case TSDKErrorConstant.TSDK_JOIN_CONF_BFCP_FAILED:
+ msg = getString(R.string.cloudLink_conf_bfcp_failed);
+ break;
+ case TSDKErrorConstant.TSDK_JOIN_CONF_CONTROL_FAILED:
+ msg = getString(R.string.cloudLink_conf_control_failed);
+ break;
+ default:
+ break;
+ }
+ DialogUtil.tipsDialog(this, msg, getString(R.string.cloudLink_sure),
+ () -> finish());
+ }
+
+ private String vmrNumber;
+
+ private void updataBaseInfo(TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd) {
+ if (tsdkOnEvtConfBaseInfoInd == null) {
+ LogUtil.zzz("tsdkOnEvtConfBaseInfoInd is null");
+ return;
+ }
+ if (tsdkOnEvtConfBaseInfoInd.param == null) {
+ LogUtil.zzz("tsdkOnEvtConfBaseInfoInd.param is null");
+ return;
+ }
+ if (tsdkOnEvtConfBaseInfoInd.param.confBaseInfo == null) {
+ LogUtil.zzz("tsdkOnEvtConfBaseInfoInd.param.confBaseInfo is null");
+ return;
+ }
+ String s = tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getAccessNumber() + "*" + VMRPwd;
+ myTsdkCallInfo.setSubject(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getSubject());
+// LogUtil.zzz("updataBaseInfo", s);
+ UIUtil.runUI(() -> {
+ convertMeetingId(s);
+ titleController.setEnabledMinBtn(true);
+ titleController.showMinBtn();
+ titleController.setTitleInfoVisibility(VISIBLE);
+ MeetingController.getInstance().setJoinConfSuccess(true);
+ rlFooter.setVisibility(VISIBLE);
+// //二次拨号,重新计时
+// if (MeetingController.getInstance().isFirstResetTimer()){
+// MeetingController.getInstance().meetingStartTime = System.currentTimeMillis();
+// titleController.stopTimer();
+// titleController.startTimer();
+// //BaseInfo 执行两次重置时间只执行一次
+// MeetingController.getInstance().setFirstResetTimer(false);
+// }
+
+ });
+ }
+
+ public void setHorizontal(boolean b) {
+// LogUtil.zzz("setHorizontal", "横竖屏", b + " " + LogUtil.getInstance().getSimpleExtraInfo());
+ if (b) {
+ //avc会议强制横屏
+ if (!isSVCMeeting) {
+ if (isConf) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);//横屏
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏
+ }
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER);//横屏和竖屏
+ }
+ } else {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏
+ }
+ }
+
+ @Override
+ public void setRequestedOrientation(int requestedOrientation) {
+// LogUtil.zzz("requestedOrientation", "横竖屏", requestedOrientation);
+ // avc会议强制横屏
+ if (isConf && !isSVCMeeting) {
+ if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
+ requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
+ super.setRequestedOrientation(requestedOrientation);
+ }
+ } else if (rlVoice.getVisibility() == View.VISIBLE) {
+ if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
+ super.setRequestedOrientation(requestedOrientation);
+ }
+ } else {
+ super.setRequestedOrientation(requestedOrientation);
+ }
+ }
+
+ boolean isMiniback() {
+ return getIntent().getBooleanExtra("isMiniback", false);
+ }
+
+ @Override
+ protected void initIntent() {
+ getWindow().addFlags(
+// WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED //锁屏显示
+// | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD //解锁
+// |
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON //保持屏幕不息屏
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);//点亮屏幕
+ boolean isCallEnd = getIntent().getBooleanExtra("isCallEnd", false);
+ if (isCallEnd) {
+ finish();
+ }
+ }
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_spponaor_meeting;
+ }
+
+ private boolean isAuxBack() {
+ return getIntent().getBooleanExtra(Constant.AUX, false);
+ }
+
+ private void setEnable(View child, Boolean isEnable) {
+ if (child instanceof ViewGroup) {
+ for (int i = 0; i < ((ViewGroup) child).getChildCount(); i++) {
+ setEnable(((ViewGroup) child).getChildAt(i), isEnable);
+ }
+ } else {
+ child.setEnabled(isEnable);
+ }
+ }
+
+ /**
+ * 跳到与会者界面
+ *
+ * @param isAuto 是否自动打开邀请界面
+ */
+ public void jumpParticipants(boolean isAuto) {
+ Log.d(TAG, "initListener: memberList = ");
+// if (svcMemberList != null && svcMemberList.size() > 0) {
+//// Intent intent = new Intent(SponsorMeetingActivity.this, ParticipantsActivity.class);
+// Intent intent = new Intent(SponsorMeetingActivity.this, ParticipantsActivityV2.class);
+// intent.putExtra(Constant.CONF_ID, confID);
+//// if (MeetingController.getInstance().isChairman()) {
+//// intent.putExtra(Constant.CONF_LIST_DATA, (Serializable) memberList);
+//// } else {
+//// intent.putExtra(Constant.CONF_LIST_DATA, (Serializable) svcMemberList);
+//// }
+// intent.putExtra(Constant.WATCH_MEMBER, watch_member);
+// intent.putExtra(Constant.IS_VIDEO_CONF, true);
+// intent.putExtra(Constant.IS_AUX, isAuxData);
+// intent.putExtra(Constant.IS_AUTO_JUMP, isAuto);
+// intent.putExtra(Constant.IS_SVC_MEETING, isSVCMeeting);
+// IS_SHOW_PARTICIPANTS = true;
+// intent.putExtra(Constant.CAMERA_INDEX, MeetingController.getInstance().cameraIndex ? CallConstant.BACK_CAMERA : CallConstant.FRONT_CAMERA);
+// LogUtil.zzz("open ParticipantsActivity");
+// startActivityForResult(intent, Constant.WATCH_ATTENDEE_REQUEST_CODE);
+// MeetingController.getInstance().isInMeetingPage = false;
+// }
+ }
+
+ private void initListener() {
+ tvParticipants.setOnClickListener(new NoDoubleClickListener() {
+ @Override
+ public void onNoDoubleClick(View view) {
+ jumpParticipants(false);
+ }
+ });
+
+ if (isConf) {
+ if (myTsdkCallInfo == null) {
+ LogUtil.e(TAG, "initListener isConf myTsdkCallInfo is null ");
+ return;
+ }
+ if (MeetingController.getInstance().isSponsorConf()) { // 发起,预约等
+ boolean isVideoOpen = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_VIDEO");
+ mPresenter.closeOrOpenCamera(!isVideoOpen, myTsdkCallInfo.getCallId(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ if (!isVideoOpen) {
+ setFloatBG();
+ }
+ } else {
+ mPresenter.closeOrOpenCamera(true, myTsdkCallInfo.getCallId(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ setFloatBG();
+
+ }
+ }
+ vedioVp.setCanRightScrollListener((dx) -> {
+ int canPage = isAuxData ? 2 : 1;
+ boolean canRight = vedioVp.getCurrentItem() >= canPage;
+ boolean noRightScroll = MeetingController.getInstance().isSvcBigConf() && !MeetingController.getInstance().isChairman() && canRight;
+ if (noRightScroll && dx < 0) {
+ ToastUtils.show(getString(R.string.cloudLink_svc_big_conf_hint));
+ }
+ return noRightScroll;
+ });
+ }
+
+ private Handler handler = new Handler();
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ if (isConf) {
+ if (!isMiniback()) {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (MeetingController.getInstance().isSponsorConf()) { // 发起,预约等
+ boolean isAudio = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ LogUtil.zzz("本地状态isAudio" + isAudio);
+ if (!isFirstMic && isAudio) {
+ MeetingMgrV2.getInstance().muteAttendee(self, false);
+ } else {
+ MeetingMgrV2.getInstance().muteAttendee(self, true);
+ }
+ } else {
+ MeetingMgrV2.getInstance().muteAttendee(self, true);
+ }
+ MeetingController.getInstance().setSponsorConf(false);
+ }
+ if (PhoneUtil.isTelephonyCalling()) {
+ mPresenter.receivedMobileCall(true, true);
+ }
+ } else {
+ CallFunc callFunc = CallFunc.getInstance();
+ boolean currentMuteStatus = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ if (MeetingController.getInstance().isSponsorConf()) { // 发起,预约等
+ currentMuteStatus = isFirstMic || !currentMuteStatus;
+ } else {
+ currentMuteStatus = true;
+ }
+ if (null != callInfo) {
+ if (CallMgrV2.getInstance().muteMic(callInfo.getCallID(), currentMuteStatus)) {
+ callFunc.setMuteStatus(currentMuteStatus);
+ updateView(CALL_UPDATE_VOICE, currentMuteStatus, "");
+ }
+ }
+ }
+ }
+ };
+
+ private void registerBroadcast() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(WATCH_MYSELF_CAMERA_STATE);
+ localReceiver = new LocalReceiver();
+ localBroadcastManager = LocalBroadcastManager.getInstance(this);
+ localBroadcastManager.registerReceiver(localReceiver, intentFilter);
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ }
+
+ @Override
+ public void initData() {
+ LogUtil.zzz(TAG, "initData");
+ setWatchLableSsrc();
+ if (adapter != null) {
+ refreshAdapter();
+ return;
+ }
+ if (isSVCMeeting) {
+ SvcMeetingInit();
+ } else {
+ adapter = new MySponsorPageAdapter(getSupportFragmentManager(), isAuxData, svcMemberList, 0, false, myTsdkCallInfo, null);
+ }
+ vedioVp.setAdapter(adapter);
+ rbGoneOrVisible();
+ vedioVp.setCurrentItem(tempRedusChecked);
+ LogUtil.zzz("initData tempRedusChecked = " + tempRedusChecked);
+ voiceNum = CallMgrV2.getInstance().getMediaSpeakVolume();
+ }
+
+ private void setWatchLableSsrc() {
+ LogUtil.zzz(TAG, "setWatchLableSsrc");
+ if (myTsdkCallInfo != null && ListTools.isThan(MeetingController.getInstance().getSvcLableSsrc(), 0)) {
+ myTsdkCallInfo.setSsrcTableStart(MeetingController.getInstance().getSvcLableSsrc().get(0).getLableSsrcData());
+ myTsdkCallInfo.setSsrcTableEnd(MeetingController.getInstance().getSvcLableSsrc().get(1).getLableSsrcData());
+ }
+ }
+
+ @Override
+ protected BasePresenterV2 createPresenter() {
+ return null;
+ }
+
+ private void refreshAdapter() {
+ LogUtil.zzz(TAG, "refreshAdapter");
+ List videoList = new ArrayList<>(svcMemberList);
+ boolean watchMemberIsInRoom = false;
+ int videoSize = videoList.size();
+ Iterator iterator = videoList.iterator();
+ while (iterator.hasNext()) {
+ Member member = iterator.next();
+ if (member.getStatus() != IN_CONF) {
+ videoSize--;
+ iterator.remove();
+ } else if (member.getIsAudio()) {
+ videoSize--;
+ } else if (!watchMemberIsInRoom && watch_member != null && watch_member.getNumber().equals(member.getNumber())) {
+ watchMemberIsInRoom = true;
+ }
+ }
+ if (!watchMemberIsInRoom) {
+ watch_member = null;
+ }
+ if (videoSize > 0) {
+ if (videoSize == 1) {
+ svcPages = 0;
+ } else if ((videoSize - 1) % 3 == 0) {
+ svcPages = (videoSize - 1) / 3;
+ } else {
+ svcPages = (videoSize - 1) / 3 + 1;
+ }
+
+ int totalPages = svcPages + (isAuxData ? 2 : 1);
+ if (tempRedusChecked > totalPages) {
+ tempRedusChecked = totalPages;
+ }
+ refreshIndicator(tempRedusChecked);
+ }
+ svcPages = isSVCMeeting ? svcPages : 0;
+ adapter.refreshMemberListAndPage(isAuxData, videoList, svcPages, isSVCMeeting, myTsdkCallInfo, watch_member);
+ rbGoneOrVisible();
+ }
+
+ /**
+ * SVC 会议需要的初始化步骤
+ *
+ * 2020/10/13 SVC 语音入会不显示在画中画和画廊中
+ */
+ private void SvcMeetingInit() {
+ Log.d(TAG, "SvcMeetingInit: ---start---");
+ List videoList = new ArrayList<>(svcMemberList);
+ if (videoList.size() > 0) {
+ if (videoList.size() == 1) {
+ svcPages = 0;
+ } else if ((videoList.size() - 1) % 3 == 0) {
+ svcPages = (videoList.size() - 1) / 3;
+ } else {
+ svcPages = (videoList.size() - 1) / 3 + 1;
+ }
+ adapter = new MySponsorPageAdapter(getSupportFragmentManager(), isAuxData, videoList, svcPages, true, myTsdkCallInfo, watch_member);
+ } else {
+ adapter = new MySponsorPageAdapter(getSupportFragmentManager(), isAuxData, new ArrayList<>(), 0, true, myTsdkCallInfo, watch_member);
+ }
+ }
+
+ /**
+ * 需要权限的悬浮窗的初始化
+ */
+ @SuppressLint("ClickableViewAccessibility")
+ private void initFloatWindow() {
+// if (Build.VERSION.SDK_INT >= 23) {
+// if (Settings.canDrawOverlays(this)) {
+// initFloat(); //有悬浮窗权限
+// } else {
+ myWindowManager = MyWindowManager.getInstance();
+ rootFloatView = findViewById(R.id.local_video);
+ rootFloatView.setOnTouchListener(new OnDragTouchListener());
+// }
+// } else {
+// initFloat();
+// }
+ }
+
+ /**
+ * @param intent 上下文
+ * @author liukaixiang
+ */
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ LogUtil.zzz(TAG, "onNewIntent");
+ if (intent != null && intent.getExtras() != null) {
+ MyTsdkCallInfo callInfo = (MyTsdkCallInfo) intent.getExtras().getSerializable(Constant.MY_CONF_INFO);
+ if (callInfo != null && (myTsdkCallInfo == null || callInfo.isSVC() != myTsdkCallInfo.isSVC())) {
+ myTsdkCallInfo = callInfo;
+ isSVCMeeting = myTsdkCallInfo.isSVC();
+ if (!isSVCMeeting && isConf) {
+ VideoMgr.getInstance().changeRotation(0);
+ }
+ //SVC会走2次joinconfresult回调,第一次为avc,设为横屏,第二次为svc,需要手动把横屏刷新为竖屏
+ if (isSVCMeeting) {
+ super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ restLocWindow(false);
+ }
+ } else {
+ myTsdkCallInfo = callInfo;
+ }
+ }
+// setEnable(rlFooter, true);
+ setEnable(rl_footer_audit_dir, true);
+// new AudioRouteManager.Builder()
+// .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+// .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+// .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+// .build();
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ LogUtil.zzz("收到会控结果回调");
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void initView() {
+ LogUtil.UIAction("SponsorMeeting:initView()");
+ if (MeetingController.getInstance().finishSp) {
+ MeetingController.getInstance().finishSp = false;
+ }
+ mConfControl.setOnClickListener(view -> LogUtil.zzz(TAG, "mConfControl is clicked"));
+ mPresenter = new SponsorMeetingPresenter(this, this);
+ mPresenter.registerBroadcast();
+ registerBroadcast();
+
+ if (isMiniback() || isAuxBack()) {
+ LogUtil.zzz(TAG, "isAuxBack: 结束共享");
+ initMiniData();
+ }
+
+ LogUtil.zzz(TAG, "initView: start---------------");
+ vmrNumber = EncryptedSPTool.getString(Constant.VMR_ACCESSNUMBER) + EncryptedSPTool.getString(Constant.VMR_CONF_ID);
+ initSignalController();
+ initTitleController();
+ titleController.updateNetworkQuality(MeetingTitleBarController.mCurrentNetLevel, true);
+ updateNetworkQuality(MeetingTitleBarController.mCurrentNetLevel, true);
+ DialogUtil.updateNetworkQuality(MeetingTitleBarController.mCurrentNetLevel);
+ // initBottomController();
+ isConfConnect = getIntent().getBooleanExtra(Constant.CONF_IS_CONNECT, false);
+ if (getIntent().getExtras() != null) {
+ myTsdkCallInfo = (MyTsdkCallInfo) getIntent().getExtras().getSerializable(Constant.MY_CONF_INFO);
+ }
+ if (!TextUtils.isEmpty(getIntent().getStringExtra(Constant.DIAL))) {
+ callInfo = (CallInfo) getIntent().getSerializableExtra(Constant.CALL_INFO);
+ updateView(Constant.UPDATE_CALL_CONNECT, false, callInfo);
+ new AudioRouteManager.Builder()
+ .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .build();
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ }
+
+ // TODO
+ if (myTsdkCallInfo != null) { // 视频加入会议
+ boolean isConfControlEnable = getIntent().getBooleanExtra(Constant.MEETING_CONF_CONTROL_ENABLE, false);
+ mConfControl.setVisibility(isConfControlEnable ? VISIBLE : View.GONE);
+ meetingId = myTsdkCallInfo.getPeerNumber();
+ if (!TextUtils.isEmpty(meetingId) && meetingId.contains("*")) {
+ String[] split = meetingId.split("\\*");
+ if (split != null && split.length > 1) {
+ VMRPwd = meetingId.split("\\*")[1];
+ } else {
+ VMRPwd = "";
+ }
+ convertMeetingId("");
+ } else {
+ convertMeetingId(meetingId);
+ }
+ isConf = myTsdkCallInfo.isConf();
+ MeetingController.getInstance().isConf = isConf;
+ isSVCMeeting = myTsdkCallInfo.isSVC();
+ }
+ // 获取屏幕方向
+ Configuration configuration = this.getResources().getConfiguration();
+ mOrientation = configuration.orientation;
+ VideoMgr.getInstance().changeRotation(mOrientation);
+ EncryptedSPTool.putBoolean(Constant.IS_CLOSE_VIDEO, true); // AVC会议退桌面默认关闭摄像头
+ initFloatWindow();
+ setData(isConf, null);
+ boolean isCallVideoToVoice = getIntent().getBooleanExtra(Constant.CALL_VIDEO_TO_VOICE, false);
+ // 判断是否是会议
+ if (!isConf) {
+ callConnection = true;
+ MeetingController.getInstance().setConversation(true);
+ titleController.hideEncryptedImage();
+ Drawable top = ContextCompat.getDrawable(this, R.drawable.ic_mic);
+ Log.d(TAG, "静音调试 initView: 点对点设置为开音");
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top, null, null);
+ CallInfo callInfo = (CallInfo) getIntent().getSerializableExtra(Constant.CALL_INFO);
+ if (callInfo != null) {
+ String meetingName = (String.format(getString(R.string.cloudLink_meeting_calllWith), getDisplayName(callInfo)));
+ String meetingId = (UIUtil.splitString(callInfo.getPeerNumber()));
+ MeetingController.getInstance().setTerminal(callInfo.getPeerNumber());
+ tvVoiceDisplayName.setText(meetingName);
+ tvVoiceUserNumber.setText(meetingId);
+ titleController.displayMeetingName(meetingName);
+ if (!UIUtil.isService3() && EncryptedSPTool.getString(IS_VMR_2_ID).equals(vmrNumber)) {
+ titleController.displayMeetingId(UIUtil.splitString(vmrNumber));
+ } else {
+ titleController.displayMeetingId(meetingId);
+ }
+ titleController.startTimer();
+ if (isCallVideoToVoice) {
+ LogUtil.zzz("点对点视频语音接听", "");
+ this.callInfo = callInfo;
+ MeetingController.getInstance().isVideo = this.callInfo.isVideoCall();
+ MeetingController.getInstance().setTerminal(callInfo.getPeerNumber());
+ tv_name.setText(getDisplayName(callInfo));
+ setLayoutToVoice();
+ } else {
+ if (!callInfo.isVideoCall()) {
+ LogUtil.zzz("点对点语音被叫", "");
+ this.callInfo = callInfo;
+ MeetingController.getInstance().isVideo = this.callInfo.isVideoCall();
+ tv_name.setText(getDisplayName(callInfo));
+ setLayoutToVoice();
+ }
+ if (callInfo.isVideoCall()) {
+ LogUtil.zzz("点对点视频被叫", "");
+ this.callInfo = callInfo;
+ MeetingController.getInstance().isVideoOpen = true;
+
+ frbg.setVisibility(View.GONE);
+ }
+ }
+ setVoiceBackground(callInfo.getPeerDisplayName());
+ }
+ } else {
+ MeetingController.getInstance().setConversation(false);
+ if (!isMiniback() || !MeetingController.getInstance().isRealInConf()) {
+ titleController.setTitleInfoVisibility(View.GONE);
+ titleController.showMinBtn();
+ titleController.setEnabledMinBtn(true);
+ }
+ if (isSVCMeeting) {
+ LogUtil.zzz("SVC视频会议", "");
+ setHorizontal(false);
+ restLocWindow(false);
+ } else {
+ LogUtil.zzz("AVC视频会议", "");
+ setHorizontal(true);
+ restLocWindow(true);
+ }
+ }
+ int callType = getIntent().getIntExtra(Constant.VOICE_JOIN_TYPE, 0);
+ //点对点语音主叫
+ if (Constant.POINT_CALL == callType) {
+ LogUtil.zzz("点对点语音主叫", "");
+ setLayoutToVoice();
+ String dispalyName = UIUtil.formName(getIntent().getStringExtra(Constant.CALLED_NAME));
+ CallMgrV2.getInstance().startCall(getIntent().getStringExtra(Constant.VOICE_CALL_NUM), false
+ , TextUtils.isEmpty(getIntent().getStringExtra(Constant.CALLED_NAME))
+ ? getIntent().getStringExtra(Constant.VOICE_CALL_NUM)
+ : getIntent().getStringExtra(Constant.CALLED_NAME));
+ if (TextUtils.isEmpty(dispalyName)) {
+ tv_name.setText(getDisplayName(callInfo));
+ } else {
+ tv_name.setText(dispalyName);
+ tvVoiceDisplayName.setText(dispalyName);
+ tvVoiceUserNumber.setText(UIUtil.formName(getIntent().getStringExtra(Constant.VOICE_CALL_NUM)));
+ setVoiceBackground(dispalyName);
+ }
+ tv_confId.setText(R.string.cloudLink_meeting_calling);
+ tv_method.setVisibility(View.GONE);
+ linCallTool.setVisibility(View.GONE);
+ linCallingTool.setVisibility(View.VISIBLE);
+ rlVoiceTitle.setVisibility(View.GONE);
+ } else if (Constant.POINT_VIDEO == callType) {
+ LogUtil.zzz("点对点视频主叫", "");
+ if (TextUtils.isEmpty(UIUtil.formName(getIntent().getStringExtra(Constant.CALLED_NAME)))) {
+ titleController.displayMeetingName(getDisplayName(callInfo));
+ tvVoiceDisplayName.setText(getDisplayName(callInfo));
+ tvVoiceUserNumber.setText(getPeerNumber(callInfo));
+ } else {
+ titleController.displayMeetingName(UIUtil.formName(getIntent().getStringExtra(Constant.CALLED_NAME)));
+ tvVoiceDisplayName.setText(UIUtil.formName(getIntent().getStringExtra(Constant.CALLED_NAME)));
+ tvVoiceUserNumber.setText(UIUtil.formName(getIntent().getStringExtra(Constant.VIDEO_CALL_NUM)));
+ }
+ tvTip.setVisibility(View.VISIBLE);
+ rlFooter.setVisibility(View.GONE);
+ MeetingController.getInstance().meetingBottomChanged(View.GONE);
+ titleController.setTitleVisibility(View.VISIBLE);
+ MeetingController.getInstance().isVideoOpen = true;
+ closeOrOpenFloat(true);
+ }
+ //单向直播会议
+ if (MeetingMgrV2.getInstance().isFlag()) {
+ initAuditDirView();
+ }
+ vedioVp.addOnPageChangeListener(this);
+ Log.d(TAG, "initView: end---------------");
+ if (isMiniback()) {
+ convertMeetingId(getIntent().getStringExtra(SponsorMeetingConstant.CONF_ID));
+ }
+ if (!isMiniback() && PhoneUtil.isTelephonyCalling() && !isConf) {
+ mPresenter.receivedMobileCall(false, true);
+ }
+
+ initListener();
+ AppUtil.hideStatusBar(this);
+ /*
+ * 是否获得会议控制权
+ */
+// conferenceRight();
+ Log.d(TAG, "onCreate: end----------------");
+ if (TextUtils.isEmpty(getIntent().getStringExtra(Constant.DIAL))) {
+ rlFooter.setVisibility(View.GONE);
+ }
+ if (MeetingController.getInstance().isHasExecutedRefreshView()) {
+ MeetingController.getInstance().setHasExecutedRefreshView(false);
+ mPresenter.setHideViewContainer(hideVideoView);
+ }
+ updateTsDkMobileAudioRoute(SwitchAudioRouteManager.Companion.getInstance().getAudioRoute());
+ EncryptedSPTool.putBoolean(Constant.JOIN_MEETING_NOLOGIN, false);
+ }
+
+ /**
+ * 根据姓名显示背景图片
+ */
+ private void setVoiceBackground(String dispalyName) {
+ if (!TextUtils.isEmpty(dispalyName)) {
+ int index = FirstLetterUtil.getFirstLetter(dispalyName).toLowerCase().charAt(0) * 10;
+ bg_icon.setImageResource(R.drawable.profile_image_bg);
+ bg_icon.setImageLevel(index);
+ }
+ }
+
+ private String getDisplayName(CallInfo callInfo) {
+ if (callInfo == null) {
+ return "";
+ } else {
+ return TextUtils.isEmpty(callInfo.getPeerDisplayName()) ? callInfo.getPeerNumber() : callInfo.getPeerDisplayName();
+ }
+ }
+
+ private String getPeerNumber(CallInfo callInfo) {
+ return callInfo == null ? "" : callInfo.getPeerNumber();
+ }
+
+ /**
+ * title
+ */
+ private void initTitleController() {
+ titleController = new MeetingTitleBarController(this);
+ titleController.inflaterRoot(clVideo);
+ titleController.setTitleClickListener(new IMeetingTitleController.TitleClickListener() {
+
+ @Override
+ public void titleInfoClick() {
+ showTitleInfoDialog(false);
+ }
+
+ @Override
+ public void minimizeClick() {
+ // 视频最小化
+/* if (MeetingController.getInstance().isAux()) {
+ return;
+ }*/
+ minimize(ConferenceConstant.VIDEO_CALL, true, false);
+ }
+
+ @Override
+ public void exit() {
+
+ LogUtil.userAction("点击右上角离会");
+ // 离会
+ if (isConf) {
+ if (MeetingController.getInstance().isAux()) {
+ ToastUtils.show(getResources().getString(R.string.cloudLink_meeting_auxingError));
+ } else {
+ mPresenter.showLandLeaveConfBottomSheetDialog();
+ }
+
+ } else {
+ if (callInfo != null) {
+ CallMgrV2.getInstance().endCall(callInfo.getCallID());
+ }
+ }
+ MeetingController.getInstance().isVideoOpen = true;
+ }
+
+ @Override
+ public void timerCall(int hour, int minute, int second) {
+ String time = getResources().getString(R.string.cloudLink_meeting_callWaiting) + " " + unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second);
+ tv_method.setText(time);
+ }
+
+ });
+ }
+
+ /**
+ * 点击标题弹出的dialog
+ */
+ private void showTitleInfoDialog(boolean b) {
+ LogUtil.zzz(TAG, "showTitleInfoDialog" + b);
+ MeetingTitleInfoBean bean = new MeetingTitleInfoBean();
+ boolean ish = mOrientation == Configuration.ORIENTATION_LANDSCAPE;
+ if (isConf) {
+ bean.setSubject(MeetingMgrV2.getInstance().getSubject());
+ if (!UIUtil.isService3() && EncryptedSPTool.getString(IS_VMR_2_ID).equals(vmrNumber)) {
+ if (TextUtils.isEmpty(vmrNumber)) {
+ LogUtil.zzz("showTitleInfoDialog", "vmrNumber is empty " + MeetingController.getInstance().getConfId());
+ bean.setConfID(MeetingController.getInstance().getConfId());
+ } else {
+ bean.setConfID(vmrNumber);
+ LogUtil.zzz("showTitleInfoDialog", "vmrNumber" + vmrNumber);
+ }
+ } else {
+ LogUtil.zzz("showTitleInfoDialog",
+ "MeetingController.getInstance().getConfID()" + MeetingController.getInstance().getConfId());
+ bean.setConfID(MeetingController.getInstance().getConfId());
+ }
+ bean.setTime(DateUtil.fromMeetingTime(MeetingController.getInstance().getStartTime(),
+ MeetingController.getInstance().getEndTime()));
+ bean.setChairmanPwd(MeetingController.getInstance().getChairmanPwd());
+ bean.setGuestPwd(MeetingController.getInstance().getGuestPwd());
+ bean.setScheduleUserName(MeetingController.getInstance().getScheduleUserName());
+
+ LogUtil.zzz(TAG, "showTitleInfoDialog_isConf_bean" + bean.toString());
+ DialogUtil.meetingTitleInfoDialog(SponsorMeetingActivity.this, BaseConfContract.LAYOUT_CONF_VIDEO, ish, bean, v -> mPresenter.showBitStream(b));
+ } else {
+ bean.setSubject(titleController.getMeetingName());
+ bean.setTerminal(MeetingController.getInstance().getTerminal());
+
+ LogUtil.zzz(TAG, "showTitleInfoDialog_!isConf_bean" + bean.toString());
+ DialogUtil.meetingTitleInfoDialog(SponsorMeetingActivity.this, BaseConfContract.LAYOUT_POINT_VIDEO, ish, bean, v -> mPresenter.showBitStream(b));
+ }
+ }
+
+ /**
+ * 质量
+ */
+ private void initSignalController() {
+ signalController = new MeetingSignalController();
+ signalController.initView(this);
+ signalController.setAuxChangeListener(new IMeetingSignalController.IAuxChangeListener() {
+ @Override
+ public boolean isAux() {
+ return isAuxData || MeetingController.getInstance().isAux();
+ }
+
+ @Override
+ public boolean isAudio() {
+ return rlVoice.getVisibility() == View.VISIBLE;
+ }
+
+ @Override
+ public boolean isSvc() {
+ return isSVCMeeting;
+ }
+ });
+ }
+
+ /**
+ * 初始化单向直播布局
+ */
+ private void initAuditDirView() {
+ LogUtil.zzz("更新单向直播UI");
+ rl_footer_audit_dir.setVisibility(View.VISIBLE);
+ rlFooter.setVisibility(View.GONE);
+ tvMore.setVisibility(View.GONE);
+ UIUtil.setVisibility(this, R.id.tv_audit_dir_cancel_speak, true);
+ //动态获取底部布局的height,设置聊天室名称和底部的边距
+ int bottomViewHeight = rlBottomView.getHeight();
+ ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams) rlBottomView.getLayoutParams();
+ layoutParams.setMargins(15, 0, 0, bottomViewHeight + 15);
+ rlBottomView.setLayoutParams(layoutParams);
+ }
+
+ /**
+ * 设置为音频布局
+ */
+ void setLayoutToVoice() {
+ signalController.dismissDialog();
+ setHorizontal(false);
+ rlVoice.setVisibility(View.VISIBLE);
+ MeetingController.getInstance().isVoicePage = true;
+ clVideo.setVisibility(View.GONE);
+ closeOrOpenFloat(true);
+ }
+
+ private void initSenor() {
+// mPresenter.registerSenor();
+ }
+
+ /**
+ * 设置为视频布局?
+ */
+ void setLayoutToVideo() {
+ signalController.dismissDialog();
+ setHorizontal(true);
+ rlVoice.setVisibility(View.GONE);
+ MeetingController.getInstance().isVoicePage = false;
+ clVideo.setVisibility(View.VISIBLE);
+ if (isMiniback()) {
+ closeOrOpenFloat(!MeetingController.getInstance().isFloatWindowOpen);
+ } else {
+ closeOrOpenFloat(false);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ LogUtil.zzz(TAG, "onPause");
+ }
+
+ /**
+ * 处理屏幕旋转
+ */
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ Configuration configuration = this.getResources().getConfiguration();
+ mOrientation = configuration.orientation;
+ VideoMgr.getInstance().changeRotation(mOrientation);
+
+ LogUtil.zzz(TAG, "onConfigurationChanged", mOrientation + "");
+ if (signalController != null) {
+ signalController.onScreenRotation(rlVoice.getVisibility() == View.VISIBLE, isAuxData || MeetingController.getInstance().isAux());
+ }
+ if (pvTime != null && pvTime.isShowing()) {
+ pvTime.dismiss();
+ }
+ //改变小悬浮窗宽高
+ setFloatWandH();
+ tempRedusChecked = vedioVp.getCurrentItem();
+ LogUtil.zzz(TAG, "onConfigurationChanged", "isAuxData == " + isAuxData + "AuxStateChange == " + AuxStateChange);
+ if (isAuxData) {
+ AuxStateChange = true;
+ setData(isConf, null);
+ } else {
+ if (isConf) {
+ setData(isConf, null);
+ }
+ }
+ //屏幕宽高
+ if ((popWindow != null) && (popWindow.isShowing())) {
+ popWindow.dismiss();
+ }
+ if (DialogUtil.isShow()) {
+ TimerUtil.delay(200, () -> showTitleInfoDialog(rlVoice.getVisibility() == VISIBLE));
+ }
+ if (adapter != null) {
+ adapter.notifyDataSetChanged();
+ }
+ }
+
+ private void setData(boolean isConf, Object data) {
+ if (!isConf) {
+ iv_keyboard.setVisibility(View.GONE);
+ tvParticipants.setVisibility(View.GONE);
+ ivVoiceSetting.setVisibility(View.GONE);
+ titleController.setTimer("");
+ titleController.displayMeetingCount("");
+ String name = getIntent().getStringExtra(Constant.CALLED_NAME);
+// String name = MeetingController.getInstance().getTerminal();
+ if (name != null && !TextUtils.isEmpty(name)) {
+ titleController.displayMeetingName(String.format(getString(R.string.cloudLink_meeting_calllWith), name));
+ tv_name.setText(UIUtil.formName(name));
+ } else if (callInfo != null) {
+ titleController.displayMeetingName(String.format(getString(R.string.cloudLink_meeting_calllWith), getDisplayName(callInfo)));
+ tv_name.setText(TextUtils.isEmpty(callInfo.getPeerDisplayName()) ?
+ callInfo.getPeerNumber() : callInfo.getPeerDisplayName());
+ }
+ String id = getIntent().getStringExtra(Constant.VIDEO_CALL_NUM);
+ if (!isCallConnect) {
+ CallMgrV2.getInstance().startCall(id, true,
+ TextUtils.isEmpty(getIntent().getStringExtra(Constant.CALLED_NAME)) ?
+ getIntent().getStringExtra(Constant.VOICE_CALL_NUM)
+ : getIntent().getStringExtra(Constant.CALLED_NAME));
+ }
+ if (id != null && !TextUtils.isEmpty(id)) {
+ titleController.displayMeetingId(UIUtil.splitString(id));
+ } else if (callInfo != null) {
+ titleController.displayMeetingId(UIUtil.splitString(callInfo.getPeerNumber()));
+ } else if (!UIUtil.isService3() && EncryptedSPTool.getString(IS_VMR_2_ID).equals(vmrNumber)) {
+ titleController.displayMeetingId(UIUtil.splitString(vmrNumber));
+ }
+ titleController.setExitText(getString(R.string.cloudLink_meeting_hangUp));
+ tvSpeaker.setVisibility(View.VISIBLE);
+ tvShare.setVisibility(View.GONE);
+ String tv_name = getString(R.string.cloudLink_meeting_callings) + name;
+ tvTip.setText(tv_name);
+ if (!isMiniback()) {
+ frbg.setVisibility(View.VISIBLE);
+ }
+ setVoiceBackground(name);
+ } else {
+ //会议已连接
+ if (isConfConnect) {
+ if (null != data) {
+ if (data instanceof TsdkCall) {
+ TsdkCall call = (TsdkCall) data;
+ convertMeetingId(call.getCallInfo().getPeerNumber());
+ } else if (data instanceof TsdkConference) {
+ TsdkConference conference = (TsdkConference) data;
+ String peerNumber = conference.getCall().getCallInfo().getPeerNumber();
+ String accessNumber = EncryptedSPTool.getString(Constant.VMR_ACCESSNUMBER);
+ //不带*号,才赋值
+ LogUtil.zzz(TAG, "etData()");
+ if (!TextUtils.isEmpty(peerNumber) && !peerNumber.contains(accessNumber) && !isMiniback()) {
+ convertMeetingId(conference.getCall().getCallInfo().getPeerNumber());
+ }
+ }
+ }
+ tvTip.setVisibility(View.GONE);
+ }
+ }
+ if (isFirstStart && isConf) {
+ showLabel();
+ titleController.startTimer();
+ }
+ if (AuxStateChange) {
+ LogUtil.zzz(TAG, "initData: AuxStateChange");
+ initData();
+ }
+ }
+
+ /**
+ * AVC有辅流时 或者SVC选看
+ *
+ * @param member 选看的与会者
+ */
+ private void watchAttendee(Member member, boolean isScroll) {
+ if (isScroll) {
+ scrolltomain();
+ }
+ watch_member = member;
+ adapter.setWatchMember(member);
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_HWCLOUDLINK_WATCH_ATTENDEE, member);
+ }
+
+ /**
+ * 滑动到大画面
+ */
+ private void scrolltomain() {
+ if (isAuxData && tempRedusChecked != 0) { //1,有辅流
+ LogUtil.zzz("有辅流 scrolltomain = 1");
+ tempRedusChecked = 1;
+ vedioVp.setCurrentItem(1);//跳到大画面并选看
+ rb2.setChecked(true);
+ rb1.setChecked(false);
+ } else {
+ LogUtil.zzz("没辅流 scrolltomain = 0");
+ if (tempRedusChecked != 0) {
+ vedioVp.setAdapter(adapter);
+ adapter.notifyDataSetChanged();
+ }
+ vedioVp.setCurrentItem(0);
+ tempRedusChecked = 0;
+ rb2.setChecked(false);
+ rb1.setChecked(true);
+ }
+ rb3.setChecked(false);
+ }
+
+ /**
+ * 滑动到第一页
+ */
+ private void scrollToFirstPage() {
+ Log.d(TAG, "共享共享共享scrollToFirstPage: vedioVp = " + vedioVp);
+ vedioVp.setCurrentItem(0, false);
+ rb1.setChecked(true);
+ rb2.setChecked(false);
+ rb3.setChecked(false);
+ }
+
+ @Override
+ public void finish() {
+ super.finish();
+ MeetingController.getInstance().finishSp = false;
+ LogUtil.UIAction("SponsorMeeting:finish()");
+ }
+
+ /**
+ * 点对点:静音、扬声器、视频、与会者、更多
+ * 会议: 静音、视频、 共享、与会者、更多
+ * 切换到后台
+ *
+ * @param view 点击的空间
+ */
+ @SuppressLint("NewApi")
+ @OnClick({R2.id.img_voice_minimize, R2.id.tv_more, /*R.id.tv_participants,*/ R2.id.tv_video,
+ R2.id.tv_speaker, R2.id.tv_speaker_voice, R2.id.tv_mute, R2.id.iv_voice_setting, R2.id.vedio_vp, R2.id.tv_share,
+ R2.id.iv_keyboard, R2.id.btn_voiceToVideo, R2.id.iv_center_red_call, R2.id.tv_mute_voice, R2.id.tv_audit_dir_cancel_speak, R2.id.tv_speak_audit_dir,
+ R2.id.btn_voice_title_exit, R2.id.lin_voice_titleInfo})
+ public void viewClick(View view) {
+ if (tvTip.getVisibility() == View.VISIBLE) {
+ return;
+ }
+ int id = view.getId();//点击音频转视频
+ if (id == R.id.btn_voiceToVideo) {
+ checkPermission(() -> {
+ if (callInfo != null) {
+ LogUtil.zzz("点击音频转视频");
+ btn_voiceToVideo.setEnabled(false);
+ isVoice2Video = true;
+ VideoMgr.getInstance().initVideoWindow(callInfo.getCallID());
+ initData();
+ TsdkCall callByCallId = TsdkManager.getInstance().getCallManager().getCallByCallId(callInfo.getCallID());
+ if (callByCallId != null) {
+ callByCallId.addVideo();
+ ToastUtils.show(getString(R.string.cloudLink_meeting_waitingRemoteOpenCamera));
+ }
+ } else {
+ LogUtil.e(TAG, "viewClick callInfo is null");
+ }
+ }, this::permissionReconfirm, CameraFactory.class, PhoneStateFactory.class);
+ } else if (id == R.id.img_voice_minimize) {// 语音最小化
+ rootFloatView.setVisibility(View.GONE);
+ minimize(ConferenceConstant.VIDEO_VOICE_LAYOUT_CALL, true, false);
+ //静音
+ } else if (id == R.id.tv_mute_voice || id == R.id.tv_mute) {
+ if (isConf) {
+ mPresenter.muteSelf();
+ } else {
+ if (callInfo != null) {
+ mPresenter.muteCall(callInfo.getCallID());
+ } else {
+ LogUtil.e(TAG, "tv_mute callInfo is null");
+ }
+ }
+ //更多
+ } else if (id == R.id.tv_more) {
+ releasePopping();
+ LogUtil.zzz(TAG, "OnClick()", "isChairman = " + MeetingController.getInstance().isChairman());
+ popWindow = new SponsorMorePopWindow(this, isConf, mPresenter, tempRedusChecked, isAuxData,
+ isRemote);
+ popWindow.show();
+ popWindow.setSubtitleStatus(mIsConfSubtitleEnable, mIsSubtitleEnable);
+ popWindow.setPlaceSubtitleIsOpen(mSubtitleView.isEnable());
+ //摄像头开关
+ } else if (id == R.id.tv_video) {
+ LogUtil.zzz(TAG, "摄像头测试" + MeetingController.getInstance().isVideoOpen);
+ MeetingController.getInstance().isVideoOpen = !MeetingController.getInstance().isVideoOpen;
+ if (isConf) {
+ if (null == callInfo) {
+ mPresenter.closeOrOpenCamera(!MeetingController.getInstance().isVideoOpen, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ LogUtil.zzz("摄像头开关 isConf callInfo is null");
+ } else {
+ mPresenter.closeOrOpenCamera(!MeetingController.getInstance().isVideoOpen, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ if (!MeetingController.getInstance().isVideoOpen) {
+ setFloatBG();
+ }
+ } else {
+ if (callInfo != null) {
+ mPresenter.closeOrOpenCamera(!MeetingController.getInstance().isVideoOpen, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ if (!MeetingController.getInstance().isVideoOpen) {
+ setFloatBG();
+ }
+ }
+ //听筒扬声器切换
+ } else if (id == R.id.tv_speaker_voice || id == R.id.tv_speaker || id == R.id.iv_voice_setting) {
+ setAudioRoute(mPresenter.switchAudioRoute());
+ //共享屏幕
+ } else if (id == R.id.tv_share) {
+ if (isAuxData) {
+ replaceAuxData();
+ } else {
+ requestPermission();
+ }
+ } else if (id == R.id.iv_keyboard) {//二次拨号盘
+ mPresenter.showDialogKeyboard();
+ //挂断
+ } else if (id == R.id.btn_voice_title_exit || id == R.id.iv_center_red_call) {
+ if (callInfo != null) {
+ CallMgrV2.getInstance().endCall(callInfo.getCallID());
+ }
+ } else if (id == R.id.tv_speak_audit_dir) {
+ auditDirSpeak(true);
+ } else if (id == R.id.tv_audit_dir_cancel_speak) {
+ auditDirCancelSpeak(true);
+ //音频点击标题
+ } else if (id == R.id.lin_voice_titleInfo) {
+ showTitleInfoDialog(true);
+ }
+ }
+
+ private void releasePopping() {
+ if (popWindow != null) {
+ if (popWindow.isShowing()) {
+ popWindow.dismiss();
+ }
+ popWindow = null;
+ }
+ }
+
+ /**
+ * 单向直播申请发言
+ */
+ private void auditDirSpeak(boolean showToast) {
+ int i = MeetingMgrV2.getInstance().switchAuditSitesDir(1);
+ LogUtil.zzz("auditDirSpeak", "单向直播申请发言", i);
+ if (i == 0) {
+ tvMore.setVisibility(View.GONE);
+ UIUtil.setVisibility(this, R.id.tv_audit_dir_cancel_speak, true);
+ MeetingController.getInstance().isAuditDirSpeak = true;
+ rlFooter.setVisibility(View.VISIBLE);
+ MeetingController.getInstance().meetingBottomChanged(View.VISIBLE);
+ rl_footer_audit_dir.setVisibility(View.GONE);
+ if (MeetingController.getInstance().isVideoOpen) {
+ openCamera();
+ }
+ if (!isMiniback() && showToast) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_auditDirSpeakSuccess));
+ }
+ } else if (showToast) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_auditDirSpeakFail));
+ }
+ }
+
+ /**
+ * 单向直播取消发言
+ */
+ private void auditDirCancelSpeak(boolean showToast) {
+ int i = MeetingMgrV2.getInstance().switchAuditSitesDir(0);
+ LogUtil.zzz("auditDirSpeak", "单向直播取消发言", i);
+ if (i == 0) {
+ MeetingController.getInstance().isAuditDirSpeak = false;
+ rlFooter.setVisibility(View.GONE);
+ mPresenter.closeOrOpenCamera(true, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ MeetingController.getInstance().meetingBottomChanged(View.GONE);
+ rl_footer_audit_dir.setVisibility(View.VISIBLE);
+ closeFloat();
+ if (showToast) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_auditDirCancelSpeakSuccess));
+ }
+ } else if (showToast) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_auditDirCancelSpeakFail));
+ }
+ }
+
+ boolean floatIsReopen = false;
+
+ /**
+ * 打开或者关闭小窗口
+ *
+ * @param close true:隐藏 false:显示
+ */
+ private void closeOrOpenFloat(boolean close) {
+
+ if (!MeetingController.getInstance().isMinimize() ||
+ MeetingController.getInstance().isAux()) {
+ try {
+ if (close) {
+ LogUtil.zzz(TAG, "closeOrOpenFloat close");
+ rootFloatView.setVisibility(View.GONE);
+ if (VideoMgr.getInstance().getLocalVideoView() != null) {
+ VideoMgr.getInstance().getLocalVideoView().setZOrderMediaOverlay(false);
+ }
+ showLocalVideo = false;
+ MeetingController.getInstance().isFloatWindowOpen = false;
+ if (MeetingController.getInstance().conferenceRight && MeetingController.getInstance().isHasMember) {
+ if (VideoMgr.getInstance().getLocalVideoView() != null && isRemote) {
+ VideoMgr.getInstance().getLocalVideoView().setVisibility(View.GONE);
+ }
+ }
+ } else {
+ LogUtil.zzz(TAG, "closeOrOpenFloat open");
+ rootFloatView.setVisibility(View.VISIBLE);
+ if (VideoMgr.getInstance().getLocalVideoView() != null) {
+ VideoMgr.getInstance().getLocalVideoView().setZOrderMediaOverlay(true);
+ }
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_FLOAT_VISIBILITY, null);
+ showLocalVideo = true;
+ MeetingController.getInstance().isFloatWindowOpen = true;
+ if (MeetingController.getInstance().conferenceRight && MeetingController.getInstance().isHasMember) {
+ if (VideoMgr.getInstance().getLocalVideoView() != null) {
+ VideoMgr.getInstance().getLocalVideoView().setVisibility(View.VISIBLE);
+ }
+ }
+ if (MeetingController.getInstance().isVideoOpen && rootFloatView.getChildCount() == 0) {
+ if (isSVCMeeting) {
+ if (!floatIsReopen) {
+ reopenCamera();
+ floatIsReopen = true;
+ }
+ } else {
+ reopenCamera();
+ }
+
+ }
+ if (MeetingController.getInstance().isVideoOpen && isAuxBack()) {
+ getIntent().putExtra(Constant.AUX, false);
+ reopenCamera();
+ }
+ if ((mOrientation == Configuration.ORIENTATION_LANDSCAPE) && !isLocWindHorizontal()) {
+ setFloatWandH();
+ }
+ }
+ } catch (Exception e) {
+ LogUtil.zzz(TAG, "closeOrOpenFloat", "error = " + e.getMessage());
+ }
+ }
+ }
+
+ public boolean isLocWindHorizontal() {
+ if (rootFloatView != null) {
+ return rootFloatView.getWidth() > 0 && rootFloatView.getWidth() > rootFloatView.getHeight();
+ } else {
+ LogUtil.zzz(TAG, "isLocWindHorizontal", "rootFloatView is null");
+ return false;
+ }
+ }
+
+
+ private void replaceAuxData() {
+ if (shareCloudLinkDialog == null) {
+ shareCloudLinkDialog = new CloudLinkDialog(SponsorMeetingActivity.this);
+ }
+
+ shareCloudLinkDialog.setStr_message(getResources().getString(R.string.cloudLink_meeting_replaceAux), null);
+ shareCloudLinkDialog.setYes(getString(R.string.cloudLink_sure), null, () -> {
+ shareCloudLinkDialog.dismiss();
+ requestPermission();
+ });
+ shareCloudLinkDialog.setNo(getString(R.string.cloudLink_cancel), null, () -> shareCloudLinkDialog.dismiss());
+ if (!shareCloudLinkDialog.isShowing()) {
+ shareCloudLinkDialog.show();
+ }
+ }
+
+ /**
+ * 请求悬浮窗权限
+ */
+ private void requestPermission() {
+ if (Build.VERSION.SDK_INT >= 23) {
+ if (Settings.canDrawOverlays(this)) {
+ //有悬浮窗权限开启服务绑定 绑定权限
+ //请求截屏的权限
+ requestCapturePermission();
+// closeOrOpenFloat(true);
+ } else {
+ showOpenPermissionDialog();
+ }
+ }
+ }
+
+ /**
+ * 请求截屏
+ */
+ public void requestCapturePermission() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ //5.0 之后才允许使用屏幕截图
+ ToastUtils.show(getString(R.string.cloudLink_meeting_noShare));
+ } else {
+ MediaProjectionManager mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
+ Intent screenCaptureIntent = mediaProjectionManager.createScreenCaptureIntent();
+ screenCaptureIntent.setPackage(getPackageName());
+ startActivityForResult(screenCaptureIntent, CAPTURE_PERMISSION_REQ_CODE);
+ }
+ }
+
+ int mResultCode;
+ Intent mData;
+
+ @SuppressLint("NewApi")
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+ if (requestCode == CAPTURE_PERMISSION_REQ_CODE) {
+ if (resultCode != RESULT_OK) {
+ return;
+ }
+ if (null == data) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_1));
+ return;
+ }
+ LogUtil.zzz(TAG, "申请共享" + data);
+ this.mResultCode = resultCode;
+ this.mData = data;
+ HarmonyCaptureScreen.setProjectionResult(mResultCode, mData);
+ //调发送辅流的接口
+ mPresenter.sendAuxData();
+ }
+ }
+
+ @Override
+ public void showCustomToast(int resID) {
+ UiUtils.runUI(() -> {
+ ToastUtils.show(getString(resID));
+ });
+ }
+
+ @Override
+ public void showCustomToast(String str) {
+ UiUtils.runUI(() -> {
+ ToastUtils.show(str);
+ });
+ }
+
+ /**
+ * 离开会议
+ */
+ @Override
+ public void leaveConfSuccess() {
+ if (EncryptedSPTool.getBoolean(Constant.IS_NO_STREAM_DURATION)) {
+ return;
+ }
+ // 刷新会议列表
+ Intent it = new Intent(BroadcastConstant.ACTION_REFRESH_MEETINGS);
+ LocalBroadcastManager.getInstance(this).sendBroadcast(it);
+ Log.d(TAG, "leaveConfSuccess: ");
+ stopService(new Intent(this, AuxSendService.class));
+ LogUtil.d(TAG, "stopService leaveConfSuccess: ");
+ MeetingController.getInstance().setAux(false);
+ finish();
+ }
+
+ /**
+ * 抗啸叫自动关闭麦克风UI && 给用户提示
+ */
+ private void closeVoiceUI(boolean b) {
+ if (b) {
+ if (EncryptedSPTool.getBoolean(Constant.HOWL_AUTO_MUTE, true)) {
+ Drawable top;
+ top = ContextCompat.getDrawable(SponsorMeetingActivity.this, R.drawable.ic_mic_close);
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ if (rlVoice.getVisibility() == View.VISIBLE) {
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ } else {
+ tvMute.setCompoundDrawables(null, top, null, null);
+ }
+ MeetingController.getInstance().isVoiceOpen = true;
+ CallFunc.getInstance().setMuteStatus(true);
+ ToastUtils.show(getString(R.string.cloudLink_mine_howlautomutetoast));
+ }
+ } else {
+ ToastUtils.show(getString(R.string.cloudLink_mine_howlautomutetoastna));
+ }
+ }
+
+ private boolean isMule;
+ private boolean isFirstMic = false;
+ private int index = 0;
+
+ @Override
+ public void updateView(int what, boolean isShow, Object obj) {
+ runOnUiThread(() -> {
+ Drawable top;
+ LogUtil.zzz("updateView", "what=" + what + ",isShow=" + isShow);
+ switch (what) {
+ case CALL_UPDATE_VOICE:
+ //更新mic状态
+ case Constant.UPDATE_VOICE:
+ LogUtil.zzz("updateView", "what=" + what + ",isShow=" + isShow /*+ Log.getStackTraceString(new Throwable())*/);
+ if (PhoneUtil.isTelephonyCalling()) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic_close_gray);
+ isMule = isShow;
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ tvMute.setCompoundDrawables(null, top, null, null);
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ tvMute.setEnabled(false);
+ tv_mute_voice.setEnabled(false);
+ tvSpeaker.setEnabled(false);
+ ivVoiceSetting.setEnabled(false);
+ tv_speaker_voice.setEnabled(false);
+ } else {
+ if (!isShow) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic);
+ } else {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic_close);
+ }
+ tvMute.setEnabled(true);
+ tv_mute_voice.setEnabled(true);
+ tvSpeaker.setEnabled(true);
+ ivVoiceSetting.setEnabled(true);
+ tv_speaker_voice.setEnabled(true);
+ isMule = isShow;
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top, null, null);
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ }
+ // 执行本地设置的麦克风状态 规避方法 SDK第一次会默认设置麦克风
+ // 排除匿名链接入会场景,匿名链接入会时没有SDK给的初始麦克风状态
+ if (index == 0 && isConf && !EncryptedSPTool.getBoolean(Constant.IS_LOGOUT)) { // 非点呼界面
+ if (MeetingController.getInstance().conferenceRight && MeetingController.getInstance().isOnBaseInfoDo) {
+ isFirstMic = isShow;
+ index++;
+ }
+ }
+ break;
+ //更新摄像头状态
+ case Constant.UPDATE_VIDEO_STATUS:
+ LogUtil.zzz(TAG, "UPDATE_VIDEO_STATUS " + isShow);
+ if (!isShow) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_camera_24dp_seletor);
+ } else {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_camera_24dp_close_seletor);
+ }
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvVideo.setCompoundDrawables(null, top, null, null);
+ // 根据图标的显示标记摄像头的打开/关闭状态
+ boolean isOpenVideo = !isShow;
+ mPresenter.setOpenVideo(isOpenVideo);
+ break;
+ //会议链接成功
+ case Constant.UPDATE_CONF_CONNECT:
+ Log.d(TAG, "updateView: 会议连接成功");
+ if (!isConf) {
+ titleController.showMinBtn();
+ titleController.setEnabledMinBtn(true);
+ }
+ isConfConnect = true;
+ if (obj instanceof TsdkCall) {
+ TsdkCall call = (TsdkCall) obj;
+ this.confID = String.valueOf(call.getCallInfo().getConfId());
+ } else if (obj instanceof TsdkConference) {
+ TsdkConference conference = (TsdkConference) obj;
+ this.confID = String.valueOf(conference.getHandle());
+ tsdkConference = conference;
+ MeetingController.getInstance().setConference(tsdkConference);
+ }
+ setData(true, obj);
+ break;
+ //初始化callInfo值
+ case Constant.CALL_VOICE_GOING:
+ case Constant.CALL_VIDEO_GOING:
+ mini.setVisibility(View.GONE);
+ titleController.hideMinBtn();
+ closeOrOpenFloat(true);
+ this.callInfo = (CallInfo) obj;
+ if (UIUtil.isService3() && callInfo != null) {
+ Constant.TEMP_CONF_INFO = callInfo.getPeerNumber();
+ }
+ break;
+ //视频会话已建立
+ case Constant.UPDATE_CALL_CONNECT:
+ rlFooter.setVisibility(View.VISIBLE);
+ LogUtil.zzz(TAG, "视频通道建立 updateView: UPDATE_CALL_CONNECT");
+ mini.setVisibility(View.VISIBLE);
+ if (!isConf) {
+ titleController.showMinBtn();
+ titleController.setEnabledMinBtn(true);
+ }
+ top = ContextCompat.getDrawable(this, R.drawable.mic_selector);
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top, null, null);
+ isCallConnect = true;
+ this.callInfo = (CallInfo) obj;
+ if (!isMiniback()) {
+ MeetingController.getInstance().isVideo = this.callInfo.isVideoCall();
+ }
+ tvTip.setVisibility(View.GONE);
+ setData(false, callInfo);
+ frbg.setVisibility(View.GONE);
+ currentMillers = 0;
+ titleController.setTimer("00:00");
+ boolean isCallVideoToVoice = getIntent().getBooleanExtra(Constant.CALL_VIDEO_TO_VOICE, false);
+ if (MeetingController.getInstance().isVideo && !isCallVideoToVoice) { //视频
+ initSenor();//注册传感器,贴近耳朵就息屏
+ setLayoutToVideo();
+ if (!callInfo.isFocus()) {
+ initData();
+ }
+ } else { // 音频
+ tv_confId.setText("");
+ tv_method.setVisibility(View.VISIBLE);
+ linCallTool.setVisibility(View.VISIBLE);
+ rlVoiceTitle.setVisibility(View.VISIBLE);
+ linCallingTool.setVisibility(View.GONE);
+ btn_voiceToVideo.setVisibility(View.VISIBLE);
+ setLayoutToVoice();
+ }
+
+ break;
+ //视频转音频
+ case Constant.ACTION_VIDEO_TO_AUDIO:
+ DialogUtil.cancelRequestAddVideo();
+ btn_voiceToVideo.setVisibility(View.VISIBLE);
+ MeetingController.getInstance().isVideo = false;
+ setLayoutToVoice();
+ if (MeetingController.getInstance().isVoiceOpen) {
+ Drawable top2 = ContextCompat.getDrawable(this, R.drawable.ic_mic_close);
+ if (top2 != null) {
+ top2.setBounds(0, 0, top2.getMinimumWidth(), top2.getMinimumHeight());
+ }
+ tv_mute_voice.setCompoundDrawables(null, top2, null, null);
+ } else {
+ Drawable top2 = ContextCompat.getDrawable(this, R.drawable.ic_mic);
+ if (top2 != null) {
+ top2.setBounds(0, 0, top2.getMinimumWidth(), top2.getMinimumHeight());
+ }
+ tv_mute_voice.setCompoundDrawables(null, top2, null, null);
+ }
+ break;
+ //对方请求音频转视频
+ case Constant.CALL_UPGRADE_ACTION:
+ CallInfo callInfo = (CallInfo) obj;
+ if (callInfo != null) {
+ closeOrOpenFloat(true);
+ requestAddVideo(callInfo);
+ }
+ break;
+ //音频转视频,对方同意打开视频
+ case Constant.OPEN_VIDEO:
+ LogUtil.zzz("音频转视频,对方同意打开视频,会话界面打开摄像头");
+ btn_voiceToVideo.setEnabled(true);
+ initData();
+ setLayoutToVideo();
+ MeetingController.getInstance().isVideo = true;
+ MeetingController.getInstance().isVideoOpen = true;
+ LogUtil.zzz("!MeetingController.getInstance().isVoiceOpen = " + !MeetingController.getInstance().isVoiceOpen);
+ if (MeetingController.getInstance().isVoiceOpen) {
+ Drawable top2 = ContextCompat.getDrawable(this, R.drawable.ic_mic_close);
+ if (top2 != null) {
+ top2.setBounds(0, 0, top2.getMinimumWidth(), top2.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top2, null, null);
+ } else {
+ Drawable top2 = ContextCompat.getDrawable(this, R.drawable.ic_mic);
+ if (top2 != null) {
+ top2.setBounds(0, 0, top2.getMinimumWidth(), top2.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top2, null, null);
+ }
+ //30006554 打开摄像头
+ CallInfo mcallInfo = (CallInfo) obj;
+ if (mcallInfo != null) {
+ mPresenter.closeOrOpenCamera(!MeetingController.getInstance().isVideoOpen, mcallInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ frbg.setVisibility(View.GONE);
+ break;
+ //音频转视频,对方不同意打开视频
+ case Constant.REFUSE_OPEN_VIDEO:
+ ToastUtils.show(UIUtil.getContext().getString(R.string.cloudLink_meeting_noAddView));
+ btn_voiceToVideo.setEnabled(true);
+ isVoice2Video = false;
+ break;
+ case Constant.AUDIT_DIR:
+ TsdkonEvtAuditDir dir = (TsdkonEvtAuditDir) obj;
+ if (dir != null && dir.param != null) {
+ if (dir.param.directionType == 0) {
+ auditDirCancelSpeak(false);
+ } else {
+ auditDirSpeak(false);
+ }
+ }
+ break;
+ case Constant.RECEIVED_MOBILE_CALL:
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic_close_gray);
+ isMule = isShow;
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top, null, null);
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ tvMute.setEnabled(false);
+ tv_mute_voice.setEnabled(false);
+ tvSpeaker.setEnabled(false);
+ ivVoiceSetting.setEnabled(false);
+ tv_speaker_voice.setEnabled(false);
+ break;
+ case Constant.END_MOBILE_CALL:
+ if (!isShow) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic);
+ } else {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic_close);
+ }
+ isMule = isShow;
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvMute.setCompoundDrawables(null, top, null, null);
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ tvMute.setEnabled(true);
+ tv_mute_voice.setEnabled(true);
+ tvSpeaker.setEnabled(true);
+ ivVoiceSetting.setEnabled(true);
+ tv_speaker_voice.setEnabled(true);
+ break;
+ default:
+ break;
+ }
+ });
+ }
+
+ /**
+ * 设置音频路由
+ *
+ * @param audioRoute 0 默认
+ * 1 扬声器
+ */
+ private void setAudioRoute(TsdkMobileAuidoRoute audioRoute) {
+ if (SwitchAudioRouteManager.Companion.getInstance().setAudioRoute(audioRoute)) {
+// updateTsDkMobileAudioRoute(audioRoute);
+ LogUtil.d("setUserSettingAudioRoute", "用户设置声音路由为 = " + audioRoute);
+ SwitchAudioRouteManager.Companion.getInstance().setUserSettingAudioRoute(audioRoute);
+ SwitchAudioRouteManager.Companion.getInstance().setSettingAudioRoute(true);
+ }
+ }
+
+ private void initMiniData() {
+ this.isConf = getIntent().getBooleanExtra("isConf", false);
+ this.isSVCMeeting = getIntent().getBooleanExtra("isSVC", false);
+ isAuxData = MeetingMgrV2.getInstance().isAuxData();
+ mAuxIntent = MeetingController.getInstance().getAuxServiceIntent();
+ isFirstStart = false;
+ MeetingController.getInstance().setAuxServiceIntent(null);
+ EncryptedSPTool.putBoolean(Constant.IS_MINIMIZE_CLICK, false);
+ if (MeetingMgrV2.getInstance().isFlag()) {
+ rlFooter.setVisibility(View.GONE);
+ MeetingController.getInstance().meetingBottomChanged(View.GONE);
+ }
+ if (isConf) {
+ CallInfo callInfo = (CallInfo) getIntent().getSerializableExtra("callInfo");
+ if (callInfo != null) {
+ this.callInfo = callInfo;
+ }
+ this.isConfConnect = getIntent().getBooleanExtra("isConfConnect", false);
+ TsdkConferenceLiveData.getInstance().setValue(MeetingMgrV2.getInstance().getCurrentConference());
+ TimerUtil.delay(200, () -> {
+ rlFooter.setVisibility(View.VISIBLE);
+ if (MeetingMgrV2.getInstance().getCurrentConference() == null) {
+ LogUtil.zzz("MeetingMgr.getInstance().getCurrentConference() = null");
+ return;
+ }
+ updateView(Constant.UPDATE_CONF_CONNECT, false, MeetingMgrV2.getInstance().getCurrentConference());
+ updateSystemCallStatus();
+ if (MeetingMgrV2.getInstance().getCurrentConference() != null) {
+ String handle = MeetingMgrV2.getInstance().getCurrentConference().getHandle() + "";
+ MeetingMgrV2.getInstance().getConfNotification().onConfEventNotify(ConfConstant.CONF_EVENT.STATE_UPDATE, handle);
+ }
+ if (!isSVCMeeting) {
+ VideoMgr.getInstance().initVideoWindow(MeetingMgrV2.getInstance().getCurrentConference().getCall().getCallInfo().getCallId());
+ } else {
+ if (MeetingController.getInstance().getWatchingMember() != null) {
+ watchAttendee(MeetingController.getInstance().getWatchingMember(), true);
+ }
+ }
+ if (MeetingController.getInstance().meetingStartTime != 0) {
+ titleController.setOldTime((System.currentTimeMillis() - MeetingController.getInstance().meetingStartTime) / 1000);
+ }
+ titleController.updateTime();
+ titleController.startTimer();
+ if (MeetingMgrV2.getInstance().isFlag() && MeetingController.getInstance().isAuditDirSpeak) {
+ auditDirSpeak(true);
+ }
+ });
+ } else {
+ this.callInfo = (CallInfo) getIntent().getSerializableExtra("callInfo");
+ closeOrOpenFloat(MeetingController.getInstance().getTempFloat() == 2);
+ if (callInfo == null) {
+ LogUtil.e(TAG, "initMiniData callInfo is null");
+ return;
+ }
+ mPresenter.closeOrOpenCamera(!MeetingController.getInstance().isVideoOpen, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ TsdkConferenceLiveData.getInstance().setValue(MeetingMgrV2.getInstance().getCurrentConference());
+ TimerUtil.delay(200, () -> {
+ if (MeetingController.getInstance().isShowRequestAddVideo()) {
+ LogUtil.zzz("isShowRequestAddVideo=" + MeetingController.getInstance().isShowRequestAddVideo());
+ requestAddVideo(callInfo);
+ }
+ rlFooter.setVisibility(View.VISIBLE);
+ updateView(Constant.UPDATE_CALL_CONNECT, false, callInfo);
+ isMule = getIntent().getBooleanExtra("isMute", false);
+ updateView(Constant.UPDATE_VOICE, isMule, null);
+ updateSystemCallStatus();
+ if (ConferenceConstant.VIDEO_VOICE_LAYOUT_CALL.equals(getIntent().getStringExtra("type"))) {
+ setLayoutToVoice();
+ } else {
+ setLayoutToVideo();
+ }
+ String name = getIntent().getStringExtra(Constant.CALLED_NAME);
+ if (callInfo == null) {
+ LogUtil.e(TAG, "runOnUiThread callInfo is null");
+ return;
+ }
+ if (TextUtils.isEmpty(name)) {
+ name = TextUtils.isEmpty(callInfo.getPeerDisplayName()) ?
+ callInfo.getPeerNumber() : callInfo.getPeerDisplayName();
+ }
+ tv_name.setText(UIUtil.formName(name));
+ tvVoiceDisplayName.setText(getIntent().getStringExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME));
+ tvVoiceUserNumber.setText(getIntent().getStringExtra(SponsorMeetingConstant.VOICE_NUMBER));
+ setVoiceBackground(name);
+ VideoMgr.getInstance().initVideoWindow(callInfo.getCallID());
+ titleController.setOldTime(getIntent().getLongExtra("time", 0));
+ titleController.updateTime();
+ titleController.startTimer();
+ if (getIntent().getBooleanExtra(SponsorMeetingConstant.IS_VOICE_TO_VIDEO_FORM_MINIMIZE, false)) {
+ int callId = CallMgrV2.getInstance().getCallId();
+ TsdkCall tsdkCall = TsdkManager.getInstance().getCallManager().getCallByCallId(callId);
+ CallMgrV2.getInstance().handleOpenVideoReq(tsdkCall, null);
+ }
+ if (MeetingController.getInstance().isVoiceOpen) {
+ Drawable top;
+ if (!PhoneUtil.isTelephonyCalling()) {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic_close);
+ } else {
+ top = ContextCompat.getDrawable(this, R.drawable.ic_mic_close_gray);
+ }
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ if (rlVoice.getVisibility() == View.VISIBLE) {
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ } else {
+ tvMute.setCompoundDrawables(null, top, null, null);
+ }
+ } else {
+ Drawable top = ContextCompat.getDrawable(this, R.drawable.ic_mic);
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ if (rlVoice.getVisibility() == View.VISIBLE) {
+ tv_mute_voice.setCompoundDrawables(null, top, null, null);
+ } else {
+ tvMute.setCompoundDrawables(null, top, null, null);
+ }
+ }
+ });
+ }
+ mIsSubtitleEnable = getIntent().getBooleanExtra(Constant.MEETING_CAPTION_ENABLE, false);
+ mIsConfSubtitleEnable = getIntent().getBooleanExtra(Constant.MEETING_CONF_CAPTION_ENABLE, false);
+ boolean isSubtitleShowing = getIntent().getBooleanExtra(Constant.MEETING_CONF_CAPTION_SHOWING, false);
+ if (isSubtitleShowing) {
+ mSubtitleView.show();
+ }
+ }
+
+ private void updateSystemCallStatus() {
+ if (PhoneUtil.isTelephonyCalling()) {
+ if (isConf) {
+ if (!isSilentState) {
+ updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ } else {
+ updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ } else {
+ if (callInfo != null) {
+ if (!isSilentState) {
+ updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ } else {
+ updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ } else {
+ LogUtil.e(TAG, "tv_mute callInfo is null");
+ }
+ }
+ }
+ }
+
+ /**
+ * 小窗口播放
+ */
+ Intent MinimizeService;
+ boolean isClickMini = false;
+
+ /**
+ * 会议类型
+ *
+ * @param type 视频 语音
+ */
+ @SuppressLint("NewApi")
+ private void minimize(String type, boolean isAnim, boolean backToDesk) {
+ if (Settings.canDrawOverlays(this)) {
+ if (isClickMini) {
+ return;
+ }
+ /* if (isConf && !MeetingController.getInstance().isJoinConfSuccess()) {
+ return;
+ }*/
+ LogUtil.userAction("点击最小化");
+ EncryptedSPTool.putBoolean(Constant.IS_MINIMIZE_CLICK, true);
+ //防止多次点击
+ mini.setEnabled(false);
+ EncryptedSPTool.putBoolean(Constant.IS_CLOSE_VIDEO, false);
+ MeetingController.getInstance().setMinimize(true);
+ MinimizeService = new Intent(SponsorMeetingActivity.this, com.tengshisoft.chatmodule.hwclud.serivce.MinimizeService.class);
+ MinimizeService.putExtra("type", type);
+ MinimizeService.putExtra("isConf", isConf);
+ MinimizeService.putExtra("isSVC", isSVCMeeting);
+ MinimizeService.putExtra("callInfo", callInfo);
+ MinimizeService.putExtra("isHasAux", isAuxData);
+ MinimizeService.putExtra("isConfConnect", isConfConnect);
+ MinimizeService.putExtra(Constant.CALLED_NAME, getIntent().getStringExtra(Constant.CALLED_NAME));
+ MinimizeService.putExtra("isMute", isMule);
+ MinimizeService.putExtra(SponsorMeetingConstant.CONF_ID, titleController.getMeetingId());
+ MinimizeService.putExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME, tvVoiceDisplayName.getText().toString());
+ MinimizeService.putExtra(SponsorMeetingConstant.VOICE_NUMBER, tvVoiceUserNumber.getText().toString());
+
+ if (isSVCMeeting) {
+ MinimizeService.putExtra("ssrcTableStart", String.valueOf(MeetingController.getInstance().getMiniSSrc(myTsdkCallInfo)));
+ if (watch_member != null) {
+ MinimizeService.putExtra("watchMember", watch_member);
+ }
+ }
+ long time = 0;
+ time += titleController.getSeconds() == null ? 0 : Long.parseLong(titleController.getSeconds());
+ time += titleController.getMinutes() == null ? 0 : Long.parseLong(titleController.getMinutes()) * 60;
+ time += titleController.getHours() == null ? 0 : Long.parseLong(titleController.getHours()) * 3600;
+ if (time == 0) {
+ MinimizeService.putExtra("time", getIntent().getLongExtra("time", 0));
+ } else {
+ MinimizeService.putExtra("time", time);
+ }
+ MinimizeService.putExtra(Constant.MY_CONF_INFO, myTsdkCallInfo);
+ MinimizeService.putExtra(Constant.MEETING_CAPTION_ENABLE, mIsSubtitleEnable);
+ MinimizeService.putExtra(Constant.MEETING_CONF_CAPTION_ENABLE, mIsConfSubtitleEnable);
+ MinimizeService.putExtra(Constant.MEETING_CONF_CAPTION_SHOWING, mSubtitleView.isEnable());
+ MinimizeService.putExtra(Constant.MEETING_CONF_CONTROL_ENABLE, mConfControl.getVisibility() == VISIBLE);
+ startForegroundService(MinimizeService);
+ MeetingController.getInstance().setAuxServiceIntent(mAuxIntent);
+ if (isAnim) {
+ finish();
+ } else {
+ finish();
+ overridePendingTransition(0, 0);
+ }
+ } else {
+ if (!backToDesk) {
+ showOpenPermissionDialog();
+ }
+ }
+ }
+
+ /***
+ * 点对点,对方请求音频转视频
+ */
+ private void requestAddVideo(CallInfo callInfo) {
+ LogUtil.zzz("isShowRequestAddVideo", LogUtil.getInstance().getSimpleExtraInfo());
+ MeetingController.getInstance().setShowRequestAddVideo(true);
+ DialogUtil.requestAddVideo(this, getString(R.string.cloudLink_meeting_request_open_camera), v -> {
+ checkPermission(() -> {
+ DialogUtil.cancelRequestAddVideo();
+ MeetingController.getInstance().setShowRequestAddVideo(false);
+ LogUtil.zzz("同意");
+ frbg.setVisibility(View.GONE);
+ if (callInfo == null) {
+ LogUtil.e(TAG, "requestAddVideo callInfo is null");
+ return;
+ }
+ VideoMgr.getInstance().initVideoWindow(callInfo.getCallID());
+ TsdkCall callByCallId = TsdkManager.getInstance().getCallManager().getCallByCallId(callInfo.getCallID());
+ if (callByCallId != null) {
+ callByCallId.replyAddVideo(true);
+ isVoice2Video = true;
+ initData();
+ setLayoutToVideo();
+ MeetingController.getInstance().isVideo = true;
+ MeetingController.getInstance().isVideoOpen = true;
+ setLayoutToVideo();
+ SwitchAudioRouteManager.Companion.getInstance().setIsvideo(true);
+ new AudioRouteManager.Builder()
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .setIsVideo(true)
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .build();
+ //30006554 打开摄像头
+
+ mPresenter.closeOrOpenCamera(false, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+// if (!MeetingController.getInstance().isVoiceOpen) {
+// Drawable top2 = ContextCompat.getDrawable(SponsorMeetingActivity.this, R.drawable.ic_mic_close);
+// if (top2 != null) {
+// top2.setBounds(0, 0, top2.getMinimumWidth(), top2.getMinimumHeight());
+// }
+// tvMute.setCompoundDrawables(null, top2, null, null);
+// }
+ }, this::permissionReconfirm, CameraFactory.class, PhoneStateFactory.class);
+ }, v -> {
+ MeetingController.getInstance().setShowRequestAddVideo(false);
+ LogUtil.zzz("拒绝");
+ if (callInfo != null) {
+ TsdkCall callByCallId = TsdkManager.getInstance().getCallManager().getCallByCallId(callInfo.getCallID());
+ if (callByCallId != null) {
+ callByCallId.replyAddVideo(false);
+ isVoice2Video = false;
+ }
+ }
+ });
+ setHorizontal(false);
+ }
+
+ private List tempList = new ArrayList<>();//保存上一次与会者
+ private Member temp_broad_member;//上一次广播与会者
+ private boolean is_cancle_broading = false;//是否取消广播
+ private List svcVideoMemberList = new ArrayList<>();
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public void refreshMemberList(List memberList) {
+ if (memberList != null) {
+ LogUtil.zzz(TAG + "refreshMemberList", "刷新与会者列表" + memberList.size());
+ if (memberList.size() == 1) {
+ updateView(CALL_UPDATE_VOICE, memberList.get(0).isMute(), "");
+ }
+ } else {
+ LogUtil.zzz("refreshMemberList", "与会者列表为空");
+ }
+ runOnUiThread(() -> {
+ boolean isWatchMemberInconf = false;
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
+ isWatchMemberInconf = refreshSvcMemberList(memberList);
+ }
+ if (!isWatchMemberInconf && MeetingController.getInstance().isWatchMember()) {
+ // 选看的人不在会议中 且 有选看状态 取消选看状态
+ MeetingController.getInstance().setWatchMember(false);
+ MeetingController.getInstance().setWatchingMember(null);
+ BaseAppContext.getInstance().setMember(null); }
+ try {
+ titleController.displayMeetingName(meetingTitle);
+ titleController.displayMeetingCount(String.valueOf(svcMemberList.size()));
+ if (UIUtil.isService3()) {
+ if (TextUtils.isEmpty(MeetingMgrV2.getInstance().getPeerNum())) {
+ convertMeetingId(meetingId);
+ } else {
+ convertMeetingId(MeetingMgrV2.getInstance().getPeerNum());
+ }
+ }
+ } catch (NullPointerException np) {
+ Log.e(TAG, "refreshMemberList: " + np.getMessage());
+ }
+ if (cancle_broading && !is_cancle_broading) { //被动取消广播
+ LogUtil.zzz("被动取消广播");
+ cancle_broading = false;
+ temp_broad_member = null;
+ if (MeetingController.getInstance().isWatchMember()) {
+ watchAttendee(MeetingController.getInstance().getWatchingMember(), false);
+ }
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SPONSOR_MEETING_BROAD_MEMBER_CANCEL, null);
+ }
+ if (!UIUtil.compareList(svcVideoMemberList, tempList)) { //取消 && 开始广播 或者与会者数量变化
+ LogUtil.zzz("refreshMemberList", "与会者列表发生改变");
+ tempList.clear();
+ tempList.addAll(svcVideoMemberList);
+ if (isSVCMeeting && svcMemberList != null) { //svc
+ LogUtil.zzz(TAG, "refreshMemberList: initData ---------");
+ initData();
+ }
+ }
+ if (SystemUtil.isRunningForeground(this) && MeetingController.getInstance().isInMeetingPage && !MeetingMgrV2.getInstance().isFlag()) { //我们app运行在前台&&非单项直播
+ if (svcMemberList.size() > 1) {
+ //关闭打开小窗口
+ if (isAuxData) { //
+ if (tempRedusChecked == 1 && MeetingController.getInstance().getTempFloat() != 2) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ } else {
+ if (tempRedusChecked == 0 && MeetingController.getInstance().getTempFloat() != 2) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ }
+ } else {
+ closeOrOpenFloat(MeetingController.getInstance().getTempFloat() != 1);
+ }
+ } else {
+ if (MeetingMgrV2.getInstance().isFlag() && MeetingController.getInstance().isInMeetingPage) {
+ if (isAuxData && tempRedusChecked == 1 && MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else if (!isAuxData && tempRedusChecked == 0 && MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ } else {
+ closeOrOpenFloat(true);
+ }
+ }
+ if (MeetingController.getInstance().isVideoOpen) {
+ closeCamera();
+ } else {
+ openCamera();
+ }
+ if (!UIUtil.isEmpty(MeetingMgrV2.getInstance().getSubject())) {
+ meetingTitle = MeetingMgrV2.getInstance().getSubject();
+ titleController.displayMeetingName(meetingTitle);
+ if (UIUtil.isService3() && TextUtils.isEmpty(meetingId)) {
+ meetingId = Constant.TEMP_CONF_INFO;
+ }
+ }
+ });
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private synchronized boolean refreshSvcMemberList(List memberList) {
+ is_cancle_broading = false;
+ svcMemberList.clear();
+ svcVideoMemberList.clear();
+ boolean isWatchMemberInconf = false; // 选看的人是否在会议中K
+ if (memberList != null) {
+ int tempVideoMember = 0; // 视频入会成员数量
+ for (Member member : memberList) {
+ if (member != null) {
+ if (member.getStatus() == IN_CONF) {
+ svcMemberList.add(member);
+ if (!member.getIsAudio()) {
+ svcVideoMemberList.add(member);
+ tempVideoMember++;
+ }
+ if (!isWatchMemberInconf && MeetingController.getInstance().isWatchMember()) {
+ if (member.getNumber() != null && MeetingController.getInstance().getWatchingMember() != null
+ && MeetingController.getInstance().getWatchingMember().getNumber() != null) {
+ if (member.getNumber().equals(MeetingController.getInstance().getWatchingMember().getNumber())) {
+ isWatchMemberInconf = true; // 选看的人在会议中
+ }
+ }
+ }
+ if (member.isBroadcastSelf()) {
+ if (temp_broad_member == null || !temp_broad_member.getNumber().equals(member.getNumber())) {
+ temp_broad_member = member;
+ LogUtil.zzz(TAG, "scrolltomain");
+ scrolltomain();
+ }
+ watch_member = member;
+ is_cancle_broading = true;
+ cancle_broading = true;
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SPONSOR_MEETING_REFLUSHED, watch_member);
+ }
+ }
+ }
+ //匿名入会Terminal为空
+ //帐号长度小于4位 sdk给返回的数据判断不出来自己是自己,需要适配
+ if (member != null && member.isSelf()) {
+ refreshDataForSelf(member);
+ refreshMorePopView();
+ }
+ }
+ MeetingController.getInstance().videoJoinMeeting = tempVideoMember;
+ }
+ return isWatchMemberInconf;
+ }
+
+ /**
+ * 与会者状态更新后 自己的变化
+ */
+ private void refreshDataForSelf(Member member) {
+ // 关闭加入会议密码键盘
+ if (mPresenter != null) {
+ mPresenter.dismissDialogKeyboard();
+ }
+
+ if (member.getRole() == TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN) {
+ MeetingController.getInstance().setChairman(true);
+ } else {
+ MeetingController.getInstance().setChairman(false);
+ }
+ if (member.getRole() != TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN) {
+ if (MeetingController.getInstance().isSvcBigConf() && MeetingController.getInstance().isWatchMember()) {
+ LocalBroadcast.getInstance().sendBroadcast(Constant.SPONSOR_MEETING_WATCH_MEMBER, null);
+ MeetingController.getInstance().setWatchMember(false);
+ MeetingController.getInstance().setWatchingMember(null);
+ }
+ }
+ updateView(Constant.UPDATE_VOICE, member.isMute(), confID);
+ if (!MeetingController.getInstance().hasSyncVoiceState && MeetingController.getInstance().isOnBaseInfoDo) {
+ boolean isAudio = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ boolean is_vmr = EncryptedSPTool.getBoolean(Constant.INITIATE_VMR, false);
+ if (!MeetingController.getInstance().hasReqChairMan && isAudio == member.isMute() && !is_vmr) {
+ handler.post(runnable);
+ } else if (PhoneUtil.isTelephonyCalling() &&
+ MeetingMgrV2.getInstance().getCurrentConference() != null) {
+ mPresenter.receivedMobileCall(true, true);
+ } else {
+ MeetingController.getInstance().setSponsorConf(false);
+ }
+ MeetingController.getInstance().hasSyncVoiceState = true;
+ }
+
+ if (MeetingMgrV2.getInstance().isFlag() && !MeetingController.getInstance().isAuditDirSpeak) {
+ rl_footer_audit_dir.setVisibility(View.VISIBLE);
+ rlFooter.setVisibility(View.GONE);
+ MeetingController.getInstance().meetingBottomChanged(View.GONE);
+ }
+ }
+
+ @Override
+ public void confEnd() {
+ handler.postDelayed(() -> {
+ hideLoading();
+ if (MinimizeService != null) {
+ stopService(MinimizeService);
+ }
+ finishActivity(CAPTURE_PERMISSION_REQ_CODE);
+ finish();
+ overridePendingTransition(0, R.anim.dialog_out_anim);
+ if (MeetingController.getInstance().isNotHaveNet()) {
+ if (isConf) {
+ showNoNetDialog(getString(R.string.cloudLink_meeting_tls_error));
+ }
+ }
+ }, 300);
+ }
+
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ @Override
+ public void toBackStartService() {
+ goBackgroundAndStartService();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private void goBackgroundAndStartService() {
+ LogUtil.d("enter goBackgroundAndStartService()");
+
+ MeetingController.getInstance().setAux(true);
+ moveTaskToBack(true);
+ mPresenter.closeOrOpenCamera(true, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ mAuxIntent = new Intent(SponsorMeetingActivity.this, AuxSendService.class);
+ mAuxIntent.putExtra("type", ConferenceConstant.VIDEO_CALL);
+ mAuxIntent.putExtra("isConf", isConf);
+ mAuxIntent.putExtra("isSVC", isSVCMeeting);
+ mAuxIntent.putExtra("callInfo", callInfo);
+ mAuxIntent.putExtra("isHasAux", isAuxData);
+ mAuxIntent.putExtra("isConfConnect", isConfConnect);
+ mAuxIntent.putExtra(Constant.CALLED_NAME, getIntent().getStringExtra(Constant.CALLED_NAME));
+ mAuxIntent.putExtra("isMute", isMule);
+ mAuxIntent.putExtra(SponsorMeetingConstant.CONF_ID, titleController.getMeetingId());
+ mAuxIntent.putExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME, tvVoiceDisplayName.getText().toString());
+ mAuxIntent.putExtra(SponsorMeetingConstant.VOICE_NUMBER, tvVoiceUserNumber.getText().toString());
+
+ if (isSVCMeeting) {
+ mAuxIntent.putExtra("ssrcTableStart", String.valueOf(MeetingController.getInstance().getMiniSSrc(myTsdkCallInfo)));
+ if (watch_member != null) {
+ mAuxIntent.putExtra("watchMember", watch_member);
+ }
+ }
+ long time = 0;
+ time += titleController.getSeconds() == null ? 0 : Long.parseLong(titleController.getSeconds());
+ time += titleController.getMinutes() == null ? 0 : Long.parseLong(titleController.getMinutes()) * 60;
+ time += titleController.getHours() == null ? 0 : Long.parseLong(titleController.getHours()) * 3600;
+ if (time == 0) {
+ mAuxIntent.putExtra("time", getIntent().getLongExtra("time", 0));
+ } else {
+ mAuxIntent.putExtra("time", time);
+ }
+ mAuxIntent.putExtra(Constant.MY_CONF_INFO, myTsdkCallInfo);
+ mAuxIntent.putExtra(Constant.MEETING_CAPTION_ENABLE, mIsSubtitleEnable);
+ mAuxIntent.putExtra(Constant.MEETING_CONF_CAPTION_ENABLE, mIsConfSubtitleEnable);
+ mAuxIntent.putExtra(Constant.MEETING_CONF_CAPTION_SHOWING, mSubtitleView.isEnable());
+ mAuxIntent.putExtra(Constant.MEETING_CONF_CONTROL_ENABLE, mConfControl.getVisibility() == VISIBLE);
+ startForegroundService(mAuxIntent);
+ finish();
+ LogUtil.d("leave goBackgroundAndStartService()");
+ }
+
+ @Override
+ public void showStreamDialog(boolean isAudio) {
+ if (signalController == null) {
+ return;
+ }
+ signalController.showDialog(isAudio, isAuxData || MeetingController.getInstance().isAux());
+ }
+
+ @Override
+ public void watchText(String allText) {
+ Intent intent = new Intent(this, WatchAllTextActivityV2.class);
+ intent.putExtra(Constant.ALL_TEXT, allText);
+ startActivityForResult(intent, 121);
+ }
+
+ @Override
+ public void updateTsDkMobileAudioRoute(TsdkMobileAuidoRoute audioRoute) {
+ Drawable top;
+ LogUtil.zzz("updateTsDkMobileAudioRoute audioRoute = " + audioRoute);
+ if (audioRoute == TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER) {
+ ivVoiceSetting.setBackgroundResource(R.drawable.ic_voice);
+ top = ContextCompat.getDrawable(this, R.drawable.ic_speaker);
+ tvSpeaker.setText(getResources().getString(R.string.cloudLink_meeting_speaker));
+ tv_speaker_voice.setText(getResources().getString(R.string.cloudLink_meeting_speaker));
+ } else if (audioRoute == TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_BLUETOOTH) {
+ ivVoiceSetting.setBackgroundResource(R.drawable.ic_bluetooth_default);
+ top = ContextCompat.getDrawable(this, R.drawable.ic_baseline_bluetooth_connected_24);
+ tvSpeaker.setText(getResources().getString(R.string.cloudLink_bluetooth));
+ tv_speaker_voice.setText(getResources().getString(R.string.cloudLink_bluetooth));
+ } else {
+ ivVoiceSetting.setBackgroundResource(R.drawable.mic);
+ top = ContextCompat.getDrawable(this, R.drawable.ic_hearing_24);
+ tvSpeaker.setText(getResources().getString(R.string.cloudLink_listen));
+ tv_speaker_voice.setText(getResources().getString(R.string.cloudLink_listen));
+ }
+ if (top != null) {
+ top.setBounds(0, 0, top.getMinimumWidth(), top.getMinimumHeight());
+ }
+ tvSpeaker.setCompoundDrawables(null, top, null, null);
+ tv_speaker_voice.setCompoundDrawables(null, top, null, null);
+ }
+
+ private int duration;
+ OptionsPickerView pvTime;
+
+ @Override
+ public void postponeConf() {
+ TimeSelectUtils utils = new TimeSelectUtils();
+ List minute1 = utils.getMinute2();
+ pvTime = new OptionsPickerBuilder(this, (options1, options2, options3, v) -> {
+ if (TimeSelectUtils.getHour().get(options1) == 0 && minute1.get(options2) == 0) {
+ showCustomToast(R.string.cloudLink_meeting_extendConfTimeErr);
+ return;
+ }
+ duration = minute1.get(options1);
+ MeetingController.getInstance().duration = duration;
+ MeetingMgrV2.getInstance().postpone(duration);
+
+ })
+ .setLabels(" " + getString(R.string.cloudLink_min), "", "")
+ .isCenterLabel(false)
+ .setCancelText(this.getResources().getString(R.string.cloudLink_meeting_selectConfAddTime))
+ .setSubmitText(getString(R.string.cloudLink_sure))
+ .setTextColorCenter(this.getResources().getColor(R.color.saveButtonBackground))
+ .setCancelColor(this.getResources().getColor(R.color.contact_text))
+ .isDialog(true)
+ .build();
+ pvTime.setPicker(minute1);
+ pvTime.show();
+// if (pvTime.getDialogContainerLayout() != null) {
+// if (pvTime.getDialogContainerLayout().getHandler() != null) {
+// pvTime.getDialogContainerLayout().getHandler().postDelayed(() -> {
+// int height = pvTime.getDialogContainerLayout().getHeight();
+// System.out.println("height=====" + height);
+// myWindowManager.postPoneRefreshFloatWindow(mOrientation != Configuration.ORIENTATION_LANDSCAPE, true, height);
+// }, 100);
+// }
+// }
+// pvTime.setOnDismissListener(o -> {
+// Log.d(TAG, "postponeConf: setOnDismissListener");
+// myWindowManager.postPoneRefreshFloatWindow(mOrientation != Configuration.ORIENTATION_LANDSCAPE, false, 0);
+// });
+
+ }
+
+ @Override
+ public void openFloat() {
+ rootFloatView.setVisibility(View.VISIBLE);
+ if (MeetingController.getInstance().conferenceRight && MeetingController.getInstance().isHasMember) {
+ if (VideoMgr.getInstance().getLocalVideoView() != null) {
+ VideoMgr.getInstance().getLocalVideoView().setVisibility(View.VISIBLE);
+ }
+ }
+ showLocalVideo = !showLocalVideo;
+ LogUtil.zzz("openFloat", " rootFloatView.getChildCount()=" + rootFloatView.getChildCount());
+ if (MeetingController.getInstance().isVideoOpen && rootFloatView.getChildCount() == 0) {
+ reopenCamera();
+ }
+ MeetingController.getInstance().setTempFloat(1);
+ MeetingController.getInstance().isFloatWindowOpen = true;
+ if ((mOrientation == Configuration.ORIENTATION_LANDSCAPE) != isLocWindHorizontal()) {
+ setFloatWandH();
+ }
+ }
+
+ void reopenCamera() {
+ if (isConf) {
+ mPresenter.closeOrOpenCamera(true, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ mPresenter.closeOrOpenCamera(false, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ } else {
+ mPresenter.closeOrOpenCamera(true, callInfo == null ? 0 : callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ mPresenter.closeOrOpenCamera(false, callInfo == null ? 0 : callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ }
+
+ @Override
+ public void closeFloat() {
+ rootFloatView.setVisibility(View.GONE);
+ if (MeetingController.getInstance().conferenceRight && MeetingController.getInstance().isHasMember) {
+ if (VideoMgr.getInstance().getLocalVideoView() != null && isRemote) {
+ VideoMgr.getInstance().getLocalVideoView().setVisibility(View.GONE);
+ }
+ }
+ showLocalVideo = !showLocalVideo;
+ MeetingController.getInstance().setTempFloat(2);
+ MeetingController.getInstance().isFloatWindowOpen = false;
+ }
+
+ @Override
+ public boolean floatIsClose() {
+ if (rootFloatView != null) {
+ return rootFloatView.getVisibility() == View.VISIBLE;
+ }
+ return false;
+ }
+
+ @Override
+ public FrameLayout getHideContainer() {
+ return hideVideoView;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public void handleChairmanResult(Constant.ParticipantEvent type, String name, int result) {
+ runOnUiThread(() -> {
+ switch (type) {
+ case RELEASE_CHAIRMAN_FAILED:
+ if (MeetingController.getInstance().isInMeetingPage) {
+ showCustomToast(R.string.cloudLink_meeting_releaseChairmanFailed);
+ MeetingController.getInstance().setChairman(true);
+ }
+ break;
+ case RELEASE_CHAIRMAN_SUCCESS:
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, "");
+ break;
+ case REQUEST_CHAIRMAN_FAILED:
+ MeetingController.getInstance().setChairman(false);
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, "");
+ if (result == 67109017 && MeetingController.getInstance().isShowDialog) {
+ if (MeetingController.getInstance().isInMeetingPage) {
+ showRequestChairmanDialog();
+ }
+ } else if (result == 67109022) {
+ showCustomToast(R.string.cloudLink_meeting_requestChairmanFailed);
+ } else if (result == 67109023) {
+ showCustomToast(R.string.cloudLink_meeting_requestChairmanPasswordFailed);
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+ } else {
+ showCustomToast(R.string.cloudLink_meeting_releaseChairmanFailedPsw);
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+ }
+
+ break;
+ case REQUEST_CHAIRMAN_SUCCESS:
+ MeetingController.getInstance().setChairman(true);
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+ showCustomToast(getString(R.string.cloudLink_meeting_requestChairmanSuccess));
+ break;
+ default:
+ break;
+ }
+ refreshMorePopView();
+ });
+ }
+
+ @Override
+ public void auxFailed(int code) {
+ switch (code) {
+ case 1:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_1)));
+ break;
+ case 2:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_2)));
+ break;
+ case 3:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_3)));
+ break;
+ case 4:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_4)));
+ break;
+ case 5:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_5)));
+ break;
+ case 6:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_6)));
+ break;
+ case 7:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_7)));
+ break;
+ case 8:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_AUX_ERROR_CODE_8)));
+ break;
+ default:
+ runOnUiThread(() -> ToastUtils.show(getString(R.string.cloudLink_meeting_shareFailed)));
+ break;
+ }
+ }
+
+ @Override
+ public void updateViewEnable() {
+ runOnUiThread(() -> {
+ if (titleController != null) {
+ titleController.showMinBtn();
+ titleController.setEnabledMinBtn(true);
+ }
+ if (mConfControl != null) {
+ mConfControl.setVisibility(View.GONE);
+ }
+ });
+ }
+
+ //申请主席弹出框
+ private void showRequestChairmanDialog() {
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ return;
+ }
+ LinkDialog = new CloudLinkDialog(SponsorMeetingActivity.this);
+ LinkDialog.setStr_message(getString(R.string.cloudLink_meeting_chairmanpwd), Gravity.START);
+ LinkDialog.editText_message(true, getString(R.string.cloudLink_login_inputPwd));
+ LinkDialog.setYes(getString(R.string.cloudLink_sure), null, () -> {
+ mPresenter.requestChairman(TextUtils.isEmpty(LinkDialog.getEditTextMessage()) ? MathUtil.getRandom6Int() : LinkDialog.getEditTextMessage());
+ LinkDialog.dismiss();
+ });
+ LinkDialog.setNo(getString(R.string.cloudLink_cancel), null, () -> LinkDialog.dismiss());
+ LinkDialog.show();
+ }
+
+ @Override
+ public void showNetworkError() {
+
+ }
+
+ @Override
+ public void showLoading() {
+
+ }
+
+
+ @Override
+ public void hideLoading() {
+
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.clear();
+ }
+
+ //投机取巧解决了 Android正在进行即时视频会议,切换至后台,打开照相机随后关闭,再打开Kit,本地视频画面凝固(或者黑屏),无法恢复 问题 start ---->
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ protected void onResume() {
+ super.onResume();
+ LogUtil.zzz(TAG, "onResume()");
+ if (MeetingController.getInstance().finishSp) {
+ LogUtil.zzz(TAG, "onResume: finish");
+ finish();
+ }
+ updateSystemCallStatus();
+ onResumeCloseOrOpenFloat();
+ MeetingController.getInstance().isInMeetingPage = true;
+ if (MeetingController.getInstance().isVideoOpen) {
+ if (isConf) {
+ if (isMiniback()) {
+ mPresenter.closeOrOpenCamera(false, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ getIntent().putExtra("isMiniback", false);
+ } else {
+ mPresenter.closeOrOpenCamera(false, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ } else {
+ if (isMiniback()) {
+ mPresenter.closeOrOpenCamera(true, callInfo == null ? 0 : callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ getIntent().putExtra("isMiniback", false);
+ }
+ if (callInfo != null) {
+ mPresenter.closeOrOpenCamera(false, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ }
+ }
+
+ if (LinkDialog != null && LinkDialog.isShowing()) {
+ LinkDialog.dismiss();
+ }
+ mPresenter.reflushSensor();//刷新注册传感器
+ if (MeetingController.getInstance().isAux() && (boolean) MYSPUtils.get(Constant.IS_CLOSE_AUX, false)) { //开关控制共享的时候进自己应用关不关辅流
+ stopService(mAuxIntent);
+ LogUtil.d(TAG, "onResume: stopService");
+ }
+ if (!isSVCMeeting && isConf) {
+ VideoMgr.getInstance().changeRotation(0);
+ }
+ }
+
+ /**
+ * onResume 时小窗口是否显示
+ */
+ private void onResumeCloseOrOpenFloat() {
+ if (MeetingMgrV2.getInstance().isFlag()) { // 单项直播 小窗口永远关闭
+ if (!MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else if (isAuxData && tempRedusChecked == 1 && MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else if (!isAuxData && tempRedusChecked == 0 && MeetingController.getInstance().isAuditDirSpeak) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ } else {
+ if (isAuxData) {
+ if (MeetingController.getInstance().getTempFloat() != 2 && tempRedusChecked == 1) {
+ closeOrOpenFloat(false);
+ } else {
+ /*if (MeetingController.getInstance().isFloatWindowOpen && tempRedusChecked == 0 && svcMemberList != null && svcMemberList.size() > 1 && !isF) {
+ closeOrOpenFloat(false);
+ } else {*/
+ closeOrOpenFloat(true);
+ }
+ } else {
+ if (isConf) {
+ if (MeetingController.getInstance().isFloatWindowOpen && tempRedusChecked == 0) {
+ closeOrOpenFloat(false);
+ } else if (MeetingController.getInstance().getTempFloat() != 2 && tempRedusChecked == 0 && svcMemberList != null && svcMemberList.size() > 1) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ } else {
+ if (MeetingController.getInstance().getTempFloat() != 2 && rlVoice.getVisibility() != View.VISIBLE) {
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (isConf) {
+ if (MeetingController.getInstance().isAux()) {
+ ToastUtils.show(getResources().getString(R.string.cloudLink_meeting_auxingError));
+ } else {
+ mPresenter.showLandLeaveConfBottomSheetDialog();
+ }
+ } else {
+ if (callInfo != null) {
+ if (!MeetingController.getInstance().isMinimize() ||
+ MeetingController.getInstance().isAux()) {
+ CallMgrV2.getInstance().endCall(callInfo.getCallID());
+ Constant.TEMP_CONF_INFO = "";
+ }
+ }
+ finish();
+ }
+ }
+
+ //<------ end
+ @Override
+ protected void onStop() {
+ super.onStop();
+ stopTimer();
+ isFirstStart = false;
+ isPressTouch = false;
+ MeetingController.getInstance().isInMeetingPage = false;
+ // 是否点击小画面 && 是否在与会者界面
+ if (EncryptedSPTool.getBoolean(Constant.IS_CLOSE_VIDEO) && !IS_SHOW_PARTICIPANTS) {
+ closeOrOpenFloat(true);
+ if ((isConf && MeetingController.getInstance().isAux())) {
+ mPresenter.closeOrOpenCamera(true,
+ MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ }
+ }
+
+ /**
+ * 强制关闭摄像头
+ */
+ private void mandatoryCloseCamera() {
+ if (isConf) {
+ mPresenter.closeOrOpenCamera(true, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ } else {
+ if (callInfo == null) {
+ LogUtil.e(TAG, "closeCamera is callInfo null");
+ return;
+ }
+ mPresenter.closeOrOpenCamera(true, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ }
+
+ /**
+ * 关闭摄像头
+ */
+ private void closeCamera() {
+ if (!MeetingController.getInstance().isVideoOpen) {
+ if (isConf) {
+ mPresenter.closeOrOpenCamera(true, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ } else {
+ if (callInfo == null) {
+ LogUtil.e(TAG, "closeCamera is callInfo null");
+ return;
+ }
+ mPresenter.closeOrOpenCamera(true, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ }
+ }
+
+ /**
+ * 打开摄像头
+ */
+ private void openCamera() {
+ if (MeetingController.getInstance().isVideoOpen) {
+ if (isConf) {
+ mPresenter.closeOrOpenCamera(false, MeetingController.getInstance().cameraIndex ? 0 : 1);
+ } else {
+ if (callInfo != null) {
+ mPresenter.closeOrOpenCamera(false, callInfo.getCallID(), MeetingController.getInstance().cameraIndex ? 0 : 1);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy: SponsorMeetingActivity");
+ releasePopping();
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ localBroadcastManager.unregisterReceiver(localReceiver);
+ isSVCMeeting = false;
+ MeetingController.getInstance().isVoicePage = false;
+ MeetingController.getInstance().isVMR = false;
+ mPresenter.detachView();
+ EncryptedSPTool.remove(Constant.INITIATE_VMR);
+ if (myWindowManager != null) {
+ myWindowManager.removeNormalView(this);
+ myWindowManager = null;
+ }
+ if (titleController != null) {
+ titleController.onDestroy();
+ }
+ super.onDestroy();
+ }
+
+ private void setFloatBG() {
+ if (rootFloatView != null) {
+ if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ //横屏
+ rootFloatView.setBackgroundResource(R.drawable.camerablack2);
+ } else {
+ //竖屏
+ rootFloatView.setBackgroundResource(R.drawable.camera_default);
+ }
+ }
+ }
+
+ /**
+ * 横竖屏切换时小窗口尺寸变化
+ */
+ private void setFloatWandH() {
+ if (tvMore != null) {
+ LogUtil.zzz(TAG, "setFloatWandH", "VideoMgr", "setFloatWandH: " + mOrientation);
+ if (rootFloatView == null || rootFloatView.getVisibility() != View.VISIBLE) {
+ return;
+ }
+ LogUtil.zzz(TAG, "setFloatWandH", "restLocWindow");
+ UIUtil.runUI(() -> {
+ if (mOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+ //横屏
+ restLocWindow(true);
+ setFloatBG();
+ } else {
+ //竖屏
+ restLocWindow(false);
+ setFloatBG();
+ }
+ });
+
+ }
+
+ TimerUtil.delay(500, () -> {
+ if (rootFloatView != null) {
+ Point size = new Point();
+ getWindow().getWindowManager().getDefaultDisplay().getSize(size);
+ float x = size.x - rootFloatView.getWidth() - ScreenUtil.dp2ps(this, 10);
+ float y = size.y - rootFloatView.getHeight() - ScreenUtil.dp2ps(this, 90);
+ LogUtil.zzz(TAG, "rootFloatView", "" + mOrientation, "x=" + x + ",y=" + y);
+ rootFloatView.animate().x(x).y(y).setDuration(0).start();
+ }
+ });
+ }
+
+ /**
+ * 重置本地小窗口位置
+ *
+ * @param isHorizontal 是否是横屏
+ */
+ void restLocWindow(boolean isHorizontal) {
+ if (rootFloatView != null) {
+ LogUtil.zzz(TAG, "restLocWindow()", "isHorizontal=" + isHorizontal);
+ ConstraintLayout.LayoutParams layoutParams;
+ if (isHorizontal) {
+ layoutParams = new ConstraintLayout.LayoutParams(ScreenUtil.dp2ps(this, 160), ScreenUtil.dp2ps(this, 90));
+ } else {
+ layoutParams = new ConstraintLayout.LayoutParams(ScreenUtil.dp2ps(this, 90), ScreenUtil.dp2ps(this, 160));
+ }
+ layoutParams.bottomMargin = ScreenUtil.dp2ps(this, 90);
+ layoutParams.rightMargin = ScreenUtil.dp2ps(this, 10);
+ layoutParams.endToEnd = ConstraintLayout.LayoutParams.PARENT_ID;
+ layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.PARENT_ID;
+ rootFloatView.setLayoutParams(layoutParams);
+ }
+ }
+
+ /**
+ * 控制ViewPager的点
+ */
+ private void rbGoneOrVisible() {
+
+ int temp;
+ if (isAuxData) {
+ temp = 2;
+ } else {
+ temp = 1;
+ }
+ if (temp + svcPages < 2) {
+ rb1.setVisibility(View.GONE);
+ rb2.setVisibility(View.GONE);
+ rb3.setVisibility(View.GONE);
+ } else if (temp + svcPages == 2) {
+ rb1.setVisibility(View.VISIBLE);
+ rb2.setVisibility(View.VISIBLE);
+ rb3.setVisibility(View.GONE);
+ } else {
+ rb1.setVisibility(View.VISIBLE);
+ rb2.setVisibility(View.VISIBLE);
+ rb3.setVisibility(View.VISIBLE);
+ }
+ }
+
+ //------------ViewPager 拖动监听开始---------------------
+ @Override
+ public void onPageScrolled(int i, float v, int i1) {
+ if (i1 == 0) {
+ MeetingController.getInstance().isCanScroll = true;
+ } else {
+ MeetingController.getInstance().isCanScroll = false;
+ }
+ }
+
+ @Override
+ public void onPageSelected(int i) {
+ if (manualDrag) {
+ LogUtil.userAction("滑动翻页,page=" + i);
+ } else {
+ LogUtil.userAction("非用户滑动翻页,page=" + i);
+ }
+ // 向左滑动
+ if (tempRedusChecked > i) {
+ // 大会模式下,左滑出现的页面下标大于画中画下标,就自动跳转到画中画
+ int pipIndex = isAuxData ? 1 : 0;
+ if (MeetingController.getInstance().isSvcBigConf() && !MeetingController.getInstance().isChairman() && i > pipIndex) {
+ LogUtil.zzz("onPageSelected", "大会模式禁止右划");
+ if (isAuxData && tempRedusChecked != 0) {
+ tempRedusChecked = 1;
+ vedioVp.setCurrentItem(1, false);
+ rb2.setChecked(true);
+ rb1.setChecked(false);
+ } else {
+ tempRedusChecked = 0;
+ vedioVp.setCurrentItem(0, false);
+ rb2.setChecked(false);
+ rb1.setChecked(true);
+ }
+ rb3.setChecked(false);
+ return;
+ }
+ }
+ tempRedusChecked = i;
+ refreshIndicator(i);
+
+ if (isAuxData) { //有副流
+ if (i == 1) {
+ if (MeetingMgrV2.getInstance().isFlag()) {
+ if (MeetingController.getInstance().isAuditDirSpeak) {
+ VideoMgr.getInstance().clearLocGroup();
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ } else if (MeetingController.getInstance().getTempFloat() != 2) {
+ VideoMgr.getInstance().clearLocGroup();
+ closeOrOpenFloat(false); //大画面 且未手动关闭过 小画面打开
+ }
+ } else {
+ closeOrOpenFloat(true);//副流 和 其他 页面隐藏小画面
+ }
+ } else {
+ if (i == 0 && MeetingController.getInstance().getTempFloat() != 2) {
+ VideoMgr.getInstance().clearLocGroup();
+ closeOrOpenFloat(false);
+ } else {
+ closeOrOpenFloat(true);
+ }
+ }
+// vedioVp.setNoRightScroll(MeetingController.getInstance().pageNoRightScrollWithBigConf(i));
+ }
+
+ private void refreshIndicator(int i) {
+ int temp = isAuxData ? 2 : 1;
+ if (temp + svcPages > 2) {//判断小圆点
+ rg.setVisibility(View.VISIBLE);
+ if (i == 0) {
+ rb1.setChecked(true);
+ rb2.setChecked(false);
+ } else if (i == temp + svcPages - 1) {
+ rb3.setChecked(true);
+ } else {
+ rb2.setChecked(true);
+ }
+ } else if (temp + svcPages == 2) {
+ rg.setVisibility(View.VISIBLE);
+ if (i == 0) {
+ rb1.setChecked(true);
+ rb2.setChecked(false);
+ } else {
+ rb1.setChecked(false);
+ rb2.setChecked(true);
+ }
+ } else {
+ rg.setVisibility(View.GONE);
+ }
+ }
+
+ int tempSelect = -1;
+
+ @Override
+ public void onPageScrollStateChanged(int i) {
+ switch (i) {
+ case ViewPager.SCROLL_STATE_IDLE:
+ manualDrag = false;
+ Log.d(TAG, "onPageScrollStateChanged: manualDrag = false");
+ if (adapter != null && vedioVp.getCurrentItem() != tempSelect) {
+ adapter.notifyDataSetChanged();
+ }
+ tempSelect = -1;
+ break;
+ case ViewPager.SCROLL_STATE_DRAGGING:
+ Log.d(TAG, "onPageScrollStateChanged: manualDrag=true;");
+ manualDrag = true;
+ tempSelect = vedioVp.getCurrentItem();
+ break;
+ case ViewPager.SCROLL_STATE_SETTLING:
+ break;
+ }
+ }
+
+ //------------ViewPager 拖动监听结束---------------------
+
+ /**
+ * 悬浮窗点击事件
+ */
+ @Override
+ public void onClick() {
+ }
+
+ /**
+ * 显示会控
+ */
+ public void showLabel() {
+ if (isConf) {
+ startTimer();
+ } else {
+ if (callConnection) {
+ startTimer();
+ }
+ }
+ }
+
+ private void startTimer() {
+ stopTimer();
+ if (scheduledExecutorService == null) {
+ scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+ }
+ if (myScheduleThread == null) {
+ myScheduleThread = new MyScheduleThread("BottomLabel");
+ }
+// if (isFirstStart) {
+// scheduledExecutorService.schedule(myScheduleThread, 5, TimeUnit.SECONDS);
+// } else {
+ scheduledExecutorService.scheduleWithFixedDelay(myScheduleThread, 200, 5000, TimeUnit.MILLISECONDS);
+// }
+ }
+
+ private void stopTimer() {
+ if (null != myScheduleThread) {
+ myScheduleThread.interrupt();
+ myScheduleThread = null;
+ }
+ if (null != scheduledExecutorService) {
+ scheduledExecutorService.shutdownNow();
+ scheduledExecutorService = null;
+ }
+ }
+
+ public FrameLayout getFloatWindow() {
+ return localVideo;
+ }
+
+ public Member getWatchMember() {
+ return watch_member;
+ }
+
+ class MyScheduleThread extends Thread {
+ private String threadName;
+
+ public MyScheduleThread(String threadName) {
+ this.threadName = threadName;
+ }
+
+ @Override
+ public void run() {
+ runOnUiThread(() -> {
+ if (isFirstStart) {
+ hideButton();
+ isFirstStart = false;
+ stopTimer();
+ } else {
+ if (isShowBar) {
+ hideButton();
+ isPressTouch = false;
+ stopTimer();
+ } else {
+ showButton();
+ }
+ }
+ });
+ }
+ }
+
+ private void showButton() {
+ if (titleController.getTitleVisibility() == View.GONE || rlFooter.getVisibility() == View.GONE
+ || (isConf && ivVoiceSetting.getVisibility() == View.GONE)) {
+ titleController.setTitleVisibility(View.VISIBLE);
+ if (!MeetingMgrV2.getInstance().isFlag()) {
+ rlFooter.setVisibility(View.VISIBLE);
+ MeetingController.getInstance().meetingBottomChanged(View.VISIBLE);
+ } else {
+ if (MeetingController.getInstance().isAuditDirSpeak) {
+ rlFooter.setVisibility(View.VISIBLE);
+ MeetingController.getInstance().meetingBottomChanged(View.VISIBLE);
+ } else {
+ rl_footer_audit_dir.setVisibility(View.VISIBLE);
+ }
+ }
+ if (isConf) {
+ ivVoiceSetting.setVisibility(View.VISIBLE);
+ iv_keyboard.setVisibility(View.VISIBLE);
+ }
+ isShowBar = true;
+ }
+ }
+
+ private void hideButton() {
+// LogUtil.zzz("hideButton", "隐藏会控");
+ if (titleController.getTitleVisibility() == View.VISIBLE || rlFooter.getVisibility() == View.VISIBLE || rl_footer_audit_dir.getVisibility() == View.VISIBLE) {
+ titleController.setTitleVisibility(View.GONE);
+ rlFooter.setVisibility(View.GONE);
+ MeetingController.getInstance().meetingBottomChanged(View.GONE);
+ rl_footer_audit_dir.setVisibility(View.GONE);
+ ivVoiceSetting.setVisibility(View.GONE);
+ iv_keyboard.setVisibility(View.GONE);
+ isShowBar = false;
+ }
+ }
+
+ public void setRemote(boolean remote) {
+ isRemote = remote;
+ }
+
+ private void convertMeetingId(String peerNumber) {
+ if (!TextUtils.isEmpty(peerNumber)) {
+ if (isMiniback() || isAuxBack()) {
+ titleController.displayMeetingId(getIntent().getStringExtra(SponsorMeetingConstant.CONF_ID));
+ } else {
+ String[] peerArray = peerNumber.split("\\*");
+ //只有主席才显示会议密码
+ if (peerArray.length > 1 && MeetingController.getInstance().isChairman()) {
+ if (isConf) {
+ if (!UIUtil.isService3() && EncryptedSPTool.getString(IS_VMR_2_ID).equals(vmrNumber)) {
+ if (TextUtils.isEmpty(vmrNumber)) {
+ String convertId = "ID: " + UIUtil.splitString(peerArray[0]);
+ titleController.displayMeetingId(convertId);
+ } else {
+ String id = "ID: " + UIUtil.splitString(vmrNumber);
+ titleController.displayMeetingId(id);
+ MeetingController.getInstance().setChairmanPwd(peerArray[1]);
+ }
+ } else {
+ String convertId = "ID: " + UIUtil.splitString(peerArray[0]);
+ titleController.displayMeetingId(convertId);
+ }
+ } else {
+ titleController.displayMeetingId(UIUtil.splitString(peerArray[0]));
+ }
+
+ } else {
+ if (isConf) {
+ if (!UIUtil.isService3() && EncryptedSPTool.getString(IS_VMR_2_ID).equals(vmrNumber)) {
+ if (TextUtils.isEmpty(vmrNumber)) {
+ String convertId = "ID: " + UIUtil.splitString(peerArray[0]);
+ titleController.displayMeetingId(convertId);
+ } else {
+ String id = "ID: " + UIUtil.splitString(vmrNumber);
+ titleController.displayMeetingId(id);
+ if (peerArray.length > 1) {
+ MeetingController.getInstance().setChairmanPwd(peerArray[1]);
+ }
+ }
+ } else {
+ String convertId = "ID: " + UIUtil.splitString(peerArray[0]);
+ titleController.displayMeetingId(convertId);
+ }
+ } else {
+ titleController.displayMeetingId(UIUtil.splitString(peerArray[0]));
+ }
+ }
+ }
+ }
+ }
+
+ class LocalReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent != null) {
+ if (Objects.equals(intent.getAction(), WATCH_MYSELF_CAMERA_STATE)) {
+ updateView(Constant.UPDATE_VIDEO_STATUS, !MeetingController.getInstance().isVideoOpen, "");
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+
+ switch (keyCode) {
+ // 音量减小
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ if (voiceNum >= 10) {
+ voiceNum = voiceNum - 10;
+ TsdkManager.getInstance().getCallManager().setSpeakVolume(voiceNum);
+ } else {
+ TsdkManager.getInstance().getCallManager().setSpeakVolume(0);
+ }
+ break;
+ // 音量增大
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ if (voiceNum <= 90) {
+ voiceNum = voiceNum + 10;
+ } else {
+ voiceNum = 100;
+ }
+ TsdkManager.getInstance().getCallManager().setSpeakVolume(voiceNum);
+ break;
+ default:
+ break;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ private void updateNetworkQuality(int netLevel, boolean isInit) {
+ LogUtil.zzz("update Network Quality sp",
+ "current NetLevel: " + MeetingTitleBarController.mCurrentNetLevel + " net Level: " + netLevel);
+ if (ivMeetingSignal == null) {
+ return;
+ }
+ if (isInit) {
+ if (netLevel > 3 && netLevel <= 5) {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_excellent));
+ } else if (netLevel <= 3 && netLevel > 1) {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_good));
+ } else if (netLevel <= 1 && netLevel > 0) {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_medium));
+ } else {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_excellent));
+ }
+ } else {
+ if (MeetingTitleBarController.mCurrentNetLevel != netLevel) {
+ if (netLevel > 3 && netLevel <= 5) {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_excellent));
+ } else if (netLevel <= 3 && netLevel > 1) {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_good));
+ } else if (netLevel == 1) {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_medium));
+ } else {
+ runOnUiThread(() -> ivMeetingSignal.setImageResource(
+ R.drawable.network_signal_quality_excellent));
+ }
+ }
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void refreshMorePopView() {
+ if (popWindow != null && popWindow.isShowing()) {
+ popWindow.refreshView();
+ popWindow.setSubtitleStatus(mIsConfSubtitleEnable, mIsSubtitleEnable);
+ }
+ if (mSubtitleView != null) {
+ mSubtitleView.setSubtitleEnable(mIsSubtitleEnable);
+ }
+ }
+
+ public void setSubtitleViewVisibility(boolean isShow) {
+ if (isShow) {
+ mSubtitleView.show();
+ } else {
+ mSubtitleView.hide();
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/WatchAllTextActivityV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/WatchAllTextActivityV2.java
new file mode 100644
index 0000000..de04532
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/activity/WatchAllTextActivityV2.java
@@ -0,0 +1,78 @@
+package com.tengshisoft.chatmodule.activity;
+
+import android.graphics.Color;
+import android.os.Build;
+import android.view.View;
+import android.widget.TextView;
+
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.R2;
+import com.tengshisoft.chatmodule.hwclud.ui.BaseMvpActivityV2;
+import com.tengshisoft.chatmodule.hwclud.ui.BasePresenterV2;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+
+import butterknife.BindView;
+import butterknife.OnClick;
+
+/**
+ * @Time: 2021/8/11
+ * @Author: isoftstone
+ * @Description:查看文本页面
+ */
+public class WatchAllTextActivityV2 extends BaseMvpActivityV2 {
+ @BindView(R2.id.stv)
+ TextView tvContent;
+
+ @Override
+ protected int getLayoutId() {
+ return R.layout.activity_watch_all_text;
+ }
+
+ @Override
+ protected void initIntent() {
+
+ }
+
+ @Override
+ protected void initView() {
+ if (Build.VERSION.SDK_INT >= ConstantsV2.NUMBER_21) {
+ View decorView = getWindow().getDecorView();
+ int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
+ decorView.setSystemUiVisibility(option);
+ getWindow().setNavigationBarColor(Color.TRANSPARENT);
+ getWindow().setStatusBarColor(Color.TRANSPARENT);
+ }
+ String allText = getIntent().getStringExtra(Constant.ALL_TEXT);
+ tvContent.setText(allText);
+ }
+
+ @Override
+ protected void initData() {
+
+ }
+
+ /**
+ * 点击屏幕退出事件
+ */
+ @OnClick(R2.id.rl)
+ public void onViewClicked() {
+ finish();
+ overridePendingTransition(android.R.anim.fade_out, ConstantsV2.DELAY_MILLIS_100);
+ }
+
+
+ @Override
+ protected BasePresenterV2 createPresenter() {
+ return null;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ onViewClicked();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/BookConferenceInfo.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/BookConferenceInfo.java
new file mode 100644
index 0000000..df35337
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/BookConferenceInfo.java
@@ -0,0 +1,250 @@
+package com.tengshisoft.chatmodule.beans;
+
+import com.huawei.ecterminalsdk.base.TsdkConfMediaType;
+import com.huawei.ecterminalsdk.base.TsdkConfRecordMode;
+import com.tenlionsoft.baselib.core.beans.Member;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * This class is about book conference information
+ * 预约会议信息类
+ */
+public class BookConferenceInfo implements Serializable {
+
+ /**
+ * subject
+ * 会议主题
+ */
+ private String subject;
+
+ /**
+ * media type
+ * 媒体类型
+ */
+ TsdkConfMediaType mediaType;
+
+ /**
+ * timeZoneId
+ * 时区Id
+ * 默认57 北京时间
+ */
+ private int timeZoneId = 0;
+
+ /**
+ * timeOffset
+ * 时区偏移
+ */
+ private int timeOffset = 28800000;
+
+ /**
+ * media type
+ * 媒体类型 SMC 3.0
+ */
+ private int mediaTypeV3;
+
+ /**
+ * record type
+ * 录制类型
+ */
+ TsdkConfRecordMode recordType;
+
+ /**
+ * is auto
+ * 是否自动录制
+ */
+ private Boolean is_auto;
+
+ /**
+ * Conference start time
+ * 会议开始时间
+ */
+ private String startTime;
+
+ /**
+ * duration
+ * 会议时长
+ */
+ private int duration;
+
+ /**
+ * size
+ * 会议人数
+ */
+ private int size;
+
+ /**
+ * Member list
+ * 与会者列表
+ */
+ private List memberList;
+ /**
+ * 会议密码
+ */
+ private String confPassword;
+
+ /**
+ * 主席密码,最大长度为6,数字。
+ */
+ private String chairmanPwd;
+
+ /**
+ * VMR会议接入号
+ */
+ private String vmrNumber;
+
+ /**
+ * Start now
+ * 是否是即时会议
+ */
+ private boolean isInstantConference = true; //默认为立即会议
+
+ /**
+ * Ai字幕开关
+ */
+ private int supportSubtitle;
+
+ /**
+ * Ai字幕源语言
+ */
+ private int supportSrcLang;
+
+ public int getTimeOffset() {
+ return timeOffset;
+ }
+
+ public void setTimeOffset(int timeOffset) {
+ this.timeOffset = timeOffset;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public TsdkConfMediaType getMediaType() {
+ return mediaType;
+ }
+
+ public void setMediaType(TsdkConfMediaType mediaType) {
+ this.mediaType = mediaType;
+ }
+
+ public TsdkConfRecordMode getRecordType() {
+ return recordType;
+ }
+
+ public void setRecordType(TsdkConfRecordMode recordType) {
+ this.recordType = recordType;
+ }
+
+ public Boolean getIs_auto() {
+ return is_auto;
+ }
+
+ public void setIs_auto(Boolean is_auto) {
+ this.is_auto = is_auto;
+ }
+
+ public String getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(String startTime) {
+ this.startTime = startTime;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public void setDuration(int duration) {
+ this.duration = duration;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public List getMemberList() {
+ return memberList;
+ }
+
+ public void setMemberList(List memberList) {
+ this.memberList = memberList;
+ }
+
+ public boolean isInstantConference() {
+ return isInstantConference;
+ }
+
+ public void setInstantConference(boolean instantConference) {
+ isInstantConference = instantConference;
+ }
+
+ public void setConfPassword(String confPassword) {
+ this.confPassword = confPassword;
+ }
+
+ public String getConfPassword() {
+ return this.confPassword;
+ }
+
+
+ public int getMediaTypeV3() {
+ return mediaTypeV3;
+ }
+
+ public void setMediaTypeV3(int mediaTypeV3) {
+ this.mediaTypeV3 = mediaTypeV3;
+ }
+
+ public int getTimeZoneId() {
+ return timeZoneId;
+ }
+
+ public void setTimeZoneId(int timeZoneId) {
+ this.timeZoneId = timeZoneId;
+ }
+
+ public String getChairmanPwd() {
+ return chairmanPwd;
+ }
+
+ public void setChairmanPwd(String chairmanPwd) {
+ this.chairmanPwd = chairmanPwd;
+ }
+
+ public String getVmrNumber() {
+ return vmrNumber;
+ }
+
+ public void setVmrNumber(String vmrNumber) {
+ this.vmrNumber = vmrNumber;
+ }
+
+ public int getSupportSubtitle() {
+ return supportSubtitle;
+ }
+
+ public void setSupportSubtitle(int supportSubtitle) {
+ this.supportSubtitle = supportSubtitle;
+ }
+
+ public int getSupportSrcLang() {
+ return supportSrcLang;
+ }
+
+ public void setSupportSrcLang(int supportSrcLang) {
+ this.supportSrcLang = supportSrcLang;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/CallConstant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/CallConstant.java
new file mode 100644
index 0000000..bad39c7
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/CallConstant.java
@@ -0,0 +1,111 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * This class is about call module constant definition
+ * 呼叫模块常量类
+ * @author Jackson
+ */
+public class CallConstant {
+
+ /**
+ * The constant TYPE_LOUD_SPEAKER.
+ * 扬声器
+ */
+ public static final int TYPE_LOUD_SPEAKER = 1;
+
+ /**
+ * The constant CAMERA_NON.
+ * 无摄像头
+ */
+ public static final int CAMERA_NON = -1;
+
+ /**
+ * Rear camera
+ * 后置摄像头
+ */
+ public static final int BACK_CAMERA = 0;
+
+ /**
+ * front camera
+ * 前置摄像头
+ */
+ public static final int FRONT_CAMERA = 1;
+
+ /**
+ * This class is about call event enumeration
+ * 呼叫事件枚举类
+ */
+ public enum CallEvent
+ {
+ CALL_COMING(),
+
+ CALL_GOING(),
+
+ PLAY_RING_BACK_TONE(),
+
+ CALL_CONNECTED(),
+
+ CALL_ENDED(),
+
+ CALL_ENDED_FAILED(),
+
+ SESSION_MODIFIED(),
+
+ RTP_CREATED(),
+
+
+ AUDIO_HOLD_SUCCESS(),
+ AUDIO_HOLD_FAILED(),
+ VIDEO_HOLD_SUCCESS(),
+ VIDEO_HOLD_FAILED(),
+ UN_HOLD_SUCCESS(),
+ UN_HOLD_FAILED(),
+ DIVERT_FAILED(),
+ BLD_TRANSFER_SUCCESS(),
+ BLD_TRANSFER_FAILED(),
+
+ OPEN_VIDEO(),
+ CLOSE_VIDEO(),
+ REMOTE_REFUSE_ADD_VIDEO_SREQUEST(),
+ RECEIVED_REMOTE_ADD_VIDEO_REQUEST(),
+
+
+ CONF_INFO_NOTIFY(),
+
+ CONF_INCOMING(),
+ CONF_END(),
+
+
+ CALL_VIDEO_TO_AUDIO(),
+
+ ADD_LOCAL_VIEW(),
+
+ DEL_LOCAL_VIEW(),
+
+ DEVICE_CHANGED(),
+
+ REFRESH_ROUTE(), UNKNOWN()
+
+ }
+
+ /**
+ * This class is about call type enumeration
+ * 呼叫类型枚举类
+ */
+ public enum CallStatus
+ {
+ IDLE(),
+ AUDIO_CALLING(),
+ VIDEO_CALLING(),
+ UNKNOWN()
+ }
+ /**
+ * 网络状态监听
+ */
+ public enum NETWORK{
+ WIFI_CONNECT(),
+ MOBILE_CONNECT(),
+ ALL_NOT_CONNECT(),
+ ALL_CONNECT()
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/CallInfo.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/CallInfo.java
new file mode 100644
index 0000000..85df719
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/CallInfo.java
@@ -0,0 +1,236 @@
+package com.tengshisoft.chatmodule.beans;
+
+import java.io.Serializable;
+
+/**
+ * This class is about transfer between call information modules
+ * 呼叫信息模块间传递类
+ */
+public class CallInfo implements Serializable {
+ /**
+ * call ID
+ * 呼叫id
+ */
+ private int callID;
+
+ /**
+ * conference ID
+ * 会议id
+ */
+ private String confID;
+
+ /**
+ * Peer number
+ * 来电号码
+ */
+ private String peerNumber;
+
+ /**
+ * Peer display name
+ * 来电人
+ */
+ private String peerDisplayName;
+
+ /**
+ * Video call
+ * 是否是视频来电
+ */
+ private boolean isVideoCall;
+
+ /**
+ * Conference identify
+ * 是否是会议
+ */
+ private boolean isFocus;
+
+ /**
+ * Caller
+ * 主叫
+ */
+ private boolean isCaller;
+
+ /**
+ * Reason code
+ * 错误码
+ */
+ private int reasonCode;
+
+ /**
+ * Maybe video call
+ * 可能是视频呼叫
+ */
+ private Boolean maybeVideoCall;
+
+
+ @Override
+ public String toString() {
+ return "CallInfo{" +
+ "callID=" + callID +
+ ", confID='" + confID + '\'' +
+ ", peerNumber='" + peerNumber + '\'' +
+ ", peerDisplayName='" + peerDisplayName + '\'' +
+ ", isVideoCall=" + isVideoCall +
+ ", isFocus=" + isFocus +
+ ", isCaller=" + isCaller +
+ ", reasonCode=" + reasonCode +
+ ", maybeVideoCall=" + maybeVideoCall +
+ '}';
+ }
+
+
+ public int getCallID() {
+ return callID;
+ }
+
+ public void setCallID(int callID) {
+ this.callID = callID;
+ }
+
+ public String getConfID() {
+ return confID;
+ }
+
+ public void setConfID(String confID) {
+ this.confID = confID;
+ }
+
+ public String getPeerNumber() {
+ return peerNumber;
+ }
+
+ public void setPeerNumber(String peerNumber) {
+ this.peerNumber = peerNumber;
+ }
+
+ public String getPeerDisplayName() {
+ return peerDisplayName;
+ }
+
+ public void setPeerDisplayName(String peerDisplayName) {
+ this.peerDisplayName = peerDisplayName;
+ }
+
+ public boolean isVideoCall() {
+ return isVideoCall;
+ }
+
+ public void setVideoCall(boolean videoCall) {
+ isVideoCall = videoCall;
+ }
+
+ public boolean isFocus() {
+ return isFocus;
+ }
+
+ public void setFocus(boolean focus) {
+ isFocus = focus;
+ }
+
+ public boolean isCaller() {
+ return isCaller;
+ }
+
+ public void setCaller(boolean caller) {
+ isCaller = caller;
+ }
+
+
+ public int getReasonCode() {
+ return reasonCode;
+ }
+
+ public void setReasonCode(int reasonCode) {
+ this.reasonCode = reasonCode;
+ }
+
+ public Boolean getMaybeVideoCall() {
+ return maybeVideoCall;
+ }
+
+ public void setMaybeVideoCall(Boolean maybeVideoCall) {
+ this.maybeVideoCall = maybeVideoCall;
+ }
+
+ public static class Builder implements Serializable{
+ //呼叫ID
+ private int callID;
+ //会议ID
+ private String confID;
+ //对端号码
+ private String peerNumber;
+ //对端名称
+ private String peerDisplayName;
+
+ //是否是视频呼叫
+ private boolean isVideoCall;
+ //是否是会议
+ private boolean isFocus;
+ private boolean isCaller;
+ private Boolean maybeVideoCall;
+
+ private int reasonCode;
+
+ public Builder setCallID(int callID) {
+ this.callID = callID;
+ return this;
+ }
+
+ public Builder setConfID(String confID) {
+ this.confID = confID;
+ return this;
+ }
+
+ public Builder setPeerNumber(String peerNumber) {
+ this.peerNumber = peerNumber;
+ return this;
+ }
+
+ public Builder setPeerDisplayName(String peerDisplayName) {
+ this.peerDisplayName = peerDisplayName;
+ return this;
+ }
+
+ public Builder setVideoCall(boolean videoCall) {
+ isVideoCall = videoCall;
+ return this;
+ }
+
+ public Builder setFocus(boolean focus) {
+ isFocus = focus;
+ return this;
+ }
+
+ public Builder setCaller(boolean caller) {
+ isCaller = caller;
+ return this;
+ }
+
+ public Builder setReasonCode(int reasonCode) {
+ this.reasonCode = reasonCode;
+ return this;
+ }
+
+ public void setMaybeVideoCall(Boolean maybeVideoCall) {
+ this.maybeVideoCall = maybeVideoCall;
+ }
+
+ private void apply(CallInfo params) {
+ params.callID = this.callID;
+ params.confID = this.confID;
+ params.peerNumber = this.peerNumber;
+ params.peerDisplayName = this.peerDisplayName;
+
+ params.isVideoCall = this.isVideoCall;
+ params.isFocus = this.isFocus;
+ params.isCaller = this.isCaller;
+ params.reasonCode = this.reasonCode;
+ params.maybeVideoCall = this.maybeVideoCall;
+ }
+
+ public CallInfo build() {
+ CallInfo callInfo = new CallInfo();
+ apply(callInfo);
+ return callInfo;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ConfBaseInfo.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ConfBaseInfo.java
new file mode 100644
index 0000000..72c277b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ConfBaseInfo.java
@@ -0,0 +1,389 @@
+package com.tengshisoft.chatmodule.beans;
+
+
+import com.huawei.ecterminalsdk.base.TsdkConfGeneralInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfMediaType;
+import com.tenlionsoft.baselib.constant.ConfConstant;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * This class is about conference basic information for meeting List query results
+ * 会议基础信息类
+ * 用于会议列表查询结果
+ */
+public class ConfBaseInfo implements Serializable {
+ /**
+ * conference size
+ * 会议个数
+ */
+ private int size;
+
+ /**
+ * conf ID
+ * 会议id
+ */
+ private String confID;
+
+ /**
+ * conf ID 3.0
+ * 会议id
+ */
+
+ /**
+ * 3.0:预订者帐号
+ */
+ private String scheduleUserAccount;
+
+ /**
+ * 3.0:预订者姓名
+ */
+ private String scheduleUserName;
+
+ /**
+ * conf ID
+ * 会议id
+ */
+ private String confIdV3;
+
+ /**
+ * conference subject
+ * 会议主题
+ */
+ private String subject;
+
+ /**
+ * Access number
+ * 会议接入号
+ */
+ private String accessNumber;
+
+ /**
+ * active
+ * 是否激活 0:未激活,1:激活
+ */
+ private int active;
+
+ /**
+ * chairman pwd
+ * 主席密码
+ */
+ private String chairmanPwd;
+
+ /**
+ * guest pwd
+ * 普通与会者密码
+ */
+ private String guestPwd;
+
+ /**
+ * conference start time
+ * 会议开始时间
+ */
+ private String startTime;
+
+ /**
+ * conference end time
+ * 会议结束时间
+ */
+ private String endTime;
+ private String schedulerNumber;
+ private String schedulerName;
+ private String groupUri;
+ private String guestLink;
+
+ /**
+ * conference end time
+ * 会议剩余时间
+ */
+ private long remainTime;
+
+ /**
+ * Conference type
+ * 会议类型
+ */
+ private TsdkConfMediaType mediaType;
+
+ /**
+ * Conference state
+ * 会议状态
+ */
+ private ConfConstant.ConfConveneStatus confState;
+
+ /**
+ * Lock
+ * 是否锁定
+ */
+ private boolean isLock;
+
+ /**
+ * record
+ * 是否录制
+ */
+ private boolean record;
+
+ /**
+ * record
+ * 是否录制
+ */
+ private boolean supportRecord;
+
+ /**
+ * Mute all
+ * 是否全部静音
+ */
+ private boolean isMuteAll;
+
+ /**
+ * Join conference
+ * 是否加入会议
+ */
+ private boolean isJoin;
+
+ /**
+ * 3.0:与会人成员
+ */
+ private List attendees;
+
+ /**
+ * 3.0:与会人成员个数(最多返回五十个)
+ */
+ private int attendeesNum;
+
+ /**
+ * 3.0:会议类型 :0-多流;1-单流;2-语音
+ */
+ private int conferenceType;
+
+ /**
+ * 3.0:会议时间类型:0-立即;1-延时;2-周期,会议列表结果字段
+ */
+ private int conferenceTimeType;
+
+ public int getConferenceTimeType() {
+ return conferenceTimeType;
+ }
+
+ public void setConferenceTimeType(int conferenceTimeType) {
+ this.conferenceTimeType = conferenceTimeType;
+ }
+
+ public int getAttendeesNum() {
+ return attendeesNum;
+ }
+
+ public void setAttendeesNum(int attendeesNum) {
+ this.attendeesNum = attendeesNum;
+ }
+
+ public int getConferenceType() {
+ return conferenceType;
+ }
+
+ public void setConferenceType(int conferenceType) {
+ this.conferenceType = conferenceType;
+ }
+
+ public List getAttendees() {
+ return attendees;
+ }
+
+ public void setAttendees(List attendees) {
+ this.attendees = attendees;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public void setSize(int size) {
+ this.size = size;
+ }
+
+ public String getConfID() {
+ return confID;
+ }
+
+ public void setConfID(String confID) {
+ this.confID = confID;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getAccessNumber() {
+ return accessNumber;
+ }
+
+ public void setAccessNumber(String accessNumber) {
+ this.accessNumber = accessNumber;
+ }
+
+ public String getChairmanPwd() {
+ return chairmanPwd;
+ }
+
+ public void setChairmanPwd(String chairmanPwd) {
+ this.chairmanPwd = chairmanPwd;
+ }
+
+ public String getGuestPwd() {
+ return guestPwd;
+ }
+
+ public void setGuestPwd(String guestPwd) {
+ this.guestPwd = guestPwd;
+ }
+
+ public String getGuestLink() {
+ return guestLink;
+ }
+
+ public void setGuestLink(String guestLink) {
+ this.guestLink = guestLink;
+ }
+
+ public String getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(String startTime) {
+ this.startTime = startTime;
+ }
+
+ public String getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(String endTime) {
+ this.endTime = endTime;
+ }
+
+ public String getSchedulerNumber() {
+ return schedulerNumber;
+ }
+
+ public void setSchedulerNumber(String schedulerNumber) {
+ this.schedulerNumber = schedulerNumber;
+ }
+
+ public String getSchedulerName() {
+ return schedulerName;
+ }
+
+ public void setSchedulerName(String schedulerName) {
+ this.schedulerName = schedulerName;
+ }
+
+ public TsdkConfMediaType getMediaType() {
+ return mediaType;
+ }
+
+ public String getGroupUri() {
+ return groupUri;
+ }
+
+ public void setGroupUri(String groupUri) {
+ this.groupUri = groupUri;
+ }
+
+ public void setMediaType(TsdkConfMediaType mediaType) {
+ this.mediaType = mediaType;
+ }
+
+ public ConfConstant.ConfConveneStatus getConfState() {
+ return confState;
+ }
+
+ public void setConfState(ConfConstant.ConfConveneStatus confState) {
+ this.confState = confState;
+ }
+
+ public boolean isLock() {
+ return isLock;
+ }
+
+ public void setLock(boolean lock) {
+ isLock = lock;
+ }
+
+ public boolean isRecord() {
+ return record;
+ }
+
+ public void setRecord(boolean record) {
+ this.record = record;
+ }
+
+ public boolean isSupportRecord() {
+ return supportRecord;
+ }
+
+ public void setSupportRecord(boolean supportRecord) {
+ this.supportRecord = supportRecord;
+ }
+
+ public boolean isMuteAll() {
+ return isMuteAll;
+ }
+
+ public void setMuteAll(boolean muteAll) {
+ isMuteAll = muteAll;
+ }
+
+
+ public boolean isJoin() {
+ return isJoin;
+ }
+
+ public void setJoin(boolean join) {
+ isJoin = join;
+ }
+
+ public long getRemainTime() {
+ return remainTime;
+ }
+
+ public void setRemainTime(long remainTime) {
+ this.remainTime = remainTime;
+ }
+
+ public int getActive() {
+ return active;
+ }
+
+ public void setActive(int active) {
+ this.active = active;
+ }
+
+ public String getScheduleUserAccount() {
+ return scheduleUserAccount;
+ }
+
+ public void setScheduleUserAccount(String scheduleUserAccount) {
+ this.scheduleUserAccount = scheduleUserAccount;
+ }
+
+ public String getScheduleUserName() {
+ return scheduleUserName;
+ }
+
+ public void setScheduleUserName(String scheduleUserName) {
+ this.scheduleUserName = scheduleUserName;
+ }
+
+ public String getConfIdV3() {
+ return confIdV3;
+ }
+
+ public void setConfIdV3(String confIdV3) {
+ this.confIdV3 = confIdV3;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ConferenceConstant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ConferenceConstant.java
new file mode 100644
index 0000000..585d874
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ConferenceConstant.java
@@ -0,0 +1,15 @@
+package com.tengshisoft.chatmodule.beans;
+
+// 会议工具类
+public class ConferenceConstant {
+
+ // 构造函数私有防止不必要的初始化
+ private ConferenceConstant() {
+ throw new UnsupportedOperationException("you can not instantiate me...");
+ }
+
+ public static final String VOICE_CALL = "voice_call";
+ public static final String VIDEO_CALL = "video_call";
+ public static final String VIDEO_VOICE_LAYOUT_CALL = "video_voice_layout_call";
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ControlOperationsResults.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ControlOperationsResults.java
new file mode 100644
index 0000000..7ca4483
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ControlOperationsResults.java
@@ -0,0 +1,24 @@
+package com.tengshisoft.chatmodule.beans;
+
+public class ControlOperationsResults {
+ public static class ChairmanResult {
+ private int result;
+ private String name;
+
+ public int getResult() {
+ return result;
+ }
+
+ public void setResult(int result) {
+ this.result = result;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/EmptyFactory.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/EmptyFactory.java
new file mode 100644
index 0000000..85c4450
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/EmptyFactory.java
@@ -0,0 +1,16 @@
+package com.tengshisoft.chatmodule.beans;
+
+import com.tengshisoft.chatmodule.hwclud.api.IPermission;
+import com.tengshisoft.chatmodule.hwclud.api.IPermissionFactory;
+
+public class EmptyFactory implements IPermissionFactory {
+ @Override
+ public IPermission permission() {
+ return null;
+ }
+
+ @Override
+ public IPermission multiPermission(Class>... factories) {
+ return null;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/IntentConstant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/IntentConstant.java
new file mode 100644
index 0000000..c50f136
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/IntentConstant.java
@@ -0,0 +1,10 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * Intent跳转意图
+ */
+public class IntentConstant {
+
+ public static final String DEFAULT_CATEGORY = "android.intent.category.cloundlink";
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/LdapFrontstageConstant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/LdapFrontstageConstant.java
new file mode 100644
index 0000000..c5e3f53
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/LdapFrontstageConstant.java
@@ -0,0 +1,37 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * This class is about definition of enterprise address constant.
+ * Ldap地址本功能常量类
+ */
+public class LdapFrontstageConstant {
+
+ public enum Event
+ {
+ /**
+ * Failed to search contacts
+ * 查询联系人失败
+ */
+ SEARCH_CONTACTS_FAILED(),
+
+ /**
+ * No contacts were searched
+ * 查询到0个联系人
+ */
+ SEARCH_CONTACTS_NOT_FOUND(),
+
+ /**
+ * Search contacts completed
+ * 查询联系人成功
+ */
+ SEARCH_CONTACTS_COMPLETE(),
+
+ /**
+ * Search self
+ * 查询自己的信息
+ */
+ SEARCH_SELF_COMPLETE(),
+
+ UNKNOWN()
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/MeetingTitleInfoBean.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/MeetingTitleInfoBean.java
new file mode 100644
index 0000000..505b406
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/MeetingTitleInfoBean.java
@@ -0,0 +1,85 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * @author limp
+ * @date 2020/12/16
+ */
+public class MeetingTitleInfoBean {
+ String subject = "";
+ String time = "";
+ String chairmanPwd = "";
+ String guestPwd = "";
+
+ String confID = "";
+ String terminal = "";
+ String scheduleUserName = "";
+
+ public String getScheduleUserName() {
+ return scheduleUserName;
+ }
+
+ public void setScheduleUserName(String scheduleUserName) {
+ this.scheduleUserName = scheduleUserName;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getTime() {
+ return time;
+ }
+
+ public void setTime(String time) {
+ this.time = time;
+ }
+
+ public String getChairmanPwd() {
+ return chairmanPwd;
+ }
+
+ public void setChairmanPwd(String chairmanPwd) {
+ this.chairmanPwd = chairmanPwd;
+ }
+
+ public String getGuestPwd() {
+ return guestPwd;
+ }
+
+ public void setGuestPwd(String guestPwd) {
+ this.guestPwd = guestPwd;
+ }
+
+ public String getConfID() {
+ return confID;
+ }
+
+ public void setConfID(String confID) {
+ this.confID = confID;
+ }
+
+ public String getTerminal() {
+ return terminal;
+ }
+
+ public void setTerminal(String terminal) {
+ this.terminal = terminal;
+ }
+
+ @Override
+ public String toString() {
+ return "MeetingTitleInfoBean{" +
+ "subject='" + subject + '\'' +
+ ", time='" + time + '\'' +
+ ", chairmanPwd='" + chairmanPwd + '\'' +
+ ", guestPwd='" + guestPwd + '\'' +
+ ", confID='" + confID + '\'' +
+ ", terminal='" + terminal + '\'' +
+ ", scheduleUserName='" + scheduleUserName + '\'' +
+ '}';
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/MyTsdkCallInfo.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/MyTsdkCallInfo.java
new file mode 100644
index 0000000..8cef299
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/MyTsdkCallInfo.java
@@ -0,0 +1,99 @@
+package com.tengshisoft.chatmodule.beans;
+
+import java.io.Serializable;
+
+public class MyTsdkCallInfo implements Serializable {
+ private String confId;//会议id
+ private String subject;//会议主题
+ private String peerNumber;//对端号码
+ private boolean isConf;
+ private int handle;
+ private boolean isSVC;
+ private int ssrcTableStart;
+ private int ssrcTableEnd;
+ private int callId;
+
+ public int getCallId() {
+ return callId;
+ }
+
+ public void setCallId(int callId) {
+ this.callId = callId;
+ }
+
+ public int getSsrcTableEnd() {
+ return ssrcTableEnd;
+ }
+
+ public void setSsrcTableEnd(int ssrcTableEnd) {
+ this.ssrcTableEnd = ssrcTableEnd;
+ }
+
+ public int getSsrcTableStart() {
+ return ssrcTableStart;
+ }
+
+ public void setSsrcTableStart(int ssrcTableStart) {
+ this.ssrcTableStart = ssrcTableStart;
+ }
+
+ public boolean isSVC() {
+ return isSVC;
+ }
+
+ public void setSVC(boolean SVC) {
+ isSVC = SVC;
+ }
+
+ public int getHandle() {
+ return handle;
+ }
+
+ public void setHandle(int handle) {
+ this.handle = handle;
+ }
+
+ public boolean isConf() {
+ return isConf;
+ }
+
+ public void setConf(boolean conf) {
+ isConf = conf;
+ }
+
+ public MyTsdkCallInfo(String confId, String subject, String peerNumber, boolean isConf, boolean isSVC, int handle, int ssrcTableStart, int ssrcTableEnd, int callId) {
+ this.confId = confId;
+ this.subject = subject;
+ this.peerNumber = peerNumber;
+ this.isConf = isConf;
+ this.isSVC = isSVC;
+ this.handle = handle;
+ this.ssrcTableStart = ssrcTableStart;
+ this.ssrcTableEnd = ssrcTableEnd;
+ this.callId = callId;
+ }
+
+ public String getConfId() {
+ return confId;
+ }
+
+ public void setConfId(String confId) {
+ this.confId = confId;
+ }
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ public String getPeerNumber() {
+ return peerNumber;
+ }
+
+ public void setPeerNumber(String peerNumber) {
+ this.peerNumber = peerNumber;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ServiceSettingBeanV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ServiceSettingBeanV2.java
new file mode 100644
index 0000000..949ef4e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/ServiceSettingBeanV2.java
@@ -0,0 +1,87 @@
+package com.tengshisoft.chatmodule.beans;
+
+
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+/**
+ * @Time: 2021/6/24
+ * @Author: isoftstone
+ * @Description:服务器设置信息存储
+ */
+public class ServiceSettingBeanV2 {
+
+ /**
+ * 获取服务器SRTP模式
+ *
+ * @return 模式
+ */
+ public int getRtpMode() {
+ return EncryptedSPTool.getInt(Constant.LOGIN_SERVER_SRTP_MODE, 1);
+ }
+
+ /**
+ * 设置服务器SRTP模式
+ *
+ * @param model 模式
+ */
+ public void setRtpMode(int model) {
+ EncryptedSPTool.putInt(Constant.LOGIN_SERVER_SRTP_MODE, model);
+ }
+
+ public int getSipTransport() {
+ return EncryptedSPTool.getInt(Constant.UPD_TLS, 1);
+ }
+
+ public void setSipTransport(int sipTransport) {
+ EncryptedSPTool.putInt(Constant.UPD_TLS, sipTransport);
+ }
+
+ /**
+ * 获取BFCP模式
+ * 1:UDP,2:TCP,4:TLS,7:自动
+ */
+ public int getBfpTransportMode() {
+ return EncryptedSPTool.getInt(Constant.BFCP_TRANSPORT_MODE, 7);
+ }
+
+ /**
+ * BFCP模式
+ *
+ * @param transportMode 模式
+ */
+ public void setBfpTransportMode(int transportMode) {
+ EncryptedSPTool.putInt(Constant.BFCP_TRANSPORT_MODE, transportMode);
+ }
+
+ /**
+ * 是否开启GM
+ *
+ * @return
+ */
+ public boolean getIsOpenGm() {
+ return EncryptedSPTool.getBoolean(Constant.IS_OPEN_GM);
+ }
+
+ /**
+ * 设置GM是否开启
+ *
+ * @param isOpen
+ */
+ public void setIsOpenGm(boolean isOpen) {
+ EncryptedSPTool.putBoolean(Constant.IS_OPEN_GM, isOpen);
+ }
+
+ private static ServiceSettingBeanV2 serviceSettingBean;
+
+ public static ServiceSettingBeanV2 getInstance() {
+ if (serviceSettingBean == null) {
+ synchronized (ServiceSettingBeanV2.class) {
+ if (serviceSettingBean == null) {
+ serviceSettingBean = new ServiceSettingBeanV2();
+ }
+ }
+ }
+ return serviceSettingBean;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/Session.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/Session.java
new file mode 100644
index 0000000..09938ba
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/Session.java
@@ -0,0 +1,279 @@
+package com.tengshisoft.chatmodule.beans;
+
+import com.huawei.ecterminalsdk.base.TsdkDtmfTone;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+/**
+ * This class is about call session
+ * 呼叫会话类
+ */
+public class Session {
+ private static final String TAG = Session.class.getSimpleName();
+
+ /**
+ * call object
+ * 呼叫信息
+ */
+ private TsdkCall tsdkCall;
+
+ /**
+ * call type
+ * 呼叫类型
+ */
+ private CallConstant.CallStatus callStatus = CallConstant.CallStatus.IDLE;
+
+ /**
+ * hold video
+ * 是否视频保持
+ */
+ private boolean isVideoHold;
+
+ /**
+ * Blind transfer
+ * 是否盲转
+ */
+ private boolean isBlindTransfer;
+
+
+ private int callId;
+
+ public Session(TsdkCall tsdkCall) {
+ this.tsdkCall = tsdkCall;
+ this.callId = tsdkCall.getCallInfo().getCallId();
+ }
+
+ public TsdkCall getTsdkCall() {
+ return tsdkCall;
+ }
+
+ public int getCallID() {
+ return this.callId;
+ }
+
+ public CallConstant.CallStatus getCallStatus() {
+ return callStatus;
+ }
+
+ public void setCallStatus(CallConstant.CallStatus callStatus) {
+ this.callStatus = callStatus;
+ }
+
+ public boolean isVideoHold() {
+ return isVideoHold;
+ }
+
+ public void setVideoHold(boolean videoHold) {
+ isVideoHold = videoHold;
+ }
+
+ public boolean isBlindTransfer() {
+ return isBlindTransfer;
+ }
+
+ public void setBlindTransfer(boolean blindTransfer) {
+ this.isBlindTransfer = blindTransfer;
+ }
+
+ /**
+ * This method is used to answer the call
+ *
+ * @param isVideo
+ * @return
+ */
+ public boolean answerCall(boolean isVideo) {
+ if (isVideo) {
+ initVideoWindow();
+ VideoMgr.getInstance().setVideoOrient(getCallID(), CallConstant.FRONT_CAMERA);
+ CallMgrV2.getInstance().closeCamera(getCallID());
+ }
+ int result = tsdkCall.answerCall(isVideo);
+ LogUtils.d("answerCall] [isVideo=" + isVideo, result);
+ return result == 0;
+ }
+
+
+ /**
+ * This method is used to launched divert Call
+ * 发起偏转呼叫
+ *
+ * divert call
+ *
+ * @param divertNumber
+ * @return
+ */
+ public boolean divertCall(String divertNumber) {
+ return true;
+ }
+
+ /**
+ * start blind transfer request
+ * 发起盲转呼叫请求
+ *
+ * @param transferNumber 盲转号码
+ * @return
+ */
+ public boolean blindTransfer(String transferNumber) {
+ return true;
+ }
+
+ /**
+ * Call hold
+ * 呼叫保持
+ *
+ * @return
+ */
+ public boolean holdCall() {
+ return true;
+ }
+
+
+ /**
+ * Cancel Call hold
+ * 取消呼叫保持
+ *
+ * @return
+ */
+ public boolean unHoldCall() {
+ return true;
+ }
+
+ /**
+ * send DTMF during call
+ * 二次拨号
+ *
+ * @param code (0到9,*为10,#为11)
+ * @return
+ */
+ public boolean reDial(int code) {
+ TsdkDtmfTone tsdkDtmfTone = TsdkDtmfTone.enumOf(code);
+ if (null == tsdkDtmfTone) {
+ LogUtils.e(TAG, "tsdkDtmfTone is null");
+ return false;
+ }
+ LogUtils.d(TAG, "Dtmf Tone :" + tsdkDtmfTone.getIndex());
+ int result = tsdkCall.sendDtmf(tsdkDtmfTone);
+ if (result != 0) {
+ LogUtils.e(TAG, "sendDTMF return failed, result = " + result);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * add video
+ * 音频转视频请求
+ *
+ * @return
+ */
+ public boolean addVideo() {
+ initVideoWindow();
+ int result = tsdkCall.addVideo();
+ if (result != 0) {
+ LogUtils.e(TAG, "addVideo return failed, result = " + result);
+ return false;
+ }
+
+ setCallStatus(CallConstant.CallStatus.VIDEO_CALLING);
+ return true;
+ }
+
+ /**
+ * delet video
+ * 删除视频请求
+ *
+ * @return
+ */
+ public boolean delVideo() {
+ int result = tsdkCall.delVideo();
+ if (result != 0) {
+ LogUtils.e(TAG, "delVideo return failed, result = " + result);
+ return false;
+ }
+
+ setCallStatus(CallConstant.CallStatus.AUDIO_CALLING);
+
+ return true;
+ }
+
+ /**
+ * Reject Audio Transfer Video Call
+ * 拒绝音频转视频呼叫
+ *
+ * @return
+ */
+ public boolean rejectAddVideo() {
+ int result = tsdkCall.replyAddVideo(false);
+ if (result != 0) {
+ LogUtils.e(TAG, "replyAddVideo(reject) return failed, result = " + result);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Agree to Audio transfer video Call
+ * 同意音频转视频呼叫
+ *
+ * @return
+ */
+ public boolean acceptAddVideo() {
+ initVideoWindow();
+
+ int result = tsdkCall.replyAddVideo(true);
+ if (result != 0) {
+ LogUtils.e(TAG, "replyAddVideo(accept) return failed, result = " + result);
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * set media microphone mute
+ * 设置(或取消)麦克风静音
+ *
+ * @param mute
+ * @return
+ */
+ public boolean muteMic(boolean mute) {
+ int result = tsdkCall.muteMic(mute);
+ LogUtils.d("点对点----是否静音" + mute);
+ MeetingController.getInstance().isVoiceOpen = mute;
+ if (result != 0) {
+ LogUtils.e(TAG, "mediaMuteMic return failed, result = " + result);
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * set media speaker mute
+ * 设置(或取消)扬声器静音
+ *
+ * @param mute
+ * @return
+ */
+ public boolean muteSpeak(boolean mute) {
+ int result = tsdkCall.muteSpeaker(mute);
+ if (result != 0) {
+ LogUtils.e(TAG, "mediaMuteMic return failed, result = " + result);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Initializing the video window
+ * 初始化视频窗口
+ */
+ public void initVideoWindow() {
+ VideoMgr.getInstance().initVideoWindow(getCallID());
+ }
+
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/SponsorMeetingConstant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/SponsorMeetingConstant.java
new file mode 100644
index 0000000..1c8a172
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/SponsorMeetingConstant.java
@@ -0,0 +1,12 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * @author 86137
+ * @date 2020/11/17
+ */
+public interface SponsorMeetingConstant {
+ String CONF_ID="SponsorMeetingConfID";
+ String IS_VOICE_TO_VIDEO_FORM_MINIMIZE="is_voice_to_video_form_minimize";
+ String VOICE_DISPLAY_NAME="voice_display_name";
+ String VOICE_NUMBER="voice_number";
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/SvcWatchStatus.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/SvcWatchStatus.java
new file mode 100644
index 0000000..7b49aef
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/SvcWatchStatus.java
@@ -0,0 +1,14 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * @Time: 2021/7/01
+ * @Author: isoftstone
+ * @Description: SVC会议观看类型
+ */
+public enum SvcWatchStatus {
+ None,
+ AUX_DATA,
+ MINI_WATCH,
+ PIP_WATCH,
+ GALLERY_WATCH
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/TSDKErrorConstant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/TSDKErrorConstant.java
new file mode 100644
index 0000000..c7e134f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/TSDKErrorConstant.java
@@ -0,0 +1,365 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * @date : 2021/5/27
+ * @author: isoftstone
+ * @Description : sdk错误码常量类
+ */
+public class TSDKErrorConstant {
+
+ /**
+ * 网络环境错误
+ */
+ public static final int TSDK_E_CALL_NET_ERROR = 50331708;
+ /**
+ * 发起会议失败,请联系管理员 呼叫失败,请联系管理员
+ */
+ public static final int TSDK_E_CALL_ERR_50331770 = 50331770;
+
+ /**
+ * 发起会议失败,请联系管理员 呼叫失败,请联系管理员
+ */
+ public static final int TSDK_NET_ERROR_CODE = 50331678;
+ /**
+ * 不允许修改VMR信息
+ */
+ public static final int UPDATE_VMR_PWD_ERROR_CODE2 = 67109102;
+
+ /**
+ * VMR会议创建数目超出规格
+ */
+ public static final int UPDATE_VMR_PWD_ERROR_CODE1 = 67109101;
+
+ /**
+ * 发起或预约会议失败错误码
+ */
+ public static final int TSDK_BOOK_MEETING_ERROR_CODE = 67109096;
+
+ /**
+ * 请求超时
+ */
+ public static final int TSDK_MEETING_OVERTIME_ERROR_CODE = 67108873;
+ /**
+ * 错误的用户ID
+ */
+ public static final int TSDK_USER_ID_ERROR_CODE = 67108944;
+ /**
+ * 会议正在召开,不能取消
+ */
+ public static final int TSDK_MEETING_ING_NOT_CANCEL_CODE = 67108866;
+ /**
+ * 鉴权失败
+ */
+ public static final int TSDK_AUTHENTICATION_ERROR_CODE = 67108876;
+
+
+ /**
+ * SDK正在初始化失败
+ */
+ public static final int TSDK_INIT_ERROR_CODE = -101;
+
+ /**
+ * 请求超时
+ */
+ public static final int TSDK_QUEST_OVERTIME_CODE = -1;
+ /**
+ * 无取消预约会议权限
+ */
+ public static final int TSDK_CANCEL_MEETING_NO_ACCESS_CODE = 67109055;
+
+ /**
+ * 会议未激活
+ */
+ public static final int TSDK_CONF_NOT_ACTIVITY_CODE = 67109056;
+ /**
+ * 会议不存在
+ */
+ public static final int TSDK_CONF_NOT_EXIST_CODE = 67109057;
+
+ /**
+ * 会议预约数达到上限
+ */
+ public static final int TSDK_BOOK_MAX_LIMIT_ERROR_CODE = 67109058;
+
+ /**
+ * 会议中最多包含3000个会场
+ */
+ public static final int TSDK_OVER_VENUE_ERROR_CODE = 67109062;
+
+ /**
+ * 会议服务未完全加载好,请稍后再试
+ */
+ public static final int TSDK_NOT_LOADED_ERROR_CODE = 67109065;
+
+ /**
+ * MCU级联数量超过最大限制
+ */
+ public static final int TSDK_MCU_OVER_LIMIT_ERROR_CODE = 67109068;
+
+ /**
+ * MCU资源不足
+ */
+ public static final int TSDK_MCU_RESOURCE_UNDER_ERROR_CODE = 67109070;
+
+ /**
+ * MCU视频资源不足
+ */
+ public static final int TSDK_MCU_VIDEO_UNDER_ERROR_CODE = 67109071;
+
+ /**
+ * SVC端口资源不足
+ */
+ public static final int TSDK_SVC_PORT_UNDER_ERROR_CODE = 67109072;
+
+ /**
+ * 加解密资源不足
+ */
+ public static final int TSDK_ENCRYPTION_UNDER_ERROR_CODE = 67109073;
+
+ /**
+ * 带宽资源不足
+ */
+ public static final int TSDK_BROADBAND_UNDER_ERROR_CODE = 67109074;
+
+ /**
+ * 会场资源不足
+ */
+ public static final int TSDK_VENUE_UNDER_ERROR_CODE = 67109075;
+
+ /**
+ * 音频资源不足
+ */
+ public static final int TSDK_AUDIO_UNDER_ERROR_CODE = 67109076;
+
+ /**
+ * 会场能力高于会议能力
+ */
+ public static final int TSDK_VENUE_HIGHER_MEETING_CODE = 67109060;
+
+ /**
+ * 会场号码重复
+ */
+ public static final int TSDK_VENUE_NUMBER_REPEAT_CODE = 67109061;
+
+ /**
+ * SMC禁止终端创建会议
+ */
+ public static final int TSDK_SMC_BAN_MEETING_ERROR_CODE = 67109063;
+
+ /**
+ * 不支持召开强制加密会议
+ */
+ public static final int TSDK_UNSUPPORTED_ENCRYPT_CODE = 67109064;
+
+ /**
+ * 系统有未结束会议,无法修改国密配置
+ */
+ public static final int TSDK_NOT_UPDATE_SETUP_CODE = 67109066;
+
+ /**
+ * 创建会议失败,加密方式必须设置为强制加密
+ */
+ public static final int TSDK_MUST_FORCED_ENCRYPT_CODE = 67109067;
+
+ /**
+ * 语音会场和级联会场不能设置为主会场
+ */
+ public static final int TSDK_NOT_SET_MAIN_VENUE_CODE = 67109069;
+
+ /**
+ * MCU不支持对应的视频协议
+ */
+ public static final int TSDK_MCU_UNSUPPORTED_VIDEO_CODE = 67109077;
+
+ /**
+ * MCU不支持对应的视频分辨率
+ */
+ public static final int TSDK_MCU_UNSUPPORTED_VIDEO_PS_CODE = 67109078;
+
+ /**
+ * MCU不支持对应的音频协议
+ */
+ public static final int TSDK_MCU_UNSUPPORTED_AUDIO_DEAL_CODE = 67109079;
+
+ /**
+ * MCU不支持对应的数据会议协议
+ */
+ public static final int TSDK_MCU_UNSUPPORTED_DATA_DEAL_CODE = 67109080;
+
+ /**
+ * MCU不支持对应的加解密类型
+ */
+ public static final int TSDK_MCU_UNSUPPORTED_ENCRYPT_CODE = 67109081;
+
+ /**
+ * 不允许来宾激活会议
+ */
+ public static final int TSDK_GUEST_ACTIVITY_CONF_CODE = 67109098;
+
+ /**
+ * SMC流控
+ */
+ public static final int TSDK_SMC_FLOW_CONTROL_CODE = 67109104;
+
+ /**
+ * 获取会议列表,分页page=1返回失败code
+ */
+ public static final int TSDK_GET_CONF_LIST_PAGINATION_FAILED = 67108864;
+
+ /**
+ *加入会议证书校验失败
+ */
+ public static final int TSDK_JOIN_CONF_CERTIFICATE_FAILED = 67109106;
+
+ /**
+ * 加入会议 BFCP 通道不匹配
+ */
+ public static final int TSDK_JOIN_CONF_BFCP_FAILED = 67109107;
+
+ /**
+ * 加入会议 会控建立失败
+ */
+ public static final int TSDK_JOIN_CONF_CONTROL_FAILED = 67109108;
+
+ /**
+ * 登录模块错误码:结束标志,无实际意义
+ */
+ public static final int LOGIN_ERROR_CODE_33554498 = 33554498;
+
+ /**
+ * 登录模块错误码:网络异常
+ */
+ public static final int LOGIN_ERROR_CODE_33554470 = 33554470;
+
+ /**
+ * 登录模块错误码:DNS解析异常
+ */
+ public static final int LOGIN_ERROR_CODE_33554444 = 33554444;
+
+ /**
+ * 登录模块错误码:超时
+ */
+ public static final int LOGIN_ERROR_CODE_33554441 = 33554441;
+
+ /**
+ * 登录模块错误码:账号已过期
+ */
+ public static final int LOGIN_ERROR_CODE_33554487 = 33554487;
+
+ /**
+ * 登录模块错误码:鉴权失败
+ */
+ public static final int LOGIN_ERROR_CODE_33554446 = 33554446;
+
+ /**
+ * 登录模块错误码:服务器异常
+ */
+ public static final int LOGIN_ERROR_CODE_33554448 = 33554448;
+
+ /**
+ * 登录模块错误码:账号被锁定
+ */
+ public static final int LOGIN_ERROR_CODE_33554449 = 33554449;
+
+ /**
+ * 登录模块错误码:用户已被锁定
+ */
+ public static final int LOGIN_ERROR_CODE_33554467 = 33554467;
+
+ /**
+ * 登录模块错误码:老密码错误
+ */
+ public static final int LOGIN_ERROR_CODE_33554457 = 33554457;
+
+ /**
+ * 登录模块错误码:查询服务器地址失败
+ */
+ public static final int LOGIN_ERROR_CODE_33554454 = 33554454;
+
+ /**
+ * 登录模块错误码:用户名或者密码错误
+ */
+ public static final int LOGIN_ERROR_CODE_33554466 = 33554466;
+
+ /**
+ * 登录模块错误码:账号未激活
+ */
+ public static final int LOGIN_ERROR_CODE_33554482 = 33554482;
+
+ /**
+ * 呼叫模块错误码:服务器返回“禁止”,响应码:403
+ */
+ public static final int CALL_ERROR_CODE_50331749 = 50331749;
+
+ /**
+ * 呼叫模块错误码:SIP TCP建立失败
+ */
+ public static final int CALL_ERROR_CODE_50331817 = 50331817;
+
+ /**
+ * 呼叫模块错误码:网络接入错误
+ */
+ public static final int CALL_ERROR_CODE_50331659 = 50331659;
+
+ /**
+ * 呼叫模块错误码:服务器返回“请求超时”,响应码:408
+ */
+ public static final int CALL_ERROR_CODE_50331754 = 50331754;
+
+ /**
+ * 呼叫模块错误码:服务器返回“临时失效”,响应码:480
+ */
+ public static final int CALL_ERROR_CODE_50331762 = 50331762;
+
+ /**
+ * 呼叫模块错误码:服务器返回“未发现”,响应码:404
+ */
+ public static final int CALL_ERROR_CODE_50331750 = 50331750;
+
+ /**
+ * 呼叫模块错误码:非标信令原因
+ */
+ public static final int CALL_ERROR_CODE_50331801 = 50331801;
+
+ /**
+ * 呼叫模块错误码:服务器返回“丢弃”,响应码:603
+ */
+ public static final int CALL_ERROR_CODE_50331781 = 50331781;
+
+ /**
+ * 呼叫模块错误码:未知错误
+ */
+ public static final int CALL_ERROR_CODE_50331648 = 50331648;
+
+ /**
+ * 呼叫模块错误码:服务器返回“这里请求不可接收”,响应码:488
+ */
+ public static final int CALL_ERROR_CODE_50331770 = 50331770;
+
+ /**
+ * 呼叫模块错误码:服务器返回“服务无效”,响应码:503
+ */
+ public static final int CALL_ERROR_CODE_50331776 = 50331776;
+
+ /**
+ * 呼叫模块错误码:服务器返回“请求终止”,响应码:487
+ */
+ public static final int CALL_ERROR_CODE_50331769 = 50331769;
+
+ /**
+ * 举手状态一致,快速点击导致两次举手状态一致导致举手失败
+ */
+ public static final int TSDK_MEETING_HAND_UP_FIT_CODE = 67109018;
+
+ /**
+ * 会议已有主持人
+ */
+ public static final int TSDK_MEETING_EXIST_CHAIN_MAN_CODE = 67109022;
+
+ /**
+ * 会议无主持人
+ */
+ public static final int TSDK_MEETING_NOT_CHAIN_CODE = 67109039;
+
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/UserInfoBeanV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/UserInfoBeanV2.java
new file mode 100644
index 0000000..beaf727
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/beans/UserInfoBeanV2.java
@@ -0,0 +1,33 @@
+package com.tengshisoft.chatmodule.beans;
+
+/**
+ * @author : isoftstone
+ * @date : 2021/6/17
+ * @Description :存储用户信息
+ */
+public class UserInfoBeanV2 {
+ /**
+ * 帐号
+ */
+ private String account;
+ /**
+ * 终端号
+ */
+ private String number;
+
+ public String getAccount() {
+ return account;
+ }
+
+ public void setAccount(String account) {
+ this.account = account;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CallOnResponse.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CallOnResponse.kt
new file mode 100644
index 0000000..ea778c4
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CallOnResponse.kt
@@ -0,0 +1,14 @@
+package com.tengshisoft.chatmodule.hwclud.api
+
+import com.huawei.ecterminalsdk.models.call.TsdkCall
+
+/**
+ * @Author WangBO
+ * @create 2020/12/15 12:55
+ */
+interface CallOnResponse {
+ fun onEvtCallStartResult(call: TsdkCall)
+ fun onEvtCallEnded(call: TsdkCall)
+ fun onEvtCallConnected(call:TsdkCall)
+ fun onFailed(resultCode:Int,msg:String)
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CancelConfResultCallBack.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CancelConfResultCallBack.java
new file mode 100644
index 0000000..546da2f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CancelConfResultCallBack.java
@@ -0,0 +1,11 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/21 10:46
+ * 取消会议回调
+ */
+public interface CancelConfResultCallBack {
+ void success();
+ void fail(int result, String msg);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CheckVersionCallBack.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CheckVersionCallBack.java
new file mode 100644
index 0000000..47268f1
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/CheckVersionCallBack.java
@@ -0,0 +1,11 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/21 10:46
+ * 升级检测
+ */
+public interface CheckVersionCallBack {
+ void success(String version,String url);
+ void fail(int result, String msg);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IAgree.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IAgree.java
new file mode 100644
index 0000000..5c9be57
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IAgree.java
@@ -0,0 +1,11 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * @author Yason
+ *
+ * 用于申请权限成功后的回调
+ * */
+public interface IAgree {
+
+ void agree();
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/ILdapFrontstageNotification.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/ILdapFrontstageNotification.java
new file mode 100644
index 0000000..faa3ffd
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/ILdapFrontstageNotification.java
@@ -0,0 +1,21 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+
+import com.tengshisoft.chatmodule.beans.LdapFrontstageConstant;
+
+/**
+ * This interface is about ldap frontstage service event notify.
+ * Ldap地址本业务事件通知接口
+ */
+public interface ILdapFrontstageNotification {
+
+ /**
+ * This is a callback function to handle the getting user's icon result.
+ * 处理获取地址本联系人查询返回结果的回调
+ * @param event Indicates event
+ * 获取联系人的结果事件
+ * @param object Indicates contact info
+ * 获取到的具体信息
+ */
+ void onEntLdapFrontStageNotify(LdapFrontstageConstant.Event event, Object object, String tag);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IPermission.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IPermission.java
new file mode 100644
index 0000000..80c03a4
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IPermission.java
@@ -0,0 +1,37 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * @author Yason
+ *
+ * 权限申请api
+ */
+public interface IPermission {
+
+ /**
+ * 重新申请权限
+ * 一般用于第一次申请权限后点了拒绝再次询问时使用
+ */
+ boolean reCheck();
+
+ /**
+ * 申请权限
+ * 只需要知道成功结果,不需要知道拒绝结果时使用
+ */
+ boolean checkPermission(Activity context, @Nullable IAgree iAgree);
+
+ /**
+ * 申请权限
+ * 要知道成功结果,也要知道失败结果时使用
+ */
+ boolean checkPermission(Activity context, @Nullable IAgree iAgree, @Nullable IRefuse iRefuse);
+
+ /**
+ * 申请权限后结果回调
+ */
+ void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IPermissionFactory.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IPermissionFactory.java
new file mode 100644
index 0000000..362d488
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IPermissionFactory.java
@@ -0,0 +1,19 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * @author Yason
+ *
+ * 申请权限工厂接口
+ * */
+public interface IPermissionFactory {
+
+ /**
+ * 创建单个权限申请接口工厂
+ * */
+ IPermission permission();
+
+ /**
+ * 创建多个权限申请接口工厂
+ * */
+ IPermission multiPermission(Class>... factories);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IRefuse.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IRefuse.java
new file mode 100644
index 0000000..98ee886
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/IRefuse.java
@@ -0,0 +1,13 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+import java.util.List;
+
+/**
+ * @author Yason
+ *
+ * 用于申请权限失败后的回调
+ * */
+public interface IRefuse {
+
+ void refuse(List refusePermissions, boolean notAgainAsk);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LogUpload.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LogUpload.java
new file mode 100644
index 0000000..de5280e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LogUpload.java
@@ -0,0 +1,10 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * Date: 2020/8/25 15:01
+ * 通过id匹配通讯录姓名
+ */
+public interface LogUpload {
+ void success(int result);
+ void fail(int result, String msg);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LoginView.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LoginView.java
new file mode 100644
index 0000000..2fb869c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LoginView.java
@@ -0,0 +1,31 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+
+/**
+ * 作者: adam
+ * 日期: 2021/12/2 - 3:33 下午
+ * 邮箱: itgaojian@163.com
+ * 描述:
+ */
+public interface LoginView {
+ void loginIng();
+
+ void loginFail();
+
+ void loginSuccess();
+
+ void onEvtCallStartResult(TsdkCall call);
+
+ void onEvtCallEnded(TsdkCall call);
+
+ void onEvtCallConnected(TsdkCall call);
+
+ void onFailed(int result, String msg);
+
+ void onNumIsEmpty();
+
+ void onNetError();
+
+ void onStartCall();
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LogoutCallBack.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LogoutCallBack.java
new file mode 100644
index 0000000..dc690c4
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/LogoutCallBack.java
@@ -0,0 +1,11 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/21 10:46
+ * 推出登录
+ */
+public interface LogoutCallBack {
+ void success();
+ void fail(int result, String msg);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/SearchTimeZoneListCallBack.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/SearchTimeZoneListCallBack.java
new file mode 100644
index 0000000..8fdaabc
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/SearchTimeZoneListCallBack.java
@@ -0,0 +1,11 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/21 10:46
+ * 时区列表回调
+ */
+public interface SearchTimeZoneListCallBack {
+ void success();
+ void fail(int result, String msg);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/SearchUsernameFormID.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/SearchUsernameFormID.java
new file mode 100644
index 0000000..8483b23
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/api/SearchUsernameFormID.java
@@ -0,0 +1,12 @@
+package com.tengshisoft.chatmodule.hwclud.api;
+
+import com.huawei.ecterminalsdk.base.TsdkOnEvtSearchLdapContactsResult;
+
+/**
+ * Date: 2020/8/25 15:01
+ * 通过id匹配通讯录姓名
+ */
+public interface SearchUsernameFormID {
+ void success(TsdkOnEvtSearchLdapContactsResult.Param param);
+ void fail(int result, String msg);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingController.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingController.java
new file mode 100644
index 0000000..4679ba2
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingController.java
@@ -0,0 +1,16 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 视频会议页面管理框架,用于处理会控的UI控件,接口隔离业务 会控骨架
+ */
+public interface IMeetingController {
+
+ /**
+ * 设置UI控件主题
+ *
+ * @param theme 主题
+ */
+ void theme(int theme);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingSignalController.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingSignalController.java
new file mode 100644
index 0000000..0691895
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingSignalController.java
@@ -0,0 +1,73 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+import android.content.Context;
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 会议信号信息管理器接口
+ */
+public interface IMeetingSignalController extends IMeetingController {
+ /**
+ * 初始化控件
+ *
+ * @param context
+ */
+ void initView(Context context);
+
+ /**
+ * 视频会议显示流信息对话框
+ *
+ * @param isAudio 是否是音频会议
+ * @param isAux 是否有辅流
+ */
+ void showDialog(boolean isAudio, boolean isAux);
+
+ /**
+ * 屏幕旋转变化
+ *
+ * @param isAudio 是否音频
+ * @param isAux 是否有辅流
+ */
+ void onScreenRotation(boolean isAudio, boolean isAux);
+
+ /**
+ * 关闭对话框
+ */
+ void dismissDialog();
+
+ /**
+ * 刷新辅流信息
+ */
+ void refreshAux();
+
+ /**
+ * 监听辅流变化
+ *
+ * @param auxChangeListener 辅流信息变化监听器
+ */
+ void setAuxChangeListener(IAuxChangeListener auxChangeListener);
+
+ interface IAuxChangeListener {
+ /**
+ * 是否有辅流
+ *
+ * @return
+ */
+ boolean isAux();
+
+ /**
+ * 是否音频
+ *
+ * @return
+ */
+ boolean isAudio();
+
+ /**
+ * 是否svc会议
+ *
+ * @return
+ */
+ boolean isSvc();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingTitleController.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingTitleController.java
new file mode 100644
index 0000000..3ba857e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/IMeetingTitleController.java
@@ -0,0 +1,203 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+import android.view.ViewGroup;
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 会议标题栏接口类
+ */
+public interface IMeetingTitleController extends IMeetingController {
+
+ /**
+ * 将自己增加到会议页面
+ *
+ * @param parent
+ */
+ void inflaterRoot(ViewGroup parent);
+
+ /**
+ * 设置标题栏是否显示
+ *
+ * @param visibility 是否显示
+ */
+ void setTitleVisibility(int visibility);
+
+ /**
+ * 设置标题栏中心信息是否显示
+ *
+ * @param visibility
+ */
+ void setTitleInfoVisibility(int visibility);
+
+ /**
+ * 获取标题栏是否显示
+ *
+ * @return
+ */
+ int getTitleVisibility();
+
+ /**
+ * 隐藏最小化按钮
+ */
+ void hideMinBtn();
+
+ /**
+ * 设置标题栏右边退出显示文字
+ *
+ * @param text 题栏右边显示文字
+ */
+ void setExitText(String text);
+
+ /**
+ * 设置标题栏会议主题
+ *
+ * @param name 会议主题
+ */
+ void displayMeetingName(String name);
+
+ /**
+ * 获取会议主题
+ *
+ * @return 会议主题
+ */
+ String getMeetingName();
+
+ /**
+ * 设置标题栏会议人数
+ *
+ * @param count 会议人数
+ */
+ void displayMeetingCount(String count);
+
+ /**
+ * 设置会议id
+ *
+ * @param id 会议id
+ */
+ void displayMeetingId(String id);
+
+ /**
+ * 获取会议主题
+ *
+ * @return 获取会议id
+ */
+ String getMeetingId();
+
+ /**
+ * 更新时间
+ */
+ void updateTime();
+
+ /**
+ * 设置开始时间
+ */
+ void startTimer();
+
+ /**
+ * 设置计时时间
+ *
+ * @param value 时间
+ */
+ void setTimer(String value);
+
+ /**
+ * 获取初始时间
+ *
+ * @return 初始时间
+ */
+ long getOldTime();
+
+ /**
+ * 显示最小化按钮
+ */
+ void showMinBtn();
+
+ /**
+ * 设置初始时间
+ *
+ * @param oldTime 初始时间
+ */
+ void setOldTime(long oldTime);
+
+ /**
+ * 获取会议秒小时
+ *
+ * @return 返回小时
+ */
+ String getHours();
+
+ /**
+ * 获取会议秒分钟
+ *
+ * @return 返回分钟
+ */
+ String getMinutes();
+
+ /**
+ * 获取会议秒钟
+ *
+ * @return 返回秒钟
+ */
+ String getSeconds();
+
+ /**
+ * 销毁周期
+ */
+ void onDestroy();
+
+ /**
+ * 更新网络质量
+ *
+ * @param netLevel 网络质量等级
+ * @param isInit
+ */
+ void updateNetworkQuality(int netLevel, boolean isInit);
+
+ /**
+ * 事件传递出去,不和其他业务关联
+ *
+ * @param titleClickListener 标题点击时间监听器
+ */
+ void setTitleClickListener(TitleClickListener titleClickListener);
+
+ /**
+ * 标题栏是否可以编辑
+ *
+ * @param isEnabled 是否可以编辑
+ */
+ void setEnabledMinBtn(boolean isEnabled);
+
+ /**
+ * 隐藏安全标题图标
+ */
+ void hideEncryptedImage();
+
+ interface TitleClickListener {
+ /**
+ * 点击标题
+ */
+ void titleInfoClick();
+
+ /**
+ * 最小化
+ */
+
+ void minimizeClick();
+
+ /**
+ * 退出会议
+ */
+ void exit();
+
+ /**
+ * 会议时间计时
+ *
+ * @param hour 计时小时
+ * @param minute 计时分钟
+ * @param second 计时秒钟
+ */
+ void timerCall(int hour, int minute, int second);
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingController.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingController.java
new file mode 100644
index 0000000..d9faca4
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingController.java
@@ -0,0 +1,745 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+import android.content.Intent;
+
+import com.huawei.ecterminalsdk.base.TsdkLableSsrcData;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus;
+import com.tengshisoft.chatmodule.hwclud.ui.IMeetingBottomChanged;
+import com.tengshisoft.chatmodule.hwclud.utils.BandWidthUtils;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 会议相关控制管理类
+ */
+public class MeetingController {
+ private static final String TAG = MeetingController.class.getSimpleName();
+ // 会议主题最大长度64位
+ public static final int MEETING_TITLE_MAX_LENGTH = 64;
+ public boolean needUpdateMuteState = false;
+ // 是否VMR会议
+ public boolean isVMR = false;
+ // 视频会议是否结束
+ public boolean finishSp = false;
+ // 是否会议中
+ public boolean isMeetingDuration = false;
+ private int callId;
+ private boolean hasExecutedRefreshView;
+ private SvcWatchStatus svcWatchStatus;
+ private Member voiceWatchMember;
+ private boolean isSvcH265;
+ // 会议是否最小化
+ private boolean isMinimize;
+ // 是否是主席
+ private boolean isChairman;
+ // 会议是否有主席
+ private boolean hasChairman;
+ // 视频入会成员数量
+ public int videoJoinMeeting = 0;
+ // 是否有与会者
+ public boolean isHasMember = false;
+ // 是否是会议
+ public boolean isConf = false;
+ // onEvtBaseInfo是否已经执行过
+ public boolean isOnBaseInfoDo = false;
+ // 加入会议时是否已经申请过主席
+ public boolean hasReqChairMan = false;
+ private String confId = "";
+ private String startTime = "";
+ private String endTime = "";
+ private String chairmanPwd = "";
+ private String chairman3Pwd = "";
+ private String guestPwd = "";
+ private String guestLin = "";
+ private String terminal = "";
+ private String scheduleUserName = "";
+ // 是否显示申请主席的对话框
+ public boolean isShowDialog = false;
+ // 应用到后台切没有相关权限时保存被呼信息
+ private CallInfo tempCallInfo;
+ private boolean isCallComing = false;
+ private boolean isCallOutGoing = false;
+ private int muteState;
+ // 会议总带宽
+ private int bandWidth = 0;
+ private List> iMeetingBottomChangedList;
+ // 发起会议时参数
+ private boolean hasSetting = false;
+ // 是否打开麦克风
+ private boolean isOpenMicHeightSetting = false;
+ // 是否打开摄像头
+ private boolean isOpenCameraHeightSetting = false;
+ // 会议信息
+ private TsdkConference conference;
+ // 是否可以滑动
+ public boolean isCanScroll = true;
+ // 会议中摄像头是否打开
+ public boolean isVideoOpen = false;
+ // 悬浮窗是否打开
+ public boolean isFloatWindowOpen = false;
+ // 会议中麦克风是否打开 false 打开
+ public boolean isVoiceOpen = false;
+ // 单向直播会议中是否申请发言
+ public boolean isAuditDirSpeak = false;
+ private boolean isAux;
+ // 是否有选看
+ private boolean isWatchMember = false;
+ // 是否有广播
+ private boolean isBroadMember = false;
+ // 广播的成员
+ private Member broadIngMember = null;
+ // 是否在会议界面
+ public boolean isInMeetingPage = true;
+ // 会议延长时间
+ public int duration = 0;
+ // auxService 对象
+ private Intent auxServiceIntent;
+ // 3.0是否同步过本地麦克风状态
+ public boolean hasSyncVoiceState = false;
+ // 是否显示请求音频转视频界面
+ private boolean isShowRequestAddVideo = false;
+ private TsdkCall call;
+ // 是否是视频
+ public boolean isVideo = false;
+ private Member watchingMember;
+ private int tempFloat = 0;
+ public long meetingStartTime = 0;
+ // 是否后置摄像头 true 为后置 false 为前置
+ public boolean cameraIndex = false;
+ // 语音界面
+ public boolean isVoicePage = false;
+ // 是否收到会控结果回调
+ public boolean conferenceRight = false;
+ // 是否有网络
+ public boolean haveNet = true;
+ // 是否入会成功
+ public boolean joinConfSuccess = false;
+ private boolean isSponsorConf = false;
+ // 是否登录
+ private boolean isLogin = false;
+ // 是否SVC会背景
+ private boolean isSvcBigConf = false;
+ public boolean svcBigConfMiniFlag = false;
+ public boolean svcBigConfPipFlag = false;
+ public boolean svcBigConfListFlag = false;
+ private boolean isConfSubtitleEnable;
+
+ /**
+ * 已开启会议字幕
+ */
+ private boolean isSubtitleEnable;
+ // 会议是否锁定状态
+ private boolean meetingIsLock = false;
+
+ // 是否静默解锁(释放主持人后会议解锁)
+ private boolean isSilentUnlock = false;
+ // 是否是通话
+ private boolean isConversation = false;
+
+ // 是否首次打开辅流页面、
+ private boolean isShowAuxZoomTips = true;
+
+ // 会议id有无变化
+ private int cacheCallId = 0;
+
+ // 被动选看
+ private Member isPassiveWatchingMember = null;
+
+ // 会议类型是否有修改
+ private boolean isMeetingSessionModified = false;
+
+ // 是否SC倒换
+ private boolean isScChange = false;
+
+ public void setScChange(boolean scChange) {
+ isScChange = scChange;
+ }
+
+ public boolean isScChange() {
+ return isScChange;
+ }
+
+ private ArrayList svcLableSsrc;
+
+ public ArrayList getSvcLableSsrc() {
+ return svcLableSsrc;
+ }
+
+ public void setSvcLableSsrc(ArrayList svcLableSsrc) {
+ this.svcLableSsrc = svcLableSsrc;
+ }
+
+ public boolean isMeetingSessionModified() {
+ return isMeetingSessionModified;
+ }
+
+ public void setMeetingSessionModified(boolean meetingSessionModified) {
+ isMeetingSessionModified = meetingSessionModified;
+ }
+
+ public Member getIsPassiveWatchingMember() {
+ return isPassiveWatchingMember;
+ }
+
+ public void setIsPassiveWatchingMember(Member isPassiveWatchingMember) {
+ this.isPassiveWatchingMember = isPassiveWatchingMember;
+ }
+
+ public int getCacheCallId() {
+ return cacheCallId;
+ }
+
+ public void setCacheCallId(int cacheCallId) {
+ this.cacheCallId = cacheCallId;
+ }
+
+ /**
+ * 是否真正入会
+ */
+ private boolean isRealInConf = false;
+
+ public boolean isShowAuxZoomTips() {
+ return isShowAuxZoomTips;
+ }
+
+ public void setShowAuxZoomTips(boolean showAuxZoomTips) {
+ isShowAuxZoomTips = showAuxZoomTips;
+ }
+
+ private MeetingController() {
+ }
+
+ public static class ControllerHolder {
+ private static MeetingController instance = null;
+ }
+
+ public static MeetingController getInstance() {
+ if (ControllerHolder.instance == null) {
+ ControllerHolder.instance = new MeetingController();
+ }
+ return ControllerHolder.instance;
+ }
+
+ public boolean isShowRequestAddVideo() {
+ return isShowRequestAddVideo;
+ }
+
+ public void setShowRequestAddVideo(boolean showRequestAddVideo) {
+ LogUtils.e("setShowRequestAddVideo", "showRequestAddVideo =" + showRequestAddVideo);
+ isShowRequestAddVideo = showRequestAddVideo;
+ }
+
+ public void reset() {
+ needUpdateMuteState = false;
+ isVMR = false;
+ isMinimize = false;
+ isChairman = false;
+ isVideoOpen = false;
+ isVoiceOpen = false;
+ isWatchMember = false;
+ isAuditDirSpeak = false;
+ isBroadMember = false;
+ isInMeetingPage = true;
+ hasChairman = false;
+ videoJoinMeeting = 0;
+ isOnBaseInfoDo = false;
+ tempFloat = 0;
+ isHasMember = false;
+ isConf = false;
+ isFloatWindowOpen = false;
+ cameraIndex = false;
+ chairman3Pwd = "";
+ hasReqChairMan = false;
+ hasSyncVoiceState = false;
+ duration = 0;
+ broadIngMember = null;
+ meetingStartTime = 0;
+ isShowDialog = false;
+ conferenceRight = false;
+ auxServiceIntent = null;
+ confId = "";
+ startTime = "";
+ endTime = "";
+ chairmanPwd = "";
+ guestPwd = "";
+ terminal = "";
+ scheduleUserName = "";
+ joinConfSuccess = false;
+ isShowRequestAddVideo = false;
+ if (iMeetingBottomChangedList != null) {
+ iMeetingBottomChangedList.clear();
+ }
+ voiceWatchMember = null;
+ meetingIsLock = false;
+ isConfSubtitleEnable = false;
+ isSubtitleEnable = false;
+ isSilentUnlock = false;
+ isInMeetingPage = true;
+ isShowAuxZoomTips = true;
+ isRealInConf = false;
+ cacheCallId = 0;
+ isPassiveWatchingMember = null;
+ isMeetingSessionModified = false;
+ isScChange = false;
+ }
+
+ public boolean isRealInConf() {
+ return isRealInConf;
+ }
+
+ public void setRealInConf(boolean realInConf) {
+ isRealInConf = realInConf;
+ }
+
+ public boolean isConversation() {
+ return isConversation;
+ }
+
+ public void setConversation(boolean conversation) {
+ isConversation = conversation;
+ }
+
+ public boolean isSilentUnlock() {
+ return isSilentUnlock;
+ }
+
+ public void setSilentUnlock(boolean silentUnlock) {
+ isSilentUnlock = silentUnlock;
+ }
+
+ public boolean isMeetingIsLock() {
+ return meetingIsLock;
+ }
+
+ public void setMeetingIsLock(boolean meetingIsLock) {
+ this.meetingIsLock = meetingIsLock;
+ }
+
+ public boolean isConfSubtitleEnable() {
+ return isConfSubtitleEnable;
+ }
+
+ public void setIsConfSubtitleEnable(boolean mIsConfSubtitleEnable) {
+ this.isConfSubtitleEnable = mIsConfSubtitleEnable;
+ }
+
+ public TsdkConference getConference() {
+ return conference;
+ }
+
+ public void setConference(TsdkConference conference) {
+ this.conference = conference;
+ }
+
+ public boolean isSubtitleEnable() {
+ return isSubtitleEnable;
+ }
+
+ public void setIsSubtitleEnable(boolean mIsSubtitleEnable) {
+ this.isSubtitleEnable = mIsSubtitleEnable;
+ }
+
+ public boolean isLogin() {
+ return isLogin;
+ }
+
+ public void setLogin(boolean login) {
+ isLogin = login;
+ }
+
+ public Intent getAuxServiceIntent() {
+ return auxServiceIntent;
+ }
+
+ public void setAuxServiceIntent(Intent auxServiceIntent) {
+ this.auxServiceIntent = auxServiceIntent;
+ }
+
+ public boolean isJoinConfSuccess() {
+ return joinConfSuccess;
+ }
+
+ public void setJoinConfSuccess(boolean joinConfSuccess) {
+ this.joinConfSuccess = joinConfSuccess;
+ }
+
+ public int getCallId() {
+ return callId;
+ }
+
+ public void setCallId(int callId) {
+ this.callId = callId;
+ }
+
+ public boolean getMuteState() {
+ return muteState == 1;
+ }
+
+ public void setMuteState(int muteState) {
+ this.muteState = muteState;
+ }
+
+ public boolean isHasChairman() {
+ return hasChairman;
+ }
+
+ public void setHasChairman(boolean hasChairman) {
+ this.hasChairman = hasChairman;
+ }
+
+ public boolean isAux() {
+ return isAux;
+ }
+
+ public void setAux(boolean aux) {
+ isAux = aux;
+ }
+
+ public boolean isHaveNet() {
+ return haveNet;
+ }
+
+ public boolean isNotHaveNet() {
+ return !haveNet;
+ }
+
+ public void setHaveNet(boolean haveNet) {
+ this.haveNet = haveNet;
+ }
+
+
+ public TsdkCall getCall() {
+ return call;
+ }
+
+ public void setCall(TsdkCall call) {
+ this.call = call;
+ }
+
+ public boolean isCallComing() {
+ return isCallComing;
+ }
+
+ public void setCallComing(boolean callComing) {
+ isCallComing = callComing;
+ }
+
+ public boolean isCallOutGoing() {
+ return isCallOutGoing;
+ }
+
+ public void setCallOutGoing(boolean callOutGoing) {
+ isCallOutGoing = callOutGoing;
+ }
+
+
+ public Member getBroadIngMember() {
+ return broadIngMember;
+ }
+
+ public void setBroadIngMember(Member broadingMember) {
+ this.broadIngMember = broadingMember;
+ }
+
+ public boolean isHasExecutedRefreshView() {
+ return hasExecutedRefreshView;
+ }
+
+ public void setHasExecutedRefreshView(boolean hasExecutedRefreshView) {
+ this.hasExecutedRefreshView = hasExecutedRefreshView;
+ }
+
+ public String getStartTime() {
+ return startTime;
+ }
+
+ public void setStartTime(String startTime) {
+ this.startTime = startTime;
+ }
+
+ public String getEndTime() {
+ return endTime;
+ }
+
+ public void setEndTime(String endTime) {
+ this.endTime = endTime;
+ }
+
+ public String getChairmanPwd() {
+ return chairmanPwd;
+ }
+
+ public void setChairmanPwd(String chairmanPwd) {
+ this.chairmanPwd = chairmanPwd;
+ }
+
+ public String getChairman3Pwd() {
+ return chairman3Pwd;
+ }
+
+ public void setChairman3Pwd(String chairman3Pwd) {
+ this.chairman3Pwd = chairman3Pwd;
+ }
+
+ public String getGuestPwd() {
+ return guestPwd;
+ }
+
+ public void setGuestPwd(String guestPwd) {
+ this.guestPwd = guestPwd;
+ }
+
+ public String getGuestLin() {
+ return guestLin;
+ }
+
+ public void setGuestLin(String guestLin) {
+ this.guestLin = guestLin;
+ }
+
+ public String getTerminal() {
+ return terminal;
+ }
+
+ public void setTerminal(String terminal) {
+ this.terminal = terminal;
+ }
+
+ public String getScheduleUserName() {
+ return scheduleUserName;
+ }
+
+ public void setScheduleUserName(String scheduleUserName) {
+ this.scheduleUserName = scheduleUserName;
+ }
+
+ public boolean isSponsorConf() {
+ return isSponsorConf;
+ }
+
+ public void setSponsorConf(boolean sponsorConf) {
+ isSponsorConf = sponsorConf;
+ }
+
+ public String getConfId() {
+ return confId;
+ }
+
+ public void setConfId(String confId) {
+ this.confId = confId;
+ }
+
+ public SvcWatchStatus getSvcWatchStatus() {
+ return svcWatchStatus;
+ }
+
+ public void setSvcWatchStatus(SvcWatchStatus svcWatchStatus) {
+ this.svcWatchStatus = svcWatchStatus;
+ }
+
+ public Member getVoiceWatchMember() {
+ return voiceWatchMember;
+ }
+
+ public void setVoiceWatchMember(Member voiceWatchMember) {
+ this.voiceWatchMember = voiceWatchMember;
+ }
+
+ public int getTempFloat() {
+ return tempFloat;
+ }
+
+ public void setTempFloat(int tempFloat) {
+ this.tempFloat = tempFloat;
+ }
+
+ public boolean isBroadMember() {
+ return isBroadMember;
+ }
+
+ public void setBroadMember(boolean broadMember) {
+ isBroadMember = broadMember;
+ }
+
+ public boolean isWatchMember() {
+ return isWatchMember;
+ }
+
+ public void setWatchMember(boolean watchMember) {
+ isWatchMember = watchMember;
+ }
+
+ public Member getWatchingMember() {
+ return watchingMember;
+ }
+
+ public void setWatchingMember(Member watchingMember) {
+ this.watchingMember = watchingMember;
+ }
+
+ public boolean isMinimize() {
+ return isMinimize;
+ }
+
+ public void setMinimize(boolean minimize) {
+ isMinimize = minimize;
+ }
+
+ public boolean isChairman() {
+ return isChairman;
+ }
+
+ public void setChairman(boolean chairman) {
+ LogUtils.d("setChairman chairman = " + chairman);
+ isChairman = chairman;
+ }
+
+ public CallInfo getTempCallInfo() {
+ return tempCallInfo;
+ }
+
+ public void setTempCallInfo(CallInfo tempCallInfo) {
+ this.tempCallInfo = tempCallInfo;
+ }
+
+ public void setHasSetting(boolean hasSetting) {
+ this.hasSetting = hasSetting;
+ }
+
+ public boolean isSvcBigConf() {
+ return isSvcBigConf;
+ }
+
+ public int getSSrc(MyTsdkCallInfo callInfo) {
+ LogUtils.d("getSSrc svcBigConfPipFlag == " + svcBigConfPipFlag);
+ int sSrc;
+ if (isSvcBigConf && !svcBigConfPipFlag) {
+ sSrc = callInfo.getSsrcTableEnd() - 1;
+ } else {
+ sSrc = callInfo.getSsrcTableStart() + 1;
+ }
+ svcBigConfPipFlag = !svcBigConfPipFlag;
+ return sSrc;
+ }
+
+ public int getMiniSSrc(MyTsdkCallInfo callInfo) {
+ LogUtils.d("getMiniSSrc() svcBigConfMiniFlag == " + svcBigConfMiniFlag);
+ int sSrc;
+ if (isSvcBigConf && !svcBigConfMiniFlag) {
+ sSrc = callInfo.getSsrcTableEnd() - 1;
+ } else {
+ sSrc = callInfo.getSsrcTableStart() + 1;
+ }
+ svcBigConfMiniFlag = !svcBigConfMiniFlag;
+ return sSrc;
+ }
+
+ public void setSvcBigConf(boolean svcBigConf) {
+ isSvcBigConf = svcBigConf;
+ }
+
+ public boolean isOpenMicHeightSetting() {
+ if (hasSetting) {
+ return isOpenMicHeightSetting;
+ } else {
+ return isVoiceOpen;
+ }
+ }
+
+ public void setOpenMicHeightSetting(boolean openMic) {
+ isOpenMicHeightSetting = openMic;
+ }
+
+ public boolean isOpenCameraHeightSetting() {
+ if (hasSetting) {
+ return isOpenCameraHeightSetting;
+ } else {
+ return isVideoOpen;
+ }
+ }
+
+ public void setOpenCameraHeightSetting(boolean openCamera) {
+ isOpenCameraHeightSetting = openCamera;
+ }
+
+ public int getBandWidth() {
+ return bandWidth;
+ }
+
+ public void setBandWidth(int bandWidth, boolean isSvcH265) {
+ this.bandWidth = bandWidth;
+ this.isSvcH265 = isSvcH265;
+ }
+
+ public void setBandWidth(int bandWidth) {
+ if (bandWidth == 0) {
+ return;
+ }
+ this.bandWidth = bandWidth;
+ }
+
+ /**
+ * 根据带宽计算每一个video的宽度
+ *
+ * @param videoCount 视频个数
+ * @return 视频分辨率
+ */
+ public int[] getVideoSize(int videoCount) {
+ SvcWatchStatus svcWatchStatus = getSvcWatchStatus();
+ if (svcWatchStatus == SvcWatchStatus.AUX_DATA) {
+ return BandWidthUtils.geAuxDataVideoSize(bandWidth, isSvcH265);
+ } else if (svcWatchStatus == SvcWatchStatus.MINI_WATCH) {
+ return BandWidthUtils.getMiniVideoSize(bandWidth);
+ } else if (svcWatchStatus == SvcWatchStatus.GALLERY_WATCH) {
+ return BandWidthUtils.getGalleryVideoSize(videoCount, bandWidth, isSvcH265);
+ } else {
+ return BandWidthUtils.getPipVideoSize(videoCount, bandWidth);
+ }
+ }
+
+
+ // ------------------------- 会控底部导航栏显示影藏监听 start ----------------------
+
+ public void registerBottomChanged(IMeetingBottomChanged iMeetingBottomChanged) {
+ if (iMeetingBottomChangedList == null) {
+ iMeetingBottomChangedList = new ArrayList<>();
+ }
+ WeakReference reference = new WeakReference<>(iMeetingBottomChanged);
+ iMeetingBottomChangedList.add(reference);
+ }
+
+ public void unRegisterBottomChanged(IMeetingBottomChanged iMeetingBottomChanged) {
+ Iterator> iterator = iMeetingBottomChangedList.iterator();
+ while (iterator.hasNext()) {
+ WeakReference reference = iterator.next();
+ if (reference != null && reference.get() != null && reference.get() == iMeetingBottomChanged) {
+ iterator.remove();
+ }
+ }
+ }
+
+ public void meetingBottomChanged(int visibility) {
+ if (iMeetingBottomChangedList == null) {
+ return;
+ }
+ for (WeakReference reference : iMeetingBottomChangedList) {
+ if (reference != null && reference.get() != null) {
+ reference.get().bottomChanged(visibility);
+ }
+ }
+ }
+
+ // ------------------------- 会控底部导航栏显示影藏监听 end ----------------------
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingSignalController.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingSignalController.java
new file mode 100644
index 0000000..e1a0e8e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingSignalController.java
@@ -0,0 +1,240 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.base.TsdkCallStreamInfo;
+import com.huawei.ecterminalsdk.base.TsdkCallVideoStreamInfo;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.ui.StreamInfoDialog;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 会议信号信息管理器
+ */
+public class MeetingSignalController implements IMeetingSignalController {
+ private static final String TAG = MeetingSignalController.class.getSimpleName();
+ /**
+ * 循环刷新辅流信息
+ */
+ public static final int REFRESH_AUX_LOOP_WHAT = 0x0010;
+ private WeakReference contextWeakReference;
+ private StreamInfoDialog dialog;
+ private Handler loopHandler;
+ private IAuxChangeListener auxChangeListener;
+ private boolean lastAudioState;
+
+ @Override
+ public void initView(Context context) {
+ contextWeakReference = new WeakReference<>(context);
+ loopHandler = new LoopHandler(this);
+ }
+
+ @Override
+ public void showDialog(boolean isAudio, boolean isAux) {
+ if (contextWeakReference == null || contextWeakReference.get() == null) {
+ return;
+ }
+ dismissDialog();
+ dialog = new StreamInfoDialog(contextWeakReference.get(), isAudio, isAux);
+ dialog.setNo(() -> {
+ dialog.dismiss();
+ loopHandler.removeCallbacksAndMessages(null);
+ });
+ lastAudioState = isAudio;
+ dialog.show();
+ if (loopHandler.hasMessages(REFRESH_AUX_LOOP_WHAT)) {
+ return;
+ }
+ loopHandler.sendEmptyMessage(REFRESH_AUX_LOOP_WHAT);
+ }
+
+ @Override
+ public void onScreenRotation(boolean isAudio, boolean isAux) {
+ if (contextWeakReference == null || contextWeakReference.get() == null) {
+ return;
+ }
+ if (dialog == null || !dialog.isShowing()) {
+ return;
+ }
+ showDialog(isAudio, isAux);
+ }
+
+ @Override
+ public void dismissDialog() {
+ if (dialog == null || !dialog.isShowing()) {
+ return;
+ }
+ dialog.dismiss();
+ loopHandler.removeMessages(REFRESH_AUX_LOOP_WHAT);
+ }
+
+ @Override
+ public void refreshAux() {
+ if (dialog == null || !dialog.isShowing()) {
+ return;
+ }
+ boolean isAudio = auxChangeListener != null && auxChangeListener.isAudio();
+ if (isAudio != lastAudioState) {
+ dismissDialog();
+ }
+ boolean isAux = auxChangeListener != null && auxChangeListener.isAux();
+ boolean isSvcMeeting = auxChangeListener != null && auxChangeListener.isSvc();
+ TsdkCallStreamInfo qosInfo = getQosInfo();
+ dialog.refresh(qosToMap(qosInfo, isSvcMeeting), isAux);
+ }
+
+ @Override
+ public void setAuxChangeListener(IAuxChangeListener auxChangeListener) {
+ this.auxChangeListener = auxChangeListener;
+ }
+
+ /**
+ * 设置页面上码流数据展示
+ *
+ * @return map
+ */
+ private Map qosToMap(TsdkCallStreamInfo mQosInfo, boolean isSvc) {
+ Map map = new HashMap<>(25);
+ if (null == mQosInfo) {
+ return map;
+ }
+ //----------------------音频质量开始------------------------
+ if (!TextUtils.isEmpty(mQosInfo.getAudioStreamInfo().getEncodeProtocol().trim())) {
+ map.put("text_0_1", mQosInfo.getAudioStreamInfo().getEncodeProtocol() + "");
+ map.put("text_1_1", mQosInfo.getAudioStreamInfo().getSendBitRate() + "");
+ map.put("text_2_1", mQosInfo.getAudioStreamInfo().getSendLossFraction() + "");
+ map.put("text_3_1", mQosInfo.getAudioStreamInfo().getSendDelay() + "");
+ map.put("text_4_1", mQosInfo.getAudioStreamInfo().getSendJitter() + "");
+ }
+
+ if (!TextUtils.isEmpty(mQosInfo.getAudioStreamInfo().getDecodeProtocol().trim())) {
+ map.put("text_0_2", mQosInfo.getAudioStreamInfo().getDecodeProtocol() + "");
+ map.put("text_1_2", mQosInfo.getAudioStreamInfo().getRecvBitRate() + "");
+ map.put("text_2_2", mQosInfo.getAudioStreamInfo().getRecvLossFraction() + "");
+ map.put("text_3_2", mQosInfo.getAudioStreamInfo().getRecvDelay() + "");
+ map.put("text_4_2", mQosInfo.getAudioStreamInfo().getRecvJitter() + "");
+ }
+ //----------------------音频质量结束------------------------
+ //----------------------视频质量开始------------------------
+ if (isSvc) {
+ List svcStreamInfoList = new ArrayList<>();
+ // SVC 本地发送
+ for (TsdkCallVideoStreamInfo tsdkCallVideoStreamInfo : mQosInfo.getSvcVideoStreamInfo()) {
+ String encoderSize = tsdkCallVideoStreamInfo.getEncoderSize();
+ if (!TextUtils.isEmpty(encoderSize)) {
+ svcStreamInfoList.add(tsdkCallVideoStreamInfo);
+ break;
+ }
+ }
+ if (svcStreamInfoList.size() == 0) {
+ svcStreamInfoList.add(new TsdkCallVideoStreamInfo());
+ }
+ // SVC 本地接收
+ for (TsdkCallVideoStreamInfo tsdkCallVideoStreamInfo : mQosInfo.getSvcVideoStreamInfo()) {
+ String decoderSize = tsdkCallVideoStreamInfo.getDecoderSize();
+ if (!TextUtils.isEmpty(decoderSize)) {
+ LogUtil.zzz(TAG,"qosToMap()",
+ "decoderSize == " + decoderSize + " decodeName == " + tsdkCallVideoStreamInfo.getDecodeName());
+ svcStreamInfoList.add(tsdkCallVideoStreamInfo);
+ }
+ }
+
+ map.put("text_0_1_2", svcStreamInfoList);
+ } else {
+ List streamInfoList = new ArrayList<>();
+ // 发送视频质量
+ if (!TextUtils.isEmpty(mQosInfo.getVideoStreamInfo().getEncoderSize())) {
+ streamInfoList.add(mQosInfo.getVideoStreamInfo());
+ }
+ // 接收视频质量
+ if (!TextUtils.isEmpty(mQosInfo.getVideoStreamInfo().getDecoderSize())) {
+ streamInfoList.add(mQosInfo.getVideoStreamInfo());
+ }
+ map.put("text_0_1_2", streamInfoList);
+ }
+ //----------------------视频质量结束------------------------
+ // 发送辅流
+ if (!TextUtils.isEmpty(mQosInfo.getDataStreamInfo().getEncodeName().trim())) {
+ map.put("text_0_1_2_0", mQosInfo.getDataStreamInfo().getEncodeName() + "");
+ map.put("text_5_1_0", mQosInfo.getDataStreamInfo().getSendBitRate() + "");
+ map.put("text_6_1_0", mQosInfo.getDataStreamInfo().getEncoderSize());
+ map.put("text_7_1_0", mQosInfo.getDataStreamInfo().getSendLossFraction() + "");
+ map.put("text_8_1_0", mQosInfo.getDataStreamInfo().getSendDelay() + "");
+ map.put("text_9_1_0", mQosInfo.getDataStreamInfo().getSendJitter() + "");
+ map.put("text_10_1_0", mQosInfo.getDataStreamInfo().getSendFrameRate() + "");
+ }
+
+ // 接收辅流
+ if (!TextUtils.isEmpty(mQosInfo.getDataStreamInfo().getDecodeName().trim())) {
+ map.put("text_0_2_2_0", mQosInfo.getDataStreamInfo().getDecodeName() + "");
+ map.put("text_5_2_0", mQosInfo.getDataStreamInfo().getRecvBitRate() + "");
+ map.put("text_6_2_0", mQosInfo.getDataStreamInfo().getDecoderSize());
+ map.put("text_7_2_0", mQosInfo.getDataStreamInfo().getRecvLossFraction() + "");
+ map.put("text_8_2_0", mQosInfo.getDataStreamInfo().getRecvDelay() + "");
+ map.put("text_9_2_0", mQosInfo.getDataStreamInfo().getRecvJitter() + "");
+ map.put("text_10_2_0", mQosInfo.getDataStreamInfo().getRecvFrameRate() + "");
+ }
+ return map;
+ }
+
+ private TsdkCallStreamInfo getQosInfo() {
+ int mCallId = CallMgrV2.getInstance().getCallId();
+ if (mCallId == 0) {
+ mCallId = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ if (mCallId == 0) {
+ LogUtil.zzz(TAG, "getQosInfo", "CallId is null");
+ return null;
+ }
+ return CallMgrV2.getInstance().getCallQuality(mCallId);
+ } else {
+ TsdkCallStreamInfo mQosInfo = CallMgrV2.getInstance().getCallQuality(mCallId);
+ if (mQosInfo == null) {
+ mCallId = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ mQosInfo = CallMgrV2.getInstance().getCallQuality(mCallId);
+ if (mQosInfo == null) {
+ LogUtil.zzz(TAG, "getQosInfo", "mQosInfo is null");
+ return null;
+ }
+ }
+ return mQosInfo;
+ }
+ }
+
+ @Override
+ public void theme(int theme) {
+
+ }
+
+ public static class LoopHandler extends Handler {
+
+ private WeakReference reference;
+
+ public LoopHandler(MeetingSignalController meetingSignalController) {
+ reference = new WeakReference<>(meetingSignalController);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ if (reference == null || reference.get() == null) {
+ return;
+ }
+ reference.get().refreshAux();
+ sendEmptyMessageDelayed(REFRESH_AUX_LOOP_WHAT, ConstantsV2.DELAY_MILLIS_1000);
+ }
+ }
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingTitleBarController.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingTitleBarController.java
new file mode 100644
index 0000000..47b1b37
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/MeetingTitleBarController.java
@@ -0,0 +1,349 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.util.AttributeSet;
+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.allen.library.SuperButton;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.utils.DateUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.UiUtils;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+
+import java.util.concurrent.TimeUnit;
+
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.Constraints;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+
+/**
+ * @Time: 2021/6/18
+ * @Author: isoftstone
+ * @Description: 会议标题栏控制实现类
+ */
+public class MeetingTitleBarController extends ConstraintLayout implements IMeetingTitleController,
+ View.OnClickListener {
+
+ private ConstraintLayout clMeetingTitle;
+
+ private SuperButton btnExit;
+
+ private TextView tvMeetingName, tvMeetingCount, tvMeetingId, tvMeetingTimer;
+
+ private TitleClickListener titleClickListener;
+
+ private Disposable subscribe;
+
+ private String hours, minutes, seconds;
+
+ private ImageView minimizeBtn, encryptedImage;
+
+ private ImageView ivMeetingInfo;
+
+ private Context context;
+
+ private long oldTime = 0;
+
+ /**
+ * 当前网络信号强度
+ */
+ public static int mCurrentNetLevel = 5;
+
+ @Override
+ public long getOldTime() {
+ return oldTime;
+ }
+
+ @Override
+ public void setOldTime(long oldTime) {
+ this.oldTime = oldTime;
+ }
+
+ public MeetingTitleBarController(Context context) {
+ super(context);
+ initView(context);
+ }
+
+ public MeetingTitleBarController(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView(context);
+ }
+
+ public MeetingTitleBarController(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView(context);
+ }
+
+ private void initView(Context context) {
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.video_toolbar, this);
+ clMeetingTitle = findViewById(R.id.meeting_title_cl);
+ minimizeBtn = findViewById(R.id.iv);
+ encryptedImage = findViewById(R.id.img_encryptedImage);
+ ivMeetingInfo = findViewById(R.id.tv_meeting_info);
+ tvMeetingName = findViewById(R.id.tv_meeting_name);
+ tvMeetingCount = findViewById(R.id.tv_meeting_count);
+ tvMeetingId = findViewById(R.id.tv_meeting_id);
+ btnExit = findViewById(R.id.btn_exit);
+ tvMeetingTimer = findViewById(R.id.tv_meeting_timer);
+ minimizeBtn.setOnClickListener(this);
+ btnExit.setOnClickListener(this);
+ findViewById(R.id.lin_titleInfo).setOnClickListener(this);
+ }
+
+ @Override
+ public void inflaterRoot(ViewGroup parent) {
+ if (parent instanceof ConstraintLayout) {
+ LayoutParams layoutParams =
+ new Constraints.LayoutParams(Constraints.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layoutParams.startToStart = LayoutParams.PARENT_ID;
+ layoutParams.topToTop = LayoutParams.PARENT_ID;
+ parent.addView(this, layoutParams);
+ } else if (parent instanceof RelativeLayout) {
+ RelativeLayout.LayoutParams layoutParams =
+ new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
+ RelativeLayout.LayoutParams.WRAP_CONTENT);
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
+ parent.addView(this, layoutParams);
+ }
+ }
+
+ @Override
+ public void hideMinBtn() {
+ minimizeBtn.setVisibility(GONE);
+ }
+
+ @Override
+ public void showMinBtn() {
+ minimizeBtn.setVisibility(VISIBLE);
+ }
+
+ @Override
+ public void setEnabledMinBtn(boolean isEnabled) {
+ minimizeBtn.setEnabled(isEnabled);
+ }
+
+ @Override
+ public void hideEncryptedImage() {
+ if (encryptedImage != null) {
+ encryptedImage.setVisibility(GONE);
+ }
+ }
+
+ @Override
+ public void setTitleVisibility(int visibility) {
+ clMeetingTitle.setVisibility(visibility);
+ }
+
+ @Override
+ public void setTitleInfoVisibility(int visibility) {
+ minimizeBtn.setVisibility(visibility);
+ findViewById(R.id.lin_titleInfo).setVisibility(visibility);
+ }
+
+ @Override
+ public int getTitleVisibility() {
+ return clMeetingTitle.getVisibility();
+ }
+
+ @Override
+ public void setExitText(String text) {
+ btnExit.setText(text);
+ }
+
+ @Override
+ public void displayMeetingName(String name) {
+ tvMeetingName.setText(name);
+ }
+
+ @Override
+ public String getMeetingName() {
+ return tvMeetingName == null ? "" : tvMeetingName.getText().toString();
+ }
+
+ @SuppressLint("SetTextI18n")
+ @Override
+ public void displayMeetingCount(String count) {
+ if (!count.isEmpty()) {
+ tvMeetingCount.setText("(" + count + ")");
+ }
+ }
+
+ @Override
+ public void displayMeetingId(String id) {
+ tvMeetingId.setText(id);
+ }
+
+ @Override
+ public String getMeetingId() {
+ if (tvMeetingId == null) {
+ return "";
+ } else {
+ return tvMeetingId.getText().toString();
+ }
+ }
+
+ /**
+ * 立即刷新时间
+ */
+ @Override
+ public void updateTime() {
+ long aLong = 0;
+ if (MeetingController.getInstance().meetingStartTime != 0) {
+ aLong = (System.currentTimeMillis() - MeetingController.getInstance().meetingStartTime) / 1000;
+ }
+ timeIng(aLong);
+ }
+
+ @Override
+ public void startTimer() {
+ if (subscribe == null) {
+ subscribe = Observable.interval(0, 1, TimeUnit.SECONDS)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(aLong -> {
+ if (MeetingController.getInstance().meetingStartTime != 0) {
+ aLong = (System.currentTimeMillis() - MeetingController.getInstance().meetingStartTime) / 1000;
+ }
+ timeIng(aLong);
+ });
+ }
+ }
+
+ public void timeIng(long aLong) {
+ int hour;
+ int minute;
+ int second;
+ if (aLong < 0) {
+ setTimer("00:00:00");
+ if (titleClickListener != null) {
+ titleClickListener.timerCall(0, 0, 0);
+ }
+ } else {
+ minute = (int) (aLong / ConstantsV2.MINUTE_VALUE);
+ if (minute < ConstantsV2.MINUTE_VALUE) {
+ second = (int) (aLong % ConstantsV2.MINUTE_VALUE);
+ setTimer(new StringBuilder().append("00:")
+ .append(DateUtils.unitFormat(minute)).append(":")
+ .append(DateUtils.unitFormat(second))
+ .toString());
+ if (titleClickListener != null) {
+ titleClickListener.timerCall(0, minute, second);
+ }
+ } else {
+ hour = minute / ConstantsV2.MINUTE_VALUE;
+ if (hour > ConstantsV2.MAX_HOUR_VALUE) {
+ setTimer(new StringBuilder().append(ConstantsV2.MAX_HOUR_VALUE).append(":")
+ .append(ConstantsV2.MAX_MINUTE_VALUE).append(":")
+ .append(ConstantsV2.MAX_MINUTE_VALUE)
+ .toString());
+ if (titleClickListener != null) {
+ titleClickListener.timerCall(ConstantsV2.MAX_HOUR_VALUE, ConstantsV2.MAX_MINUTE_VALUE,
+ ConstantsV2.MAX_MINUTE_VALUE);
+ }
+ }
+ minute = minute % ConstantsV2.MINUTE_VALUE;
+ second = (int) (aLong - hour * ConstantsV2.MINUTE_VALUE * ConstantsV2.MINUTE_VALUE -
+ minute * ConstantsV2.MINUTE_VALUE);
+ setTimer(new StringBuilder()
+ .append(DateUtils.unitFormat(hour)).append(":")
+ .append(DateUtils.unitFormat(minute)).append(":")
+ .append(DateUtils.unitFormat(second))
+ .toString());
+ if (titleClickListener != null) {
+ titleClickListener.timerCall(hour, minute, second);
+ }
+ hours = DateUtils.unitFormat(hour);
+ }
+ minutes = DateUtils.unitFormat(minute);
+ seconds = DateUtils.unitFormat(second);
+ }
+ }
+
+ @Override
+ public void setTimer(String value) {
+ tvMeetingTimer.setText(value);
+ }
+
+ @Override
+ public String getHours() {
+ return hours;
+ }
+
+ @Override
+ public String getMinutes() {
+ return minutes;
+ }
+
+ @Override
+ public String getSeconds() {
+ return seconds;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (subscribe != null && !subscribe.isDisposed()) {
+ subscribe.dispose();
+ }
+ }
+
+ @Override
+ public void updateNetworkQuality(int netLevel, boolean isInit) {
+ if (isInit) {
+ UiUtils.setNetLevel(netLevel, ivMeetingInfo);
+ } else {
+ if (mCurrentNetLevel != netLevel) {
+ UiUtils.setNetLevel(netLevel, ivMeetingInfo);
+ }
+ }
+ }
+
+ @Override
+ public void setTitleClickListener(TitleClickListener titleClickListener) {
+ this.titleClickListener = titleClickListener;
+ }
+
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.iv) {
+ if (titleClickListener != null) {
+ titleClickListener.minimizeClick();
+ }
+ } else if (id == R.id.btn_exit) {
+ if (titleClickListener != null) {
+ titleClickListener.exit();
+ }
+ } else if (id == R.id.lin_titleInfo) {
+ if (titleClickListener != null) {
+ titleClickListener.titleInfoClick();
+ }
+ }
+ }
+
+ @Override
+ public void theme(int theme) {
+
+ }
+
+ public static int getCurrentNetLevel() {
+ return mCurrentNetLevel;
+ }
+
+ public static void setCurrentNetLevel(int currentNetLevel) {
+ mCurrentNetLevel = currentNetLevel;
+ }
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/TsdkConferenceLiveData.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/TsdkConferenceLiveData.java
new file mode 100644
index 0000000..0c547b7
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/controller/TsdkConferenceLiveData.java
@@ -0,0 +1,20 @@
+package com.tengshisoft.chatmodule.hwclud.controller;
+
+
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+
+import androidx.lifecycle.MutableLiveData;
+
+public class TsdkConferenceLiveData extends MutableLiveData {
+
+ private TsdkConferenceLiveData() {
+ }
+
+ private static class Holder {
+ public static final TsdkConferenceLiveData INSTANCE = new TsdkConferenceLiveData();
+ }
+
+ public static TsdkConferenceLiveData getInstance() {
+ return Holder.INSTANCE;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/FloatingViewListener.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/FloatingViewListener.java
new file mode 100644
index 0000000..3c91524
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/FloatingViewListener.java
@@ -0,0 +1,15 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+/**
+ * 悬浮窗监听器
+ *
+ * @author PengZhenjin
+ * @date 2017-6-5
+ */
+public interface FloatingViewListener {
+
+ /**
+ * 悬浮窗已终止
+ */
+ void onFinishFloatingView();
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/ICallNotification.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/ICallNotification.java
new file mode 100644
index 0000000..e2159b5
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/ICallNotification.java
@@ -0,0 +1,12 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+
+import com.tengshisoft.chatmodule.beans.CallConstant;
+
+/**
+ * Call module and UI callback.
+ */
+public interface ICallNotification
+{
+ void onCallEventNotify(CallConstant.CallEvent event, Object params);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/IConfNotification.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/IConfNotification.java
new file mode 100644
index 0000000..f8f8d8f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/IConfNotification.java
@@ -0,0 +1,12 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+
+import com.tenlionsoft.baselib.constant.ConfConstant;
+
+/**
+ * Callback message interface thrown to the UI layer.
+ */
+public interface IConfNotification
+{
+ void onConfEventNotify(ConfConstant.CONF_EVENT confEvent, Object params);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/ICtdNotification.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/ICtdNotification.java
new file mode 100644
index 0000000..5a0c5dd
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/ICtdNotification.java
@@ -0,0 +1,18 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+/**
+ * This interface is about Ctd event notify.
+ * CTD事件回调接口
+ */
+public interface ICtdNotification {
+
+ /**
+ * This method is used to get start ctd call result.
+ * 开始呼叫结果响应事件
+ * @param result Indicates start Ctd call operation result
+ * 结果响应,成功返回0,失败返回相应的错误码
+ * @param description Indicates description
+ * 结果描述
+ */
+ void onStartCtdCallResult(int result, String description);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/LoginEventNotifyUi.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/LoginEventNotifyUi.java
new file mode 100644
index 0000000..184b6a8
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/LoginEventNotifyUi.java
@@ -0,0 +1,20 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+
+/**
+ * @Time: 2021/5/28
+ * @Author: isoftstone
+ * @Description:登录模块UI回调
+ */
+public interface LoginEventNotifyUi {
+ /**
+ * 登录事件通知
+ *
+ * @param evt 事件枚举
+ * @param reason 原因code
+ * @param description 描述
+ */
+ void onLoginEventNotify(Constant.LoginUIEvent evt, int reason, String description);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/NoDoubleClickListener.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/NoDoubleClickListener.java
new file mode 100644
index 0000000..d9fb95b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/NoDoubleClickListener.java
@@ -0,0 +1,22 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+import android.view.View;
+
+import java.util.Calendar;
+
+public abstract class NoDoubleClickListener implements View.OnClickListener {
+ private static long lastClickTime = 0;
+
+ public static final int MIN_CLICK_DELAY_TIME = 1000;
+
+ public abstract void onNoDoubleClick(View view);
+
+ @Override
+ public void onClick(View v) {
+ long currentTime = Calendar.getInstance().getTimeInMillis();
+ if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {
+ lastClickTime = currentTime;
+ onNoDoubleClick(v);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OnDragTouchListener.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OnDragTouchListener.java
new file mode 100644
index 0000000..6b3138a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OnDragTouchListener.java
@@ -0,0 +1,66 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+import android.annotation.SuppressLint;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+
+
+/**
+ * @author limp
+ * @date 2020/11/30
+ */
+public class OnDragTouchListener implements View.OnTouchListener {
+ float mLocalX;
+ float mLocalY;
+ private float maxWidth;
+ private float maxHeight;
+ private float viewWidth;
+ private float viewHeight;
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mLocalX = v.getX() - event.getRawX();
+ mLocalY = v.getY() - event.getRawY();
+ maxWidth = UIUtil.getMaxWidth(v.getContext());
+ maxHeight = UIUtil.getMaxHeight(v.getContext());
+ viewWidth = v.getWidth();
+ viewHeight = v.getHeight();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ float moveX = event.getRawX() + mLocalX;
+ float moveY = event.getRawY() + mLocalY;
+ moveX = moveX < 0 ? 0 : (moveX + viewWidth > maxWidth) ? (maxWidth - viewWidth) : moveX;
+ moveY = moveY < 0 ? 0 : (moveY + viewHeight) > maxHeight ? (maxHeight - viewHeight) : moveY;
+ v.animate()
+ .x(moveX)
+ .y(moveY)
+ .setDuration(0)
+ .start();
+ break;
+ case MotionEvent.ACTION_UP:
+ //做吸附效果,功能已完成,解开注释就行
+// float centerX = v.getX() + viewWidth / 2;
+// if (centerX > maxWidth / 2) {
+// //靠右吸附
+// v.animate().setInterpolator(new DecelerateInterpolator())
+// .setDuration(500)
+// .xBy(maxWidth - viewWidth - v.getX())
+// .start();
+// } else {
+// v.animate().setInterpolator(new DecelerateInterpolator())
+// .setDuration(500)
+// .x(0)
+// .start();
+// }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OnLanguageListener.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OnLanguageListener.java
new file mode 100644
index 0000000..c0d28a9
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OnLanguageListener.java
@@ -0,0 +1,27 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+import java.util.Locale;
+
+/**
+ * @Time: 2021/9/8
+ * @Author: isoftstone
+ * @Description:Activity 语种变化监听器
+ */
+public interface OnLanguageListener {
+
+ /**
+ * 当前应用语种发生变化时回调
+ *
+ * @param oldLocale 旧语种
+ * @param newLocale 新语种
+ */
+ void onAppLocaleChange(Locale oldLocale, Locale newLocale);
+
+ /**
+ * 手机系统语种发生变化时回调
+ *
+ * @param oldLocale 旧语种
+ * @param newLocale 新语种
+ */
+ void onSystemLocaleChange(Locale oldLocale, Locale newLocale);
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OrientationListener.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OrientationListener.java
new file mode 100644
index 0000000..707507b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/listener/OrientationListener.java
@@ -0,0 +1,111 @@
+package com.tengshisoft.chatmodule.hwclud.listener;
+
+import android.app.Activity;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * @author Jackson
+ * 对手机方向传感器做的监听
+ */
+public class OrientationListener implements SensorEventListener {
+ private static final int DATA_X = 0;
+ private static final int DATA_Y = 1;
+ private static final int DATA_Z = 2;
+
+ public static final int ORIENTATION_UNKNOWN = -1;
+
+ private OnOrientationListener mOnOrientationListener;
+ private WeakReference mContext;
+ private SensorManager mSensorManager;
+ private Sensor sensor;
+ /**
+ * 实例化方向监听所需的传感器
+ */
+ private void start() {
+ // 实例化传感器管理工具
+ mSensorManager = (SensorManager) mContext.get().getSystemService(Context.SENSOR_SERVICE);
+ // 初始化加速度传感器
+ sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ }
+
+ /**
+ *
+ * 注册加速度和地磁场传感器监听
+ * 在onResume中调用
+ *
+ */
+ public void registerListener() {
+ if (mSensorManager != null && sensor != null) {
+ mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+
+ /**
+ *
+ * 解除监听
+ * onPause中进行调用
+ *
+ */
+ public void unregisterListener() {
+ mSensorManager.unregisterListener(this);
+ }
+
+ /**
+ * 构造函数
+ * @param ac mContext
+ */
+ public OrientationListener(Activity ac) {
+ this.mContext= new WeakReference<>(ac);
+ start();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor arg0, int arg1) {
+
+ }
+
+ private static final String TAG = "OrientationListener";
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ float[] values = event.values;
+ int orientation = ORIENTATION_UNKNOWN;
+ float X = -values[DATA_X];
+ float Y = -values[DATA_Y];
+ float Z = -values[DATA_Z];
+// Log.d(TAG, "onSensorChanged: X = "+X+"----Y = "+Y+ "----Z = "+Z);
+ float magnitude = X * X + Y * Y;
+ if (magnitude * 4 >= Z * Z) {
+ float oneEightyOverPi = 57.29577957855f;
+ float angle = (float) Math.atan2(-Y, X) * oneEightyOverPi;
+ orientation = 90 - (int) Math.round(angle);
+ while (orientation >= 360) {
+ orientation -= 360;
+ }
+ while (orientation < 0) {
+ orientation += 360;
+ }
+ }
+ mOnOrientationListener.onOrientationChanged(orientation);
+ }
+
+ public interface OnOrientationListener{
+ /**
+ * orientation 变化监听
+ * @param orientation 方向
+ */
+ void onOrientationChanged(int orientation);
+ }
+ /**
+ * 设置监听
+ * @param onOrientationListener 监听
+ */
+ public void setOnOrientationListener(OnOrientationListener onOrientationListener) {
+ this.mOnOrientationListener = onOrientationListener;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/AudioRouteManager.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/AudioRouteManager.java
new file mode 100644
index 0000000..35d799c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/AudioRouteManager.java
@@ -0,0 +1,83 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+/**
+ * @author WangBO
+ * @date 2020/12/31 13:56
+ */
+public class AudioRouteManager extends SwitchAudioRouteManager {
+
+ private static final String TAG = AudioRouteManager.class.getName();
+
+ private volatile static AudioRouteManager instance;
+
+
+ private AudioRouteManager() {
+
+ }
+
+ public static AudioRouteManager getInstance() {
+ if (null == instance) {
+ synchronized (AudioRouteManager.class) {
+ if (null == instance) {
+ instance = new AudioRouteManager();
+ }
+ }
+ }
+ return instance;
+ }
+
+ public static class Builder {
+
+ /**
+ * 耳机是否插入 bluetoothHeadset|wiredHeadset
+ */
+ private boolean headsetPlug = false;
+
+ /**
+ * 用户是否点击设置过声音路由
+ */
+ private boolean hasSetting = false;
+ /**
+ * 是否是视频
+ */
+ private boolean isVideo = false;
+
+
+ /**
+ * @param headsetPlug 耳机是否连接 wiredHeadset|bluetoothHeadset
+ * @return Builder
+ */
+ public Builder setHeadsetPlug(boolean headsetPlug) {
+ this.headsetPlug = headsetPlug;
+ return this;
+ }
+
+ /**
+ * @param hasSetting 用户是否设置
+ * @return Builder
+ */
+ public Builder setHasSetting(boolean hasSetting) {
+ this.hasSetting = hasSetting;
+ return this;
+ }
+
+ /**
+ * @param isVideo 是否是视频
+ * @return Builder
+ */
+ public Builder setIsVideo(boolean isVideo) {
+ this.isVideo = isVideo;
+ return this;
+ }
+
+ public AudioRouteManager build() {
+ AudioRouteManager audioRouteManager = getInstance();
+ boolean succeed = audioRouteManager.setRoute(headsetPlug, isVideo, hasSetting);
+ LogUtils.d(TAG, "setAudioRoute succeed" + succeed);
+ return audioRouteManager;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/CallMgrV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/CallMgrV2.java
new file mode 100644
index 0000000..1374de5
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/CallMgrV2.java
@@ -0,0 +1,1174 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+
+import android.os.Build;
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.base.TsdkCallInfo;
+import com.huawei.ecterminalsdk.base.TsdkCallStreamInfo;
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute;
+import com.huawei.ecterminalsdk.base.TsdkVideoOrientation;
+import com.huawei.ecterminalsdk.base.TsdkVideoViewRefresh;
+import com.huawei.ecterminalsdk.base.TsdkVideoViewRefreshEvent;
+import com.huawei.ecterminalsdk.base.TsdkVideoViewType;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.call.TsdkCallManager;
+import com.tengshisoft.chatmodule.beans.CallConstant;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.Session;
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.listener.ICallNotification;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.MediaUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * This class is about call manager
+ * 呼叫管理类
+ */
+public class CallMgrV2 implements ICallMgr {
+ private static final String TAG = CallMgrV2.class.getSimpleName();
+ /**
+ * Single Case Call Management instance
+ * 单例呼叫管理实例
+ */
+ private static volatile CallMgrV2 mInstance;
+
+ /**
+ * Call Session map collection include call ID and call session
+ * 呼叫会话集合 呼叫id和呼叫会话的集合
+ */
+ private Map callSessionMap = new HashMap<>();
+
+ /**
+ * UI callback
+ * UI回调
+ */
+ private ICallNotification mCallNotification;
+
+ /**
+ * Call管理对象
+ */
+ private TsdkCallManager mTsdkCallManager;
+
+ /**
+ * Ring back tone handle
+ * 回铃音句柄
+ */
+ private int ringBackToneHandle = -1;
+
+ /**
+ * 普通通话呼叫ID,用于通话转会议失败之后,恢复原通话
+ */
+ private int originalCallId = 0;
+
+ private int mCallId = 0;
+
+ /**
+ * 是否恢复转会议通话
+ */
+ private boolean resumeHold = false;
+
+ private boolean isReferCall;
+
+ private CallMgrV2() {
+ mTsdkCallManager = TsdkManager.getInstance().getCallManager();
+ }
+
+ public static CallMgrV2 getInstance() {
+ if (mInstance == null) {
+ synchronized (CallMgrV2.class) {
+ if (mInstance == null) {
+ mInstance = new CallMgrV2();
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ public int getCallId() {
+ return mCallId;
+ }
+
+ public boolean isResumeHold() {
+ return resumeHold;
+ }
+
+ public void setResumeHold(boolean resumeHold) {
+ this.resumeHold = resumeHold;
+ }
+
+ public int getOriginalCallId() {
+ return originalCallId;
+ }
+
+ public void setOriginalCallId(int originalCallId) {
+ this.originalCallId = originalCallId;
+ }
+
+ /**
+ * This method is used to store call session
+ *
+ * @param session 会话信息
+ */
+ public void putCallSessionToMap(Session session) {
+ callSessionMap.put(session.getCallID(), session);
+ }
+
+ /**
+ * This method is used to remove call information
+ *
+ * @param session 会话信息
+ */
+ public void removeCallSessionFromMap(Session session) {
+ callSessionMap.remove(session.getCallID());
+ }
+
+ /**
+ * This method is used to get call information by ID
+ *
+ * @param callID 呼叫id
+ * @return Session 会话信息
+ */
+ public Session getCallSessionByCallID(int callID) {
+ return callSessionMap.get(callID);
+ }
+
+ /**
+ * This method is used to Video Destroy.
+ * 释放视频资源
+ */
+ public void videoDestroy() {
+ VideoMgr.getInstance().clearCallVideo();
+ }
+
+ /**
+ * This method is used to gets video device.
+ * 获取视频设备
+ *
+ * @return the video device
+ */
+ public VideoMgr getVideoDevice() {
+ return VideoMgr.getInstance();
+ }
+
+ /**
+ * This method is used to set the default audio output device
+ * 设置默认的音视频路由
+ *
+ * @param isVideoCall
+ */
+ private void setDefaultAudioRoute(boolean isVideoCall) {
+ AudioRouteManager.Builder builder = new AudioRouteManager.Builder();
+ builder.setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setIsVideo(isVideoCall)
+ .build();
+
+ }
+
+ public TsdkCallStreamInfo getCallQuality(int callId) {
+ return mTsdkCallManager.getCallStreamInfo(callId);
+ }
+
+ /**
+ * This method is used to get speak volume of media.
+ * 获取扬声器输出音量大小
+ *
+ * @return the media speak volume
+ */
+ public int getMediaSpeakVolume() {
+ return mTsdkCallManager.getSpeakVolume();
+ }
+
+ public static void setDefPicture() {
+ TsdkCallInfo tsdkCallInfo = new TsdkCallInfo();
+ tsdkCallInfo.setCallId(0);
+ TsdkCall tsdkCall = new TsdkCall(tsdkCallInfo);
+ tsdkCall.setCameraPicture(ConstantsV2.BMP_FILE_PATH);
+ LogUtils.d(TAG, "setDefPicture");
+ }
+
+ /**
+ * This method is used to open camera
+ * 打开摄像头
+ *
+ * @param callID 呼叫id
+ */
+ public boolean openCamera(int callID, int cameraIndex) {
+ LogUtils.d(TAG, "openCamera");
+ TsdkCall call = mTsdkCallManager.getCallByCallId(callID);
+ if (call == null) {
+ LogUtils.d(TAG, "openCamera callSession is null");
+ return false;
+ }
+ VideoMgr.getInstance().openCamera(call, cameraIndex);
+ return true;
+ }
+
+ /**
+ * This method is used to get call information
+ * 获取呼叫信息
+ *
+ * @param call
+ * @return
+ */
+ public CallInfo getCallInfo(TsdkCall call) {
+ LogUtils.d(TAG, "getCallInfo");
+ String peerNumber = call.getCallInfo().getPeerNumber();
+ String peerDisplayName = call.getCallInfo().getPeerDisplayName();
+ boolean isFocus = false;
+ boolean isVideoCall = false;
+ boolean isCaller = call.getCallInfo().getIsCaller() == 1;
+ if (call.getCallInfo().getIsFocus() == 1) {
+ isFocus = true;
+ }
+ if (call.getCallInfo().getIsVideoCall() == 1) {
+ isVideoCall = true;
+ }
+ return new CallInfo.Builder()
+ .setCallID(call.getCallInfo().getCallId())
+ .setConfID(call.getCallInfo().getConfId())
+ .setPeerNumber(peerNumber)
+ .setPeerDisplayName(peerDisplayName)
+ .setVideoCall(isVideoCall)
+ .setFocus(isFocus)
+ .setCaller(isCaller)
+ .setReasonCode(call.getCallInfo().getReasonCode())
+ .build();
+ }
+
+
+ /**
+ * refresh route ui
+ *
+ * @param route
+ */
+ public void onEvtCallRouteChange(int route) {
+ LogUtils.d(TAG, "onEvtCallRouteChange");
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.REFRESH_ROUTE, route);
+ }
+
+ /**
+ * audio device status changed
+ *
+ * @param route
+ */
+ public void handleAudioDeviceChanged(int route) {
+ LogUtils.d(TAG, "handleAudioDeviceChanged");
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.DEVICE_CHANGED, route);
+ }
+
+ /**
+ * [en]This method is used to handle the call incoming.
+ * [cn]处理来电事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ * @param maybeVideoCall [en]Indicates maybe video call
+ * [cn]是否是视频
+ */
+ public void handleCallComing(TsdkCall call, Boolean maybeVideoCall) {
+ LogUtils.d(TAG, "handleCallComing");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallComing call is null");
+ return;
+ }
+ if (call.getCallInfo().getIsVideoCall() == 1) {
+ closeCamera(call.getCallInfo().getCallId());
+ }
+ Session newSession = new Session(call);
+ putCallSessionToMap(newSession);
+ // 来电会议是自己创建的直接入会
+ boolean isAccount = call.getCallInfo().getPeerNumber().equals(MeetingMgrV2.getInstance().getMeetingId()) &&
+ (EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT).equals(
+ MeetingMgrV2.getInstance().getMeetingScheduserAccount()) ||
+ EncryptedSPTool.getString(Constant.SIPNUMBER).equals(
+ MeetingMgrV2.getInstance().getMeetingScheduserAccount()));
+ if (isAccount) {
+ CallMgrV2.getInstance().answerCall(call.getCallInfo().getCallId(), true);
+ MeetingMgrV2.getInstance().setMeetingId("");
+ MeetingMgrV2.getInstance().setMeetingScheduserAccount("");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CREATE_SELF_CONF_SUCCESS, 0);
+ } else {
+ CallInfo callInfo = getCallInfo(call);
+ callInfo.setMaybeVideoCall(maybeVideoCall);
+ // peerNumber相等为自己创建的会议
+ if (EncryptedSPTool.getString(Constant.CONF_TEMPORARY).equals(call.getCallInfo().getPeerNumber())) {
+ EncryptedSPTool.putString(Constant.CONF_TEMPORARY, "");
+ }
+ // 来电前设置扬声器
+ SwitchAudioRouteManager.Companion.getInstance().setCallComing(true);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.CALL_COMING, callInfo);
+ }
+ }
+
+ /**
+ * [en]This method is used to handle the call out going.
+ * [cn]处理呼出事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCallGoing(TsdkCall call) {
+ LogUtils.d(TAG, "handleCallGoing");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallGoing call is null");
+ return;
+ }
+ if (call.getCallInfo().getIsVideoCall() == 1) {
+ closeCamera(call.getCallInfo().getCallId());
+ }
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.CALL_GOING, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle call connected
+ * [cn]处理通话建立事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCallConnected(TsdkCall call) {
+ LogUtils.d(TAG, "handleCallConnected");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallConnected call is null");
+ return;
+ }
+ MeetingMgrV2.getInstance().resetCurrentConference();
+ Session callSession = getCallSessionByCallID(call.getCallInfo().getCallId());
+ if (callSession == null) {
+ LogUtils.d(TAG, "handleCallConnected callSession is null");
+ callSession = new Session(call);
+ CallMgrV2.getInstance().putCallSessionToMap(callSession);
+ }
+ if (call.getCallInfo().getIsVideoCall() == 1) {
+ callSession.setCallStatus(CallConstant.CallStatus.VIDEO_CALLING);
+ } else {
+ callSession.setCallStatus(CallConstant.CallStatus.AUDIO_CALLING);
+ }
+ SwitchAudioRouteManager.Companion.getInstance().setCallComing(false);
+ LogUtils.d(TAG, "handleCallConnected isVideoCall", call.getCallInfo().getIsVideoCall() == 1);
+ this.mCallId = call.getCallInfo().getCallId();
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.CALL_CONNECTED, call);
+ }
+
+ /**
+ * [en]This method is used to handle call ring back
+ * [cn]处理响铃音事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCallRingback(TsdkCall call) {
+ LogUtils.d(TAG, "handleCallRingback");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallRingback call is null");
+ return;
+ }
+ if (null != mCallNotification) {
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.PLAY_RING_BACK_TONE, null);
+ }
+ }
+
+ public void setReferCall(boolean referCall) {
+ LogUtils.d(TAG, "setReferCall");
+ isReferCall = referCall;
+ }
+
+ /**
+ * 判断是否发生呼叫转移提
+ */
+ public boolean getReferCall() {
+ LogUtils.d(TAG, "getReferCall");
+ return isReferCall;
+ }
+
+ /**
+ * [en]This method is used to handle call end
+ * [cn]处理通话结束
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCallEnded(TsdkCall call) {
+ LogUtils.d(TAG, "handleCallEnded");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallEnded call is null");
+ return;
+ }
+ // 清除此状态,
+ if (UIUtil.isService3()) {
+ EncryptedSPTool.remove(Constant.IS_VMR_3_ID);
+ } else {
+ EncryptedSPTool.remove(Constant.IS_VMR_2_ID);
+ }
+ CallInfo callInfo = getCallInfo(call);
+ this.mCallId = 0;
+ // 此状态为判断加入会议的入口,主动加入&发起
+ MeetingController.getInstance().setSponsorConf(false);
+ LogUtils.d(TAG, "onEvtCallEnded");
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.CALL_ENDED, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle call end destroy
+ * [cn]处理呼叫销毁事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCallDestroy(TsdkCall call) {
+ LogUtils.d(TAG, "handleCallDestroy");
+ this.mCallId = 0;
+ MeetingMgrV2.getInstance().getWatchMember().clear();
+ MeetingMgrV2.getInstance().resetCurrentConference();
+ MeetingController.getInstance().setSvcWatchStatus(SvcWatchStatus.None);
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallDestroy call is null");
+ return;
+ }
+ Session callSession = getCallSessionByCallID(call.getCallInfo().getCallId());
+ LocalBroadcast.getInstance().sendBroadcast(Constant.MEETING_DESTORY, null);
+ if (callSession == null) {
+ LogUtils.d(TAG, "handleCallDestroy callSession obj is null");
+ return;
+ }
+ // 从会话列表中移除一路会话
+ removeCallSessionFromMap(callSession);
+ }
+
+ /**
+ * [en]This method is used to handle call rtp created.
+ * [cn]处理RTP创建事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCallRtpCreated(TsdkCall call) {
+ LogUtils.d(TAG, "handleCallRtpCreated");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCallRtpCreated call is null");
+ return;
+ }
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.RTP_CREATED, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle call audio to video request.
+ * [cn]处理音频转视频请求
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ * @param orientType [en]Indicates orient type
+ * [cn]视频显示方向类型
+ */
+ public void handleOpenVideoReq(TsdkCall call, TsdkVideoOrientation orientType) {
+ LogUtils.d(TAG, "handleOpenVideoReq");
+ if (call == null) {
+ LogUtils.d(TAG, "handleOpenVideoReq call is null");
+ return;
+ }
+ mCallNotification.onCallEventNotify(
+ CallConstant.CallEvent.RECEIVED_REMOTE_ADD_VIDEO_REQUEST, getCallInfo(call));
+ }
+
+ /**
+ * [en]This method is used to handle call audio to video request result.
+ * [cn]处理音频转视频结果
+ *
+ * @param call [en]Indicates call info
+ * [cn]对方同意
+ */
+ public void handleOpenVideoInd(TsdkCall call) {
+ LogUtils.d(TAG, "handleOpenVideoInd");
+ int isVideo = call.getCallInfo().getIsVideoCall();
+ int callId = call.getCallInfo().getCallId();
+ LogUtils.d(TAG, "handleOpenVideoInd isVideo " + isVideo + "callId " + callId);
+ Session callSession = getCallSessionByCallID(callId);
+ if (callSession == null) {
+ return;
+ }
+ CallInfo callInfo = getCallInfo(call);
+ LogUtils.d(TAG, "handleOpenVideoInd upgrade to video call");
+ if (MeetingController.getInstance().isVideoOpen) {
+ VideoMgr.getInstance().setVideoOrient(callId, CallConstant.FRONT_CAMERA);
+ }
+ callSession.setCallStatus(CallConstant.CallStatus.VIDEO_CALLING);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.OPEN_VIDEO, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle call video to audio request result
+ * [cn]处理视频转音频结果
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleCloseVideoInd(TsdkCall call) {
+ LogUtils.d(TAG, "handleCloseVideoInd");
+ if (call == null) {
+ LogUtils.d(TAG, "handleCloseVideoInd call is null");
+ return;
+ }
+ Session callSession = getCallSessionByCallID(call.getCallInfo().getCallId());
+ if (callSession == null) {
+ LogUtils.d(TAG, "handleCloseVideoInd callSession obj is null");
+ return;
+ }
+ callSession.setCallStatus(CallConstant.CallStatus.AUDIO_CALLING);
+ // clear video data
+ VideoMgr.getInstance().clearCallVideo();
+ if (null != mCallNotification) {
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.CLOSE_VIDEO, callInfo);
+ }
+ if (callSession.isVideoHold()) {
+ callSession.holdCall();
+ }
+ }
+
+ /**
+ * [en]This method is used to handle call window refresh
+ * [cn]刷新窗口信息
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ * @param refreshInfo [en]Indicates refresh Info
+ * [cn]刷新信息
+ */
+ public void handleRefreshViewInd(TsdkCall call, TsdkVideoViewRefresh refreshInfo) {
+ LogUtils.d(TAG, "handleRefreshViewInd");
+ TsdkVideoViewType mediaType = TsdkVideoViewType.enumOf(refreshInfo.getViewType());
+ TsdkVideoViewRefreshEvent eventType = TsdkVideoViewRefreshEvent.enumOf(refreshInfo.getEvent());
+ int callId = call.getCallInfo().getCallId();
+ switch (mediaType) {
+ case TSDK_E_VIEW_LOCAL_PREVIEW:
+ case TSDK_E_VIEW_VIDEO_VIEW:
+ callEventNotify(eventType, callId);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ /**
+ * [en]This method is used to handle call hold failed
+ * [cn]处理呼叫保持失败事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleHoldFailed(TsdkCall call) {
+ LogUtils.d(TAG, "handleHoldFailed");
+ CallInfo callInfo = getCallInfo(call);
+ Session callSession = getCallSessionByCallID(callInfo.getCallID());
+ if (callSession == null) {
+ LogUtils.d(TAG, "handleHoldFailed callSession obj is null");
+ return;
+ }
+ if (callSession.isVideoHold()) {
+ callSession.setVideoHold(false);
+ // 保持失败,只直接通知UI失败,不自动动恢复视频
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.VIDEO_HOLD_FAILED, callInfo);
+ } else {
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.AUDIO_HOLD_FAILED, callInfo);
+ }
+ }
+
+ /**
+ * [en]This method is used to handle call unhold failed
+ * [cn]处理取消呼叫保持失败事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleUnholdFailed(TsdkCall call) {
+ LogUtils.d(TAG, "handleUnholdFailed");
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.UN_HOLD_FAILED, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle call divert failed
+ * [cn]处理偏转失败事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleDivertFailed(TsdkCall call) {
+ LogUtils.d(TAG, "handleDivertFailed");
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.DIVERT_FAILED, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle blind transfer success failed
+ * [cn]处理盲转失败事件
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleBldTransferFailed(TsdkCall call) {
+ LogUtils.d(TAG, "handleBldTransferFailed");
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.BLD_TRANSFER_FAILED, callInfo);
+ }
+
+ /**
+ * [en]This method is used to handle remote reject audio to video
+ * [cn]远端拒绝音频转视频
+ *
+ * @param call [en]Indicates call info
+ * [cn]呼叫信息
+ */
+ public void handleRefuseOpenVideoInd(TsdkCall call) {
+ LogUtils.d(TAG, "handleRefuseOpenVideoInd");
+ VideoMgr.getInstance().clearCallVideo();
+ Session callSession = getCallSessionByCallID(call.getCallInfo().getCallId());
+ if (callSession == null) {
+ LogUtils.d(TAG, "handleRefuseOpenVideoInd callSession is null");
+ return;
+ }
+ callSession.setCallStatus(CallConstant.CallStatus.AUDIO_CALLING);
+ CallInfo callInfo = getCallInfo(call);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.REMOTE_REFUSE_ADD_VIDEO_SREQUEST, callInfo);
+
+ }
+
+ public TsdkCall getTSdkCallByCallMgr() {
+ LogUtils.d(TAG, "getTSdkCallByCallMgr");
+ TsdkManager tsdkManager = TsdkManager.getInstance();
+ if (tsdkManager == null) {
+ LogUtils.d(TAG, "TsdkManager is null");
+ return null;
+ }
+ TsdkCallManager tsdkCallManager = tsdkManager.getCallManager();
+ if (tsdkCallManager == null) {
+ LogUtils.d(TAG, "TsdkCallManager is null");
+ return null;
+ }
+ return tsdkCallManager.getCallByCallId(CallMgrV2.getInstance().getCallId());
+ }
+
+ @Override
+ public void regCallServiceNotification(ICallNotification callNotification) {
+ LogUtils.d(TAG, "regCallServiceNotification");
+ this.mCallNotification = callNotification;
+ }
+
+ /**
+ * This method is used to configure Call Parameters
+ */
+ @Override
+ public void configCallServiceParam() {
+ LogUtils.d(TAG, "configCallServiceParam");
+ }
+
+ /**
+ * This method is used to switching audio routing devices
+ * 切换音频路由设备
+ *
+ * @return
+ */
+ @Override
+ public TsdkMobileAuidoRoute switchAudioRoute() {
+ LogUtils.d(TAG, "switchAudioRoute");
+ // 获取移动音频路由设备
+ int audioRoute = getCurrentAudioRoute();
+ LogUtils.d(TAG, "switchAudioRoute audioRoute is" + audioRoute);
+ if (audioRoute == TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER.getIndex()) {
+ LogUtils.d(TAG, "switchAudioRoute DEFAULT");
+ return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT;
+ } else {
+ // 设置扬声器输出音量大小
+ int setMediaSpeakVolumeResult = mTsdkCallManager.setSpeakVolume(getMediaSpeakVolume());
+ LogUtils.d(TAG, "switchAudioRoute setSpeakVolume result" + setMediaSpeakVolumeResult);
+ LogUtils.d(TAG, "switchAudioRoute LOUDSPEAKER");
+ return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER;
+ }
+ }
+
+ /**
+ * This method is used to get mobile audio route
+ *
+ * 获取移动音频路由设备
+ *
+ * @return the audio route
+ */
+ @Override
+ public int getCurrentAudioRoute() {
+ LogUtils.d(TAG, "getCurrentAudioRoute");
+ if (mTsdkCallManager.getMobileAudioRoute() == null) {
+ LogUtils.d(TAG, "getCurrentAudioRoute audioRoute is null");
+ return -1;
+ }
+ return mTsdkCallManager.getMobileAudioRoute().getIndex();
+ }
+
+ /**
+ * This method is used to get call status
+ * 获取呼叫状态
+ *
+ * @param callID 呼叫id
+ * @return
+ */
+ @Override
+ public CallConstant.CallStatus getCallStatus(int callID) {
+ LogUtils.d(TAG, "getCallStatus");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "getCallStatus callSession is null");
+ return CallConstant.CallStatus.UNKNOWN;
+ }
+ return callSession.getCallStatus();
+ }
+
+
+ /**
+ * This method is used to make call or make video call
+ * 创建一个音频或者视频呼叫
+ *
+ * @param toNumber 呼叫号码
+ * @param isVideoCall 是否是视频
+ * @return int 0 success
+ */
+ @Override
+ public synchronized int startCall(String toNumber, boolean isVideoCall, String displayName) {
+ LogUtils.d(TAG, "startCall");
+ if (TextUtils.isEmpty(toNumber)) {
+ LogUtils.d(TAG, "startCall number is null!");
+ return 0;
+ }
+ setDefPicture();
+ // 创建一路呼叫
+ TsdkCall call = mTsdkCallManager.startCall(toNumber, isVideoCall, displayName);
+ if (call != null) {
+ Session newSession = new Session(call);
+ putCallSessionToMap(newSession);
+ setDefaultAudioRoute(isVideoCall);
+ if (isVideoCall) {
+ newSession.initVideoWindow();
+ }
+ LogUtils.e(TAG, "startCall is success.");
+ return call.getCallInfo().getCallId();
+ }
+ LogUtils.e(TAG, "startCall is failed.");
+ return 0;
+ }
+
+ /**
+ * This method is used to answer incoming call
+ * 接听一路呼叫
+ *
+ * @param callID 呼叫id
+ * @param isVideo 是否是视频
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean answerCall(int callID, boolean isVideo) {
+ LogUtils.d(TAG, "answerCall");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "answerCall callSession is null");
+ return false;
+ }
+ return callSession.answerCall(isVideo);
+ }
+
+ /**
+ * This method is used to reject or hangup call
+ * 结束呼叫
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean endCall(int callID) {
+ LogUtils.d(TAG, "endCall");
+ TsdkCall tsdkCall = mTsdkCallManager.getCallByCallId(callID);
+ if (tsdkCall == null) {
+ LogUtils.d(TAG, "endCall tsdkCall is null");
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.CALL_ENDED_FAILED, null);
+ return false;
+ }
+ int result = tsdkCall.endCall();
+ // 离会清除选看缓存
+ BaseAppContext.getInstance().setMember(null);
+ LogUtils.d(TAG, "endCall result ", result);
+ return result == 0;
+ }
+
+ /**
+ * This method is used to divert incoming call
+ * 发起偏转呼叫
+ *
+ * @param callID 呼叫id
+ * @param divertNumber 偏转号码
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean divertCall(int callID, String divertNumber) {
+ LogUtils.d(TAG, "divertCall");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "divertCall callSession is null");
+ return false;
+ }
+ return callSession.divertCall(divertNumber);
+ }
+
+ /**
+ * This method is used to blind transfer call
+ * 发起盲转呼叫请求
+ *
+ * @param callID 呼叫id
+ * @param transferNumber 盲转号码
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean blindTransfer(int callID, String transferNumber) {
+ LogUtils.d(TAG, "blindTransfer");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "blindTransfer callSession is null");
+ return false;
+ }
+ return callSession.blindTransfer(transferNumber);
+ }
+
+ /**
+ * This method is used to hold call
+ * 保持一路音频呼叫
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean holdCall(int callID) {
+ LogUtils.d(TAG, "holdCall");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "holdCall callSession is null");
+ return false;
+ }
+ return callSession.holdCall();
+ }
+
+ /**
+ * This method is used to hold the video Call
+ * 保持一路视频呼叫
+ *
+ * @param callID 呼叫id
+ * @return
+ */
+ @Override
+ public boolean holdVideoCall(int callID) {
+ LogUtils.d(TAG, "holdVideoCall");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "holdVideoCall callSession is null");
+ return false;
+ }
+ // 视频保持先移除视频,待视频移除成功后,再保持
+ boolean result = callSession.delVideo();
+ if (result) {
+ callSession.setVideoHold(true);
+ }
+ return result;
+ }
+
+ /**
+ * This method is used to unhold call
+ * 取消保持呼叫
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean unHoldCall(int callID) {
+ LogUtils.d(TAG, "unHoldCall");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "unHoldCall callSession is null");
+ return false;
+ }
+ return callSession.unHoldCall();
+ }
+
+
+ /**
+ * This method is used to send DTMF tone
+ * 二次拨号
+ *
+ * @param callID 呼叫id
+ * @param code (0到9,*为10,#为11)
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean reDial(int callID, int code) {
+ LogUtils.d(TAG, "reDial code " + code);
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "reDial callSession is null");
+ return false;
+ }
+ return callSession.reDial(code);
+ }
+
+ /**
+ * This method is used to request change from an audio call to a video call
+ * 音频转视频
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean addVideo(int callID) {
+ LogUtils.d(TAG, "addVideo");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "addVideo callSession is null");
+ return false;
+ }
+ return callSession.addVideo();
+ }
+
+ /**
+ * This method is used to request a change from a video call to an audio call
+ * 视频转音频
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean delVideo(int callID) {
+ LogUtils.d(TAG, "delVideo");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "delVideo callSession is null");
+ return false;
+ }
+ return callSession.delVideo();
+ }
+
+ /**
+ * This method is used to reject change from an audio call to a video call
+ * 拒绝音频转视频请求
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean rejectAddVideo(int callID) {
+ LogUtils.d(TAG, "rejectAddVideo");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "rejectAddVideo callSession is null");
+ return false;
+ }
+ return callSession.rejectAddVideo();
+ }
+
+ /**
+ * This method is used to accept change from an audio call to a video call
+ * 接受音频转视频请求
+ *
+ * @param callID 呼叫id
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean acceptAddVideo(int callID) {
+ LogUtils.d(TAG, "acceptAddVideo");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "acceptAddVideo callSession is null");
+ return false;
+ }
+ boolean result = callSession.acceptAddVideo();
+ if (result) {
+ setDefaultAudioRoute(true);
+ callSession.setCallStatus(CallConstant.CallStatus.VIDEO_CALLING);
+ CallInfo callInfo = getCallInfo(callSession.getTsdkCall());
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.OPEN_VIDEO, callInfo);
+ }
+ return result;
+ }
+
+
+ /**
+ * This method is used to set whether mute the microphone
+ * 设置麦克风静音
+ *
+ * @param callID 呼叫id
+ * @param mute 是否静音
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean muteMic(int callID, boolean mute) {
+ LogUtils.d(TAG, "muteMic");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "muteMic callSession is null");
+ return false;
+ }
+ return callSession.muteMic(mute);
+ }
+
+ /**
+ * This method is used to set whether mute the speaker
+ * 设置扬声器静音
+ *
+ * @param callID 呼叫id
+ * @param mute 是否静音
+ * @return true:success, false:failed
+ */
+ @Override
+ public boolean muteSpeak(int callID, boolean mute) {
+ LogUtils.d(TAG, "muteSpeak");
+ Session callSession = getCallSessionByCallID(callID);
+ if (callSession == null) {
+ LogUtils.d(TAG, "muteSpeak callSession is null");
+ return false;
+ }
+ return callSession.muteSpeak(mute);
+ }
+
+ /**
+ * This method is used to Local preview
+ * 本地预览
+ *
+ * @param callID
+ * @param visible
+ */
+ @Override
+ public void switchLocalView(int callID, boolean visible) {
+ LogUtils.d(TAG, "switchLocalView");
+ }
+
+ /**
+ * This method is used to switch camera
+ * 切换摄像头
+ *
+ * @param callID 呼叫ID
+ * @param cameraIndex 摄像头下标
+ */
+ @Override
+ public boolean switchCamera(int callID, int cameraIndex) {
+ LogUtils.d(TAG, "switchCamera");
+ TsdkCall call = mTsdkCallManager.getCallByCallId(callID);
+ if (call == null) {
+ LogUtils.d(TAG, "switchCamera callSession is null");
+ return false;
+ }
+ VideoMgr.getInstance().switchCamera(call, cameraIndex);
+ return true;
+ }
+
+ /**
+ * This method is used to close camera
+ * 关闭摄像头
+ *
+ * @param callID 呼叫id
+ */
+ public boolean closeCamera(int callID) {
+ LogUtils.d(TAG, "closeCamera");
+ TsdkCall call = mTsdkCallManager.getCallByCallId(callID);
+ if (call == null) {
+ LogUtils.d(TAG, "closeCamera call is null ");
+ return false;
+ }
+ VideoMgr.getInstance().closeCamera(call);
+ return true;
+ }
+
+ /**
+ * This method is used to play ringing tone
+ * 播放铃音
+ *
+ * @param ringingFile 音频文件路径
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void startPlayRingingTone(String ringingFile) {
+ LogUtils.d(TAG, "startPlayRingingTone");
+ MediaUtil.getInstance().showSystemDefaultRing();
+ MediaUtil.getInstance().play(ringingFile);
+ }
+
+ /**
+ * This method is used to stop play ringing tone
+ * 停止播放铃音
+ */
+ @Override
+ public void stopPlayRingingTone() {
+ LogUtils.d(TAG, "stopPlayRingingTone");
+ MediaUtil.getInstance().stop();
+ }
+
+ /**
+ * This method is used to play ring back tone
+ * 播放回铃音
+ *
+ * @param ringingFile 音频文件路径
+ */
+ @Override
+ public void startPlayRingBackTone(String ringingFile) {
+ LogUtils.d(TAG, "startPlayRingBackTone");
+ // 处理可能的异常
+ if (ringBackToneHandle != -1) {
+ int result = mTsdkCallManager.stopPlayMedia(ringBackToneHandle);
+ LogUtils.d(TAG, "startPlayRingBackTone stopPlayMedia result " + result);
+ }
+ // 回铃音使用默认设备播放
+ mTsdkCallManager.setMobileAudioRoute(TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT);
+ // 播放指定回铃音
+ ringBackToneHandle = mTsdkCallManager.startPlayMedia(0, ringingFile);
+ if (ringBackToneHandle == -1) {
+ LogUtils.d(TAG, "startPlayRingBackTone startPlayMedia ringBackToneHandle", ringBackToneHandle);
+ }
+ }
+
+ /**
+ * This method is used to stop play ring back tone
+ * 停止播放回铃音
+ */
+ @Override
+ public void stopPlayRingBackTone() {
+ LogUtils.d(TAG, "stopPlayRingBackTone");
+ if (ringBackToneHandle != -1) {
+ int result = mTsdkCallManager.stopPlayMedia(ringBackToneHandle);
+ if (result != 0) {
+ LogUtils.d(TAG, "stopPlayRingBackTone stopPlayMedia result = " + result);
+ }
+ ringBackToneHandle = -1;
+ }
+ }
+
+ private void callEventNotify(TsdkVideoViewRefreshEvent eventType, int callId) {
+ if (eventType == TsdkVideoViewRefreshEvent.TSDK_E_VIDEO_LOCAL_VIEW_ADD) {
+ LogUtils.d(TAG, "handleRefreshViewInd LocalHideView LOCAL_VIEW_ADD");
+ MeetingController.getInstance().setHasExecutedRefreshView(true);
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.ADD_LOCAL_VIEW, callId);
+ } else {
+ LogUtils.d(TAG, "handleRefreshViewInd LocalHideView LOCAL_VIEW_REMOVE");
+ mCallNotification.onCallEventNotify(CallConstant.CallEvent.DEL_LOCAL_VIEW, callId);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/CloudLinkNotifyManager.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/CloudLinkNotifyManager.kt
new file mode 100644
index 0000000..d872633
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/CloudLinkNotifyManager.kt
@@ -0,0 +1,97 @@
+package com.tengshisoft.chatmodule.hwclud.manager
+
+import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.ContextWrapper
+import android.content.Intent
+import android.os.Build
+import androidx.core.app.NotificationCompat
+import com.tengshisoft.chatmodule.R
+import com.tengshisoft.chatmodule.hwclud.serivce.CloudLinkService
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil
+
+/**
+ * @Time: 2021/9/16
+ * @Author: isoftstone
+ * @Description:CloudLinkService前台通知管理
+ */
+class CloudLinkNotifyManager(private val service: CloudLinkService) : ContextWrapper(service) {
+ companion object {
+ private const val START_ID = 101
+ private const val CHANNEL_ID = "cloudLink_service_id"
+ private const val CHANNEL_NAME = "cloudLink_service_name"
+ }
+
+ private var mNotificationManager: NotificationManager? = null
+
+ private var mCompatBuilder: NotificationCompat.Builder? = null
+
+ private val compatBuilder: NotificationCompat.Builder?
+ get() {
+ if (mCompatBuilder == null) {
+ //TODO activity
+// val notificationIntent = Intent(this, MainActivityV2::class.java)
+ val notificationIntent = Intent()
+ notificationIntent.action = Intent.ACTION_MAIN
+ notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER)
+ notificationIntent.flags =
+ Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
+ // 动作意图
+ val pendingIntent = PendingIntent.getActivity(
+ this, (Math.random() * 10 + 10).toInt(),
+ notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT
+ )
+ val notificationBuilder: NotificationCompat.Builder = NotificationCompat.Builder(
+ this,
+ CHANNEL_ID
+ )
+ // 标题
+ notificationBuilder.setContentTitle(LogUtil.CLOUNDLINK)
+ // 通知内容
+ notificationBuilder.setContentText(getString(R.string.cloudLink_meeting_beferService))
+ // 设置点击通知后自动删除通知
+ notificationBuilder.setAutoCancel(true)
+ // 状态栏显示的小图标
+ notificationBuilder.setSmallIcon(R.mipmap.ic_launcher)
+ // 通知内容打开的意图
+ notificationBuilder.setContentIntent(pendingIntent)
+ mCompatBuilder = notificationBuilder
+ }
+ return mCompatBuilder
+ }
+
+ init {
+ createNotificationChannel()
+ }
+
+ // 创建通知渠道
+ private fun createNotificationChannel() {
+ mNotificationManager =
+ getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ // 针对8.0+系统
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val channel = NotificationChannel(
+ CHANNEL_ID,
+ CHANNEL_NAME,
+ NotificationManager.IMPORTANCE_LOW
+ )
+ channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
+ channel.setShowBadge(false)
+ mNotificationManager?.createNotificationChannel(channel)
+ }
+ }
+
+ // 开启前台通知
+ fun startForegroundNotification() {
+ service.startForeground(START_ID, compatBuilder?.build())
+ }
+
+ // 停止前台服务并清除通知
+ fun stopForegroundNotification() {
+ mNotificationManager?.cancelAll()
+ service.stopForeground(true)
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/ConfConvertUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/ConfConvertUtil.java
new file mode 100644
index 0000000..eec48a5
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/ConfConvertUtil.java
@@ -0,0 +1,354 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.base.TsdkAttendee;
+import com.huawei.ecterminalsdk.base.TsdkAttendeeBaseInfo;
+import com.huawei.ecterminalsdk.base.TsdkAttendeeStatusInfo;
+import com.huawei.ecterminalsdk.base.TsdkBookConfAttendeeInfo;
+import com.huawei.ecterminalsdk.base.TsdkBookConfParticipantInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfMediaType;
+import com.huawei.ecterminalsdk.base.TsdkConfParticipantStatus;
+import com.huawei.ecterminalsdk.base.TsdkConfRole;
+import com.huawei.ecterminalsdk.base.TsdkConfState;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.constant.ConfConstant;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class is about conference Tools
+ * 会议工具类
+ */
+public class ConfConvertUtil {
+
+ /**
+ * This method is used to convert conference media type.
+ * 会议媒体类型转换
+ *
+ * @param confMediaType conference media type 会议媒体类型
+ * @return
+ */
+ public static TsdkConfMediaType convertConfMediaType(int confMediaType) {
+ TsdkConfMediaType mediaType = TsdkConfMediaType.enumOf(confMediaType);
+ return mediaType;
+ }
+
+ /**
+ * This method is used to convert conference state
+ * 转换会议状态
+ *
+ * @param state
+ * @return
+ */
+ public static ConfConstant.ConfConveneStatus convertConfctrlConfState(int state) {
+ ConfConstant.ConfConveneStatus status = ConfConstant.ConfConveneStatus.UNKNOWN;
+ switch (state) {
+ case 0:
+ status = ConfConstant.ConfConveneStatus.SCHEDULE;
+ break;
+
+ case 1:
+ status = ConfConstant.ConfConveneStatus.CREATING;
+ break;
+
+ case 2:
+ status = ConfConstant.ConfConveneStatus.GOING;
+ break;
+
+ case 3:
+ status = ConfConstant.ConfConveneStatus.DESTROYED;
+ break;
+
+ default:
+ break;
+ }
+ return status;
+ }
+
+ /**
+ * This method is used to convert conference state
+ * 转换会议状态
+ *
+ * @param state
+ * @return
+ */
+ public static ConfConstant.ConfConveneStatus convertConfctrlConfState(TsdkConfState state) {
+ ConfConstant.ConfConveneStatus status = ConfConstant.ConfConveneStatus.UNKNOWN;
+ if (state == null) {
+ return status;
+ }
+ switch (state) {
+ case TSDK_E_CONF_STATE_SCHEDULE:
+ status = ConfConstant.ConfConveneStatus.SCHEDULE;
+ break;
+
+ case TSDK_E_CONF_STATE_CREATING:
+ status = ConfConstant.ConfConveneStatus.CREATING;
+ break;
+
+ case TSDK_E_CONF_STATE_GOING:
+ status = ConfConstant.ConfConveneStatus.GOING;
+ break;
+
+ case TSDK_E_CONF_STATE_DESTROYED:
+ status = ConfConstant.ConfConveneStatus.DESTROYED;
+ break;
+
+ default:
+ break;
+ }
+ return status;
+ }
+
+ public static TsdkConfParticipantStatus convertAttendStatus(int state) {
+ TsdkConfParticipantStatus tsdkConfParticipantStatus = TsdkConfParticipantStatus.enumOf(state);
+ return tsdkConfParticipantStatus;
+ }
+
+ /**
+ * This method is used to convert conference participant state
+ * 转换与会者状态
+ *
+ * @param state
+ * @return
+ */
+ public static ConfConstant.ParticipantStatus convertConfctrlParticipantStatus(TsdkConfParticipantStatus state) {
+ ConfConstant.ParticipantStatus status = ConfConstant.ParticipantStatus.UNKNOWN;
+ switch (state) {
+ case TSDK_E_CONF_PARTICIPANT_STATUS_IN_CONF:
+ status = ConfConstant.ParticipantStatus.IN_CONF;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_CALLING:
+ status = ConfConstant.ParticipantStatus.CALLING;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_JOINING:
+ status = ConfConstant.ParticipantStatus.JOINING;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_LEAVED:
+ status = ConfConstant.ParticipantStatus.LEAVED;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_NO_EXIST:
+ status = ConfConstant.ParticipantStatus.NO_EXIST;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_BUSY:
+ status = ConfConstant.ParticipantStatus.BUSY;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_NO_ANSWER:
+ status = ConfConstant.ParticipantStatus.NO_ANSWER;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_REJECT:
+ status = ConfConstant.ParticipantStatus.REJECT;
+ break;
+
+ case TSDK_E_CONF_PARTICIPANT_STATUS_CALL_FAILED:
+ status = ConfConstant.ParticipantStatus.CALL_FAILED;
+ break;
+
+ default:
+ break;
+ }
+ return status;
+ }
+
+ /**
+ * This method is used to convert conference protocol
+ * 转换会议协议类型
+ *
+ * @param protocol
+ * @return
+ */
+ public static ConfConstant.ConfProtocol convertConfctrlProtocol(int protocol) {
+ ConfConstant.ConfProtocol confProtocol = ConfConstant.ConfProtocol.IDO_PROTOCOL;
+ switch (protocol) {
+ case 0:
+ confProtocol = ConfConstant.ConfProtocol.IDO_PROTOCOL;
+ break;
+ case 1:
+ confProtocol = ConfConstant.ConfProtocol.REST_PROTOCOL;
+ break;
+ default:
+ break;
+ }
+ return confProtocol;
+ }
+
+ public static List convertMemberList(List memberList) {
+ List attendeeInfoList = new ArrayList<>();
+ for (Member member : memberList) {
+ TsdkAttendee attendeeInfo = new TsdkAttendee();
+
+ attendeeInfo.getBaseInfo().setNumber(member.getNumber());
+ attendeeInfo.getBaseInfo().setDisplayName(member.getDisplayName());
+ attendeeInfo.getBaseInfo().setAccountId(member.getAccountId());
+ attendeeInfo.getBaseInfo().setEmail(member.getEmail());
+ attendeeInfo.getBaseInfo().setSms(member.getSms());
+ attendeeInfo.getStatusInfo().setIsMute(member.isMute() ? 1 : 0);
+
+ TsdkConfRole role = ((member.getRole() == TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN) ?
+ TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN : TsdkConfRole.TSDK_E_CONF_ROLE_ATTENDEE);
+ attendeeInfo.getBaseInfo().setRole(role);
+
+// attendeeInfo.setType(ConfctrlAttendeeType.CONFCTRL_E_ATTENDEE_TYPE_NORMAL);
+
+ attendeeInfoList.add(attendeeInfo);
+ }
+ return attendeeInfoList;
+ }
+
+
+ /**
+ * This method is used to convert member info
+ * 转变与会者信息
+ *
+ * @param attendeeInfo
+ * @return
+ */
+ public static Member convertAttendeeInfo(TsdkAttendee attendeeInfo) {
+ Member member = new Member();
+
+ TsdkAttendeeStatusInfo attendeeStatusInfo = attendeeInfo.getStatusInfo();
+ TsdkAttendeeBaseInfo attendeeBaseInfo = attendeeInfo.getBaseInfo();
+
+ member.setParticipantId(attendeeStatusInfo.getParticipantId());
+ member.setNumber(attendeeBaseInfo.getNumber());
+ // 解决AD帐号,与会者列表中显示的displayName=number问题,接口返回数据displayName=number
+ if (attendeeBaseInfo.getDisplayName().equals(LoginMangerV2.getInstance().getTerminal()) &&
+ UIUtil.isAuthTypeAD()) {
+ member.setDisplayName(EncryptedSPTool.getString(Constant.MINE_USER_NAME));
+ } else {
+ member.setDisplayName(attendeeBaseInfo.getDisplayName());
+ }
+ member.setAccountId(attendeeBaseInfo.getAccountId());
+ member.setEmail(attendeeBaseInfo.getEmail());
+ member.setSms(attendeeBaseInfo.getSms());
+ member.setUserId(attendeeBaseInfo.getUserId());
+
+ member.setMute(attendeeStatusInfo.getIsMute() == 1);
+ member.setHandUp(attendeeStatusInfo.getIsHandup() == 1);
+ member.setPresent(attendeeStatusInfo.getIsPresent() == 1);
+ member.setReqTalk(attendeeStatusInfo.getIsReqTalk() == 1);
+ //member.setUserId(attendeeStatusInfo);
+ TsdkConfRole role = ((attendeeBaseInfo.getRole() == TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN.getIndex()) ?
+ TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN : TsdkConfRole.TSDK_E_CONF_ROLE_ATTENDEE);
+ member.setRole(role);
+ member.setBroadcastSelf(attendeeStatusInfo.getIsBroadcast() == 1);
+
+ TsdkConfParticipantStatus participantStatus = convertAttendStatus(attendeeStatusInfo.getState());
+ if (participantStatus != null) {
+ member.setStatus(convertConfctrlParticipantStatus(participantStatus));
+ }
+ member.setSelf((attendeeStatusInfo.getIsSelf() == 1));
+
+ member.setInDataConference(attendeeStatusInfo.getIsJoinDataconf() == 1);
+ member.setPresent(attendeeStatusInfo.getIsPresent() == 1);
+ member.setIsAudio(attendeeStatusInfo.getIsAudio() == 1);
+
+ return member;
+ }
+
+ /**
+ * This method is used to convert attendee information list
+ * 转换与会者信息列表
+ *
+ * @param attendeeInfoList
+ * @return
+ */
+ public static List convertAttendeeInfoList(List attendeeInfoList) {
+ List memberList = new ArrayList<>();
+ for (TsdkAttendeeBaseInfo attendeeInfo : attendeeInfoList) {
+ Member member = new Member();
+
+ member.setNumber(attendeeInfo.getNumber());
+ member.setDisplayName(attendeeInfo.getDisplayName());
+ member.setAccountId(attendeeInfo.getAccountId());
+ member.setEmail(attendeeInfo.getEmail());
+ member.setSms(attendeeInfo.getSms());
+// member.setMute(attendeeInfo.isMute());
+ TsdkConfRole role = ((attendeeInfo.getRole() == TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN.getIndex()) ?
+ TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN : TsdkConfRole.TSDK_E_CONF_ROLE_ATTENDEE);
+ member.setRole(role);
+
+ memberList.add(member);
+ }
+ return memberList;
+ }
+
+ /**
+ * This method is used to Transform 'memberList' to 'TsdkAttendeeBaseInfo'
+ *
+ * @param memberList
+ * @return
+ */
+ public static List memberListToAttendeeList(List memberList) {
+ List attendeeList = new ArrayList<>();
+ for (Member member : memberList) {
+ TsdkBookConfAttendeeInfo confctrlAttendee = new TsdkBookConfAttendeeInfo();
+
+ confctrlAttendee.setNumber(member.getNumber());
+ confctrlAttendee.setDisplayName(member.getDisplayName());
+ String attendeeType = member.getAttendeeType();
+ if (TextUtils.isEmpty(attendeeType)) {
+ if (UIUtil.isService3()) {
+ confctrlAttendee.setOrganizationName(member.getOrganizationName());
+ confctrlAttendee.setUri(member.getUri());
+ confctrlAttendee.setNumber(member.getAccountId());
+ confctrlAttendee.setEmail(member.getEmail());
+ confctrlAttendee.setMobile(member.getMobile());
+ }
+ attendeeList.add(confctrlAttendee);
+ } else {
+// if (!attendeeType.equals(ATTENDEE_TYPE_CONF_ROOM)) {
+ if (UIUtil.isService3()) {
+ confctrlAttendee.setOrganizationName(member.getOrganizationName());
+ confctrlAttendee.setUri(member.getUri());
+ confctrlAttendee.setNumber(member.getAccountId());
+ confctrlAttendee.setEmail(member.getEmail());
+ confctrlAttendee.setMobile(member.getMobile());
+ }
+ attendeeList.add(confctrlAttendee);
+// }
+ }
+ }
+
+ return attendeeList;
+ }
+
+
+ /**
+ * This method is used to Transform 'memberList' to 'TsdkAttendeeBaseInfo'
+ *
+ * @param memberList
+ * @return
+ */
+ public static List memberListToParticipantList(List memberList) {
+ List participantList = new ArrayList<>();
+ for (Member member : memberList) {
+ TsdkBookConfParticipantInfo confctrlParticipant = new TsdkBookConfParticipantInfo();
+
+ String attendeeType = member.getAttendeeType();
+ if (!TextUtils.isEmpty(attendeeType)) {
+ confctrlParticipant.setName(member.getDisplayName());
+ confctrlParticipant.setOrganizationName(member.getOrganizationName());
+ confctrlParticipant.setUri(member.getUri());
+ confctrlParticipant.setTerminalType(member.getTerminalType());
+ confctrlParticipant.setTerminalRate(member.getTpSpeed());
+ confctrlParticipant.setEmail(member.getEmail());
+ participantList.add(confctrlParticipant);
+ }
+ }
+ return participantList;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/FloatingViewManager.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/FloatingViewManager.java
new file mode 100644
index 0000000..6255f92
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/FloatingViewManager.java
@@ -0,0 +1,174 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+
+import com.tengshisoft.chatmodule.hwclud.listener.FloatingViewListener;
+import com.tengshisoft.chatmodule.hwclud.ui.FloatingView;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 悬浮窗管理器
+ *
+ * @author PengZhenjin
+ * @date 2017-6-5
+ */
+public class FloatingViewManager implements View.OnTouchListener {
+
+ private static final String TAG = "FloatingViewManager";
+
+ /**
+ * 上下文
+ */
+ private Context mContext;
+
+ /**
+ * 悬浮窗监听器
+ */
+ private FloatingViewListener mFloatingViewListener;
+
+ /**
+ * WindowManager
+ */
+ private final WindowManager mWindowManager;
+
+ /**
+ * 悬浮窗集合
+ */
+ private List mFloatingViewList;
+
+ /**
+ * 构造方法
+ *
+ * @param context 上下文
+ * @param floatingViewListener 悬浮窗监听器
+ */
+ public FloatingViewManager(Context context, FloatingViewListener floatingViewListener) {
+ this.mContext = context;
+ this.mFloatingViewListener = floatingViewListener;
+ this.mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ this.mFloatingViewList = new ArrayList<>();
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return false;
+ }
+
+ /**
+ * 添加悬浮窗
+ *
+ * @param view 悬浮窗视图组件
+ * @param configs 悬浮窗的配置信息
+ */
+ public void addFloatingView(View view, Configs configs) {
+ // 创建悬浮窗
+ FloatingView floatingView = new FloatingView(this.mContext, configs.floatingViewX, configs.floatingViewY);
+ floatingView.setOnTouchListener(this);
+ floatingView.setOverMargin(configs.overMargin);
+ floatingView.setMoveDirection(configs.moveDirection);
+ floatingView.setAnimateInitialMove(configs.animateInitialMove);
+ // 设置悬浮窗的大小
+ FrameLayout.LayoutParams targetParams = new FrameLayout.LayoutParams(configs.floatingViewWidth, configs.floatingViewHeight);
+ view.setLayoutParams(targetParams);
+ try {
+ floatingView.addView(view);
+ } catch (Exception e) {
+ LogUtil.zzz("addFloatingView", "添加悬浮窗失败");
+ }
+
+ // 添加悬浮窗到集合
+ this.mFloatingViewList.add(floatingView);
+
+ // 添加悬浮窗
+ this.mWindowManager.addView(floatingView, floatingView.getWindowLayoutParams());
+ }
+
+ /**
+ * 移除悬浮窗
+ *
+ * @param floatingView
+ */
+ private void removeFloatingView(FloatingView floatingView) {
+ int matchIndex = this.mFloatingViewList.indexOf(floatingView);
+ if (matchIndex != -1) {
+ this.mWindowManager.removeViewImmediate(floatingView);
+ this.mFloatingViewList.remove(matchIndex);
+ }
+ if (this.mFloatingViewList.isEmpty()) {
+ if (this.mFloatingViewListener != null) {
+ this.mFloatingViewListener.onFinishFloatingView();
+ }
+ }
+ }
+
+ /**
+ * 移除所有的悬浮窗
+ */
+ public void removeAllFloatingView() {
+ if (this.mFloatingViewList != null) {
+ for (FloatingView floatingView : mFloatingViewList) {
+ this.mWindowManager.removeViewImmediate(floatingView);
+ }
+ this.mFloatingViewList.clear();
+ }
+ }
+
+ /**
+ * 悬浮窗的配置信息
+ */
+ public static class Configs {
+ /**
+ * 悬浮窗的x坐标
+ */
+ public int floatingViewX;
+
+ /**
+ * 悬浮窗的y坐标
+ */
+ public int floatingViewY;
+
+ /**
+ * 悬浮窗的宽度(单位:px)
+ */
+ public int floatingViewWidth;
+
+ /**
+ * 悬浮窗的高度(单位:px)
+ */
+ public int floatingViewHeight;
+
+ /**
+ * 悬浮窗边缘的外边距
+ */
+ public int overMargin;
+
+ /**
+ * 悬浮窗移动方向
+ */
+ @FloatingView.MoveDirection
+ public int moveDirection;
+
+ /**
+ * 悬浮窗移动时是否带动画
+ */
+ public boolean animateInitialMove;
+
+ public Configs() {
+ this.floatingViewX = FloatingView.DEFAULT_X;
+ this.floatingViewY = FloatingView.DEFAULT_Y;
+ this.floatingViewWidth = FloatingView.DEFAULT_WIDTH;
+ this.floatingViewHeight = FloatingView.DEFAULT_HEIGHT;
+ this.overMargin = 0;
+ this.moveDirection = FloatingView.MOVE_DIRECTION_DEFAULT;
+ this.animateInitialMove = true;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/ICallMgr.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/ICallMgr.java
new file mode 100644
index 0000000..eceae0d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/ICallMgr.java
@@ -0,0 +1,203 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute;
+import com.tengshisoft.chatmodule.beans.CallConstant;
+import com.tengshisoft.chatmodule.hwclud.listener.ICallNotification;
+
+/**
+ * This class is about UI call Call functions
+ * UI回调函数
+ */
+public interface ICallMgr
+{
+ /**
+ * Config call service param
+ * 配置呼叫参数
+ */
+ void configCallServiceParam();
+
+ /**
+ * Register call module UI callback
+ * @param callNotification
+ */
+ void regCallServiceNotification(ICallNotification callNotification);
+
+ /**
+ * This method is used to switching audio routing devices
+ *
+ * @return
+ */
+ TsdkMobileAuidoRoute switchAudioRoute();
+
+ /**
+ * This method is used to get mobile audio route
+ *
+ * @return the audio route
+ */
+ int getCurrentAudioRoute();
+
+ /**
+ * This method is used to get call status
+ *
+ * @param callID call id
+ * @return
+ */
+ CallConstant.CallStatus getCallStatus(int callID);
+
+
+ /**
+ * This method is used to make call or make video call
+ *
+ * @param toNumber
+ * @param isVideoCall
+ * @return int 0 success
+ */
+ int startCall(String toNumber, boolean isVideoCall,String displayName);
+
+ /**
+ * This method is used to answer incoming call
+ * @param callID
+ * @param isVideo
+ * @return true:success, false:failed
+ */
+ boolean answerCall(int callID, boolean isVideo);
+
+ /**
+ * This method is used to reject or hangup call
+ * @param callID
+ * @return true:success, false:failed
+ */
+ boolean endCall(int callID);
+
+ /**
+ * This method is used to divert incoming call
+ * @param callID
+ * @param divertNumber
+ * @return true:success, false:failed
+ */
+ boolean divertCall(int callID, String divertNumber);
+
+ /**
+ * This method is used to blind transfer call
+ * @param callID
+ * @param transferNumber
+ * @return true:success, false:failed
+ */
+ boolean blindTransfer(int callID, String transferNumber);
+
+ /**
+ * This method is used to hold call
+ * @param callID
+ * @return true:success, false:failed
+ */
+ boolean holdCall(int callID);
+
+ /**
+ * This method is used to hold the video Call
+ *
+ * @param callID
+ * @return
+ */
+ boolean holdVideoCall(int callID);
+
+ /**
+ * This method is used to unhold call
+ *
+ * @param callID
+ * @return
+ */
+ boolean unHoldCall(int callID);
+
+ /**
+ * This method is used to send DTMF tone
+ * @param callID
+ * @param code
+ * @return true:success, false:failed
+ */
+ boolean reDial(int callID, int code);
+
+ /**
+ * This method is used to request change from an audio call to a video call
+ * @param callID
+ * @return true:success, false:failed
+ */
+ boolean addVideo(int callID);
+
+ /**
+ * This method is used to request a change from a video call to an audio call
+ * @param callID
+ * @return true:success, false:failed
+ */
+ boolean delVideo(int callID);
+
+ /**
+ * This method is used to reject change from an audio call to a video call
+ * @param callID
+ * @return true:success, false:failed
+ */
+ boolean rejectAddVideo(int callID);
+
+ /**
+ * This method is used to accept change from an audio call to a video call
+ * @param callID
+ * @return true:success, false:failed
+ */
+ boolean acceptAddVideo(int callID);
+
+ /**
+ * This method is used to set whether mute the microphone
+ * @param callID
+ * @param mute
+ * @return true:success, false:failed
+ */
+ boolean muteMic(int callID, boolean mute);
+
+ /**
+ * This method is used to set media speaker mute
+ *
+ * @param callID
+ * @param mute
+ * @return
+ */
+ boolean muteSpeak(int callID, boolean mute);
+
+ /**
+ * This method is used to switch Local View
+ *
+ * @param callID
+ * @param isClose
+ */
+ void switchLocalView(int callID, boolean isClose);
+
+ /**
+ * This method is used to switch Camera
+ *
+ * @param callID
+ * @param cameraIndex
+ */
+ boolean switchCamera(int callID, int cameraIndex);
+
+ /**
+ * This method is used to play ringing tone
+ * @param ringingFile
+ */
+ void startPlayRingingTone(String ringingFile);
+
+ /**
+ * This method is used to stop play ringing tone
+ */
+ void stopPlayRingingTone();
+
+ /**
+ * This method is used to play ring back tone
+ * @param ringingFile
+ */
+ void startPlayRingBackTone(String ringingFile);
+
+ /**
+ * This method is used to stop play ring back tone
+ */
+ void stopPlayRingBackTone();
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/LdapFrontstageMgr.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/LdapFrontstageMgr.java
new file mode 100644
index 0000000..8b196b9
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/LdapFrontstageMgr.java
@@ -0,0 +1,183 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+import android.os.Build;
+import android.util.Log;
+
+import com.huawei.ecterminalsdk.base.TsdkOnEvtSearchLdapContactsResult;
+import com.huawei.ecterminalsdk.base.TsdkPageCookieData;
+import com.huawei.ecterminalsdk.base.TsdkSearchLdapContactsParam;
+import com.huawei.ecterminalsdk.base.TsdkSearchLdapContactsResult;
+import com.huawei.ecterminalsdk.models.TsdkCommonResult;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.ldap.TsdkLdapFrontstageManager;
+import com.tengshisoft.chatmodule.beans.LdapFrontstageConstant;
+import com.tengshisoft.chatmodule.hwclud.api.ILdapFrontstageNotification;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * This class is about ldap frontstage manager.
+ * ldap地址本模块管理类
+ */
+public class LdapFrontstageMgr {
+
+ private static final String TAG = LdapFrontstageMgr.class.getSimpleName();
+
+ private int lastCookieLength = 0;
+
+ /**
+ * The LdapFrontstageMgr function object.
+ * LdapFrontstageMgr对象
+ */
+ private static LdapFrontstageMgr instance;
+
+ /**
+ * UI notification.
+ * 回调接口对象
+ */
+ private HashMap notifications;
+
+ /**
+ * The TsdkLdapFrontstageManager object.
+ * TsdkLdapFrontstageManager对象
+ */
+ private TsdkLdapFrontstageManager tsdkLdapFrontstageManager;
+
+ private String mTag;
+ /**
+ * This is a constructor of this class
+ * 构造函数
+ */
+ public LdapFrontstageMgr() {
+ if (TsdkManager.getInstance() == null) {
+ Log.e(TAG, "LdapFrontstageMgr: TsdkManager.getInstance() == null");
+ return;
+ }
+ tsdkLdapFrontstageManager = TsdkManager.getInstance().getLdapFrontstageManager();
+ }
+
+ /**
+ * This method is used to get instance object of EnterpriseAddressBookMgr.
+ * 获取EnterpriseAddressBookMgr对象实例
+ *
+ * @return EnterpriseAddressBookMgr Return instance object of EnterpriseAddressBookMgr
+ * 返回一个对象的示例
+ */
+ public static LdapFrontstageMgr getInstance() {
+ if (instance == null) {
+ instance = new LdapFrontstageMgr();
+ }
+ return instance;
+ }
+
+ /**
+ * This method is used to register EnterpriseAddressBookMgr module UI callback.
+ * 注册回调
+ *
+ * @param notification
+ */
+ public void registerNotification(ILdapFrontstageNotification notification,String tag) {
+ LogUtil.zzz(TAG, "tag === "+tag);
+ if (notifications == null) notifications = new HashMap<>();
+ notifications.put(tag,notification);
+ }
+
+ public void unregisterNotification(ILdapFrontstageNotification notification,String tag) {
+ if (notifications == null || notifications.size() == 0) return;
+ notifications.remove(tag);
+ }
+
+ /**
+ * This method is used to search contact's information.
+ * 获取联系人的信息
+ *
+ * @param keyWords Indicates keyWords
+ * 搜索条件
+ * @return int Return seq
+ * 返回查询的序列号
+ */
+ public int searchLdapContacts(String keyWords, String currentBaseDN, String sortAttribute, int searchSingleLevel,
+ int pageSize, int cookieLength, ArrayList pageCookie) {
+ return searchLdapContacts(keyWords, currentBaseDN, sortAttribute, searchSingleLevel, pageSize, cookieLength, pageCookie, null);
+ }
+
+ public int searchLdapContacts(String keyWords, String currentBaseDN, String sortAttribute, int searchSingleLevel,
+ int pageSize, int cookieLength, ArrayList pageCookie, String tag) {
+ if (null == keyWords) {
+ LogUtil.zzz(TAG, "Search condition is empty");
+ }
+ LogUtil.zzz(TAG, "Search keyWords " + keyWords);
+ TsdkSearchLdapContactsParam searchLdapContactsParam = new TsdkSearchLdapContactsParam();
+ searchLdapContactsParam.setKeywords(keyWords);
+ searchLdapContactsParam.setCurrentBaseDN(currentBaseDN);
+ searchLdapContactsParam.setSortAttribute(sortAttribute);
+ searchLdapContactsParam.setSearchSingleLevel(searchSingleLevel);
+ searchLdapContactsParam.setPageSize(pageSize);
+ searchLdapContactsParam.setCookieLength(cookieLength);
+ searchLdapContactsParam.setPageCookie(pageCookie);
+ lastCookieLength = cookieLength;
+ int result = -1;
+ if (null!=tsdkLdapFrontstageManager){
+ mTag = tag;
+ result = tsdkLdapFrontstageManager.searchLdapContacts(searchLdapContactsParam);
+ }
+ LogUtil.zzz(TAG, "searchResult -->" + result);
+ return result;
+ }
+
+ /**
+ * This method is used to get search contacts result.
+ * 查询联系人信息返回结果
+ *
+ * @param result Indicates search result
+ * 查询结果
+ * @param searchParamResult Indicates search contact information
+ * 查询到的联系人信息
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void handleSearchLdapContactResult(TsdkCommonResult result, TsdkOnEvtSearchLdapContactsResult.Param searchParamResult) {
+ int ret = result.getResult();
+ //List contactSections = new ArrayList<>();
+ //c层返回boolean 1true 0false 获取联系人成功返回1
+ if (ret == 1) {
+ TsdkSearchLdapContactsResult searchResultData = searchParamResult.getSearchResultData();
+ if (searchResultData != null) {
+ int currentCount = searchParamResult.getSearchResultData().getCurrentCount();
+ //查询到0个联系人
+ if (0 == currentCount && lastCookieLength == 0) {
+ sendNotifications(LdapFrontstageConstant.Event.SEARCH_CONTACTS_NOT_FOUND, null);
+ } else {
+ sendNotifications(LdapFrontstageConstant.Event.SEARCH_CONTACTS_COMPLETE, searchParamResult);
+ }
+ LogUtil.zzz(TAG, currentCount + " Get the current number of returned contacts");
+ } else {
+ sendNotifications(LdapFrontstageConstant.Event.SEARCH_CONTACTS_FAILED, null);
+ LogUtil.zzz(TAG, "Search contacts failed, searchResultData is null");
+ }
+ } else {
+ LogUtil.zzz(TAG, "Search contacts failed, result.result 1true 0false-->" + result.result);
+ LogUtil.zzz(TAG, "Search contacts failed, result.reasonDescription -->" + result.reasonDescription);
+ sendNotifications(LdapFrontstageConstant.Event.SEARCH_CONTACTS_FAILED, null);
+ }
+ }
+
+ /**
+ * 发送查询结果
+ * @param event 获取联系人的结果事件
+ * @param object 获取到的具体信息
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void sendNotifications(LdapFrontstageConstant.Event event, Object object){
+ if (notifications != null && notifications.size() > 0) {
+ notifications.forEach((key,value) ->{
+ if (key.equals(mTag)) {
+ value.onEntLdapFrontStageNotify(event, object, mTag);
+ }
+ });
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/LoginMangerV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/LoginMangerV2.java
new file mode 100644
index 0000000..6d36b92
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/LoginMangerV2.java
@@ -0,0 +1,415 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+
+import android.os.Build;
+import android.text.TextUtils;
+
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkAuthType;
+import com.huawei.ecterminalsdk.base.TsdkFailedInfo;
+import com.huawei.ecterminalsdk.base.TsdkForceLogoutInfo;
+import com.huawei.ecterminalsdk.base.TsdkLocalAddress;
+import com.huawei.ecterminalsdk.base.TsdkLoginFailedInfo;
+import com.huawei.ecterminalsdk.base.TsdkLoginParam;
+import com.huawei.ecterminalsdk.base.TsdkLoginSuccessInfo;
+import com.huawei.ecterminalsdk.base.TsdkLogoutSuccessInfo;
+import com.huawei.ecterminalsdk.base.TsdkServerType;
+import com.huawei.ecterminalsdk.base.TsdkServiceAccountType;
+import com.huawei.ecterminalsdk.base.TsdkUserInfoParam;
+import com.huawei.ecterminalsdk.base.TsdkVoipAccountInfo;
+import com.huawei.ecterminalsdk.models.TsdkCommonResult;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.ServiceSettingBeanV2;
+import com.tengshisoft.chatmodule.hwclud.api.LoginView;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.listener.LoginEventNotifyUi;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.serivce.ServiceManger;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.DeviceManager;
+import com.tengshisoft.chatmodule.hwclud.utils.ImportFileUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.SdkHelpUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.ThreadHelper;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.constant.PathConfig;
+import com.tenlionsoft.baselib.core.retrofit_net.BaseUrlApi;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import androidx.annotation.RequiresApi;
+
+import static com.huawei.ecterminalsdk.base.TsdkServiceAccountType.TSDK_E_VOIP_SERVICE_ACCOUNT;
+import static com.tengshisoft.chatmodule.hwclud.utils.LocContext.getString;
+
+/**
+ * This class is about login manager
+ * 登录管理类
+ */
+public class LoginMangerV2 {
+
+ private static final String TAG = LoginMangerV2.class.getSimpleName();
+
+ /**
+ * Login manager instance
+ * 登录管理实例
+ */
+ private static LoginMangerV2 instance;
+
+ /**
+ * 3.0用户信息
+ */
+ public TsdkUserInfoParam userInfoParam;
+
+ /**
+ * UI callback
+ * UI回调
+ */
+ private LoginEventNotifyUi loginEventNotifyUI;
+
+ /**
+ * Force exit
+ * 强制退出
+ */
+ private boolean isForceLogout = false;
+
+ private String account = "";
+
+ private String terminal;
+
+ private String terminalType;
+
+ private String sipNumber;
+
+ private String displayName;
+
+ private String deptName;
+
+ private LoginMangerV2() {
+ }
+
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void checkLogin(String name, String pwd, LoginView view) {
+ if (null != TsdkManager.getInstance()) {
+ boolean certificate = SdkHelpUtil.certificate();
+ if (certificate) {
+ doLogin(name, pwd, view);
+ } else {
+ ToastUtils.show("证书已经过期,请联系管理员更换.");
+ }
+ } else {
+ ToastUtils.show("通话控件正在初始化,请稍后重试");
+ SdkHelpUtil.initSdk();
+ }
+ }
+
+ /**
+ * 登录华为融合通信
+ *
+ * @param name
+ * @param pwd
+ */
+ public void doLogin(String name, String pwd, LoginView view) {
+ String address = BaseUrlApi.HW_CLOUD_URL;
+ int port = BaseUrlApi.HW_CLOUD_PORT;
+ int transport = 1;
+ if (TextUtils.isEmpty(address) || port == -1) {
+ ToastUtils.show(R.string.cloudLink_inputAddressInfoNull);
+ return;
+ }
+ if (TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd)) {
+ ToastUtils.show(R.string.cloudLink_login_inputPersonInfoNull);
+ return;
+ }
+ ServiceManger.getServiceMgr().securityParam(ServiceSettingBeanV2.getInstance()
+ .getRtpMode(), ServiceSettingBeanV2.getInstance().getBfpTransportMode());
+ String ipAddress = BaseUrlApi.HW_CLOUD_URL;
+ ServiceManger.getServiceMgr().networkParam(port + "", ipAddress, transport);
+ String sipUrl = EncryptedSPTool.getString(Constant.LOGIN_SERVER_SIP_URL);
+ if (null != TsdkManager.getInstance()) {
+ if (ipAddress != null && UIUtil.isIPv6(ipAddress)) {
+ // 设置IPV6
+ String localIpAddressIpV6 = DeviceManager.getLocalIpAddressIPV6();
+ TsdkLocalAddress localAddressIpV6 = new TsdkLocalAddress(localIpAddressIpV6);
+ LogUtil.zzz(TAG, "TsdkLocalAddress", BaseAppContext.getInstance().getString(
+ R.string.cloudLink_login_log_set_ip_6),
+ LogUtil.commonDisplay(localAddressIpV6.getIpAddress()));
+ TsdkManager.getInstance().setConfigParam(localAddressIpV6);
+ } else {
+ // 设置IPV4
+ String localIpAddress = DeviceManager.getLocalIpAddress(false);
+ TsdkLocalAddress localAddress = new TsdkLocalAddress(localIpAddress);
+ LogUtil.zzz(TAG, "TsdkLocalAddress", BaseAppContext.getInstance().getString(R.string.cloudLink_login_log_set_ip_4),
+ LogUtil.commonDisplay(localAddress.getIpAddress()));
+ TsdkManager.getInstance().setConfigParam(localAddress);
+ }
+ }
+ if (view != null) {
+ view.loginIng();
+ }
+ TsdkLoginParam tsdkLoginParam = new TsdkLoginParam();
+ tsdkLoginParam.setUserId(1);
+ tsdkLoginParam.setAuthType(TsdkAuthType.TSDK_E_AUTH_NORMAL.getIndex());
+ tsdkLoginParam.setUserName(name);
+ tsdkLoginParam.setPassword(pwd);
+ tsdkLoginParam.setSipUri(sipUrl);
+ tsdkLoginParam.setServerType(TsdkServerType.TSDK_E_SERVER_TYEP_SMC.getIndex());
+ tsdkLoginParam.setUserTicket("");
+ LogUtils.e("tsdkLoginParam:" + tsdkLoginParam.toString());
+ ThreadHelper.INST.execute(ImportFileUtil::importFile);
+ if (null != TsdkManager.getInstance()) {
+ int result = TsdkManager.getInstance().getLoginManager().login(tsdkLoginParam);
+ LogUtils.e("登录返回的状态码===" + result);
+ if (result != 0) {
+ if (view != null) {
+ view.loginFail();
+ }
+ }
+ }
+ }
+
+ /**
+ * [en]This method is used to get login manager instance
+ * [cn]获取一个登录管理类的实例对象
+ *
+ * @return LoginManger : login manager instance
+ * 返回一个登录管理类实例对象
+ */
+ public static LoginMangerV2 getInstance() {
+ if (instance == null) {
+ instance = new LoginMangerV2();
+ }
+ return instance;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDeptName() {
+ return deptName;
+ }
+
+ public void setDeptName(String deptName) {
+ this.deptName = deptName;
+ }
+
+ public boolean isForceLogout() {
+ return isForceLogout;
+ }
+
+ /**
+ * This method is used to registered login callback
+ * 注册UI回调函数
+ *
+ * @param notify : UI callback
+ */
+ public void regLoginEventNotification(LoginEventNotifyUi notify) {
+ this.loginEventNotifyUI = notify;
+ }
+
+ /**
+ * [en]This method is used to handle the success login
+ * [cn]处理登录成功事件
+ *
+ * @param userId [en]Indicates user id
+ * [cn]用户标识
+ * @param serviceAccountType [en]Indicates service account type
+ * [cn]VOIP/im登录帐号类型
+ * @param loginSuccessInfo [en]Indicates login success info
+ * [cn]登陆成功的相关信息
+ */
+ public void handleLoginSuccess(int userId, TsdkServiceAccountType serviceAccountType,
+ TsdkLoginSuccessInfo loginSuccessInfo) {
+ LogUtils.d("handleLoginSuccess");
+ MeetingController.getInstance().setLogin(true);
+ if (TSDK_E_VOIP_SERVICE_ACCOUNT == serviceAccountType) {
+ EncryptedSPTool.putInt(Constant.SERVICE_TYPE, loginSuccessInfo.getLoginServerType());
+ EncryptedSPTool.putString(Constant.USER_TYPE, loginSuccessInfo.getUserType());
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.VOIP_LOGIN_SUCCESS, userId,
+ loginSuccessInfo.getPasswordExpire() + "");
+ }
+ }
+
+ /**
+ * @param loginFailedInfo [en]Indicates information about login success
+ * [cn]登录失败信息
+ */
+ public void handleLoginFailed(TsdkLoginFailedInfo loginFailedInfo) {
+ LogUtils.d("handleLoginFailed");
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.LOGIN_FAILED,
+ loginFailedInfo.getReasonCode(), loginFailedInfo.getResidualRetryTimes() + "");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CHANGE_PWD_CLOSE,
+ loginFailedInfo.getReasonCode());
+ }
+
+ /**
+ * [en]This method is used to handle the failed authentication.
+ * [cn]处理鉴权失败事件
+ *
+ * @param result [en]Indicates response results
+ * [cn]响应结果
+ */
+ public void handleAuthFailed(TsdkCommonResult result) {
+ LogUtils.d("handleAuthFailed result " + result);
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.AUTH_FAILED,
+ result.getResult(), result.getReasonDescription());
+ }
+
+ /**
+ * [en]This method is used to handle the voip account status
+ * [cn]处理voip状态事件
+ *
+ * @param voipAccountInfo [en]voip account info
+ * [cn]voip帐号信息
+ */
+ public void handleVoipAccountStatus(TsdkVoipAccountInfo voipAccountInfo) {
+ LogUtils.d("handleVoipAccountStatus");
+ String number = voipAccountInfo.getNumber();
+ this.account = voipAccountInfo.getAccount();
+ this.sipNumber = number;
+
+ EncryptedSPTool.putString(Constant.SIPNUMBER, number);
+ // 保存帐号与终端号 防止onEvtGetUserInfoResult接口数据返回失败
+ EncryptedSPTool.putString(Constant.MINE_USER_NAME, voipAccountInfo.getAccount());
+ LoginMangerV2.getInstance().setDisplayName(voipAccountInfo.getAccount());
+ LoginMangerV2.getInstance().setTerminal(voipAccountInfo.getNumber());
+ }
+
+ public void handleLogoutFirstChangePwd(TsdkCommonResult tsdkCommonResult) {
+ LogUtils.d("handleLogoutFirstChangePwd");
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.FIRST_CHANGE_PWD,
+ tsdkCommonResult.result, "first login change password");
+ }
+
+ /**
+ * [en]This method is used to handle the success logout
+ * [cn]处理登出成功事件
+ *
+ * @param serviceAccountType [en]Indicates service account type
+ * [cn]VOIP/im登录帐号类型
+ */
+ public void handleLogoutSuccess(TsdkServiceAccountType serviceAccountType,
+ TsdkLogoutSuccessInfo tsdkLogoutSuccessInfo) {
+ LogUtils.d("handleLogoutSuccess");
+ MeetingController.getInstance().setLogin(false);
+ if (isForceLogout) {
+ isForceLogout = false;
+ } else if (tsdkLogoutSuccessInfo != null) {
+ MeetingMgrV2.getInstance().setDisconnection(true);
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.LOGOUT,
+ serviceAccountType.getIndex(),
+ BaseAppContext.getInstance().getResources().getString(R.string.cloudLink_loginout_disconnection));
+ } else {
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.LOGOUT,
+ serviceAccountType.getIndex(),
+ String.valueOf(serviceAccountType.getIndex()));
+ }
+ }
+
+ public void handleLogoutFirstChangePwdResult(TsdkFailedInfo tsdkFailedInfo, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "handleLogoutFirstChangePwd");
+ if (null != tsdkFailedInfo) {
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.CHANGE_PWD,
+ tsdkFailedInfo.getReasonCode(), tsdkFailedInfo.getResidualRetryTimes() + "");
+ } else {
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.CHANGE_PWD, result.getResult(), " ");
+ }
+ }
+
+ public void handleAuthRefreshFailed(TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "handleAuthRefreshFailed result " + result);
+ this.logout();
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.LOGOUT, 0, result.getReasonDescription());
+ }
+
+ public String getAccount() {
+ return account;
+ }
+
+ /**
+ * This method is used to get sip number
+ * 获取sip号码或者终端号
+ *
+ * @return String : sip number or terminal
+ * 返回sip号码或者终端号
+ */
+ public String getTerminal() {
+ if (TextUtils.isEmpty(terminal)) {
+ if (TextUtils.isEmpty(sipNumber)) {
+ return "";
+ } else {
+ return sipNumber;
+ }
+ }
+ return terminal;
+ }
+
+ /**
+ * [en]This method is used to handle the force logout
+ * [cn]处理强制登出事件
+ *
+ * @param forceLogoutInfo
+ */
+ public void handleForceLogout(TsdkForceLogoutInfo forceLogoutInfo) {
+ LogUtil.zzz(TAG, "handleForceLogout");
+ isForceLogout = true;
+ this.logout();
+ String des = "";
+ switch (forceLogoutInfo.getReason()) {
+ case ConstantsV2.CONSTANTSV2_ZERO:
+ des = getString(R.string.cloudLink_login_loginRepeat);
+ break;
+ case ConstantsV2.CONSTANTSV2_ONE:
+ des = getString(R.string.cloudLink_theAccountIsStoppedByTheServer);
+ break;
+ case ConstantsV2.CONSTANTSV2_TWO:
+ des = getString(R.string.cloudLink_accountExpired);
+ break;
+ default:
+ break;
+ }
+ this.loginEventNotifyUI.onLoginEventNotify(Constant.LoginUIEvent.LOGOUT, 0, des);
+ }
+
+
+ public void setTerminal(String terminal) {
+ this.terminal = terminal;
+ }
+
+ public String getTerminalType() {
+ return terminalType;
+ }
+
+ public void setTerminalType(String terminalType) {
+ this.terminalType = terminalType;
+ }
+
+ public int logout() {
+ if (TsdkManager.getInstance() == null) {
+ return 0;
+ } else {
+ int ret = TsdkManager.getInstance().getLoginManager().logout();
+ if (ret != 0) {
+ LogUtil.zzz(TAG, "logout is failed, ret " + ret);
+ } else {
+ userInfoParam = null;
+ account = null;
+ sipNumber = null;
+ terminal = null;
+ displayName = null;
+ deptName = null;
+ }
+ return ret;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/MeetingMgrV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/MeetingMgrV2.java
new file mode 100644
index 0000000..323d0ba
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/MeetingMgrV2.java
@@ -0,0 +1,2262 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+import android.annotation.SuppressLint;
+import android.media.projection.MediaProjection;
+import android.os.Build;
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.base.TsdkAddAttendeesInfo;
+import com.huawei.ecterminalsdk.base.TsdkAttendee;
+import com.huawei.ecterminalsdk.base.TsdkAttendeeBaseInfo;
+import com.huawei.ecterminalsdk.base.TsdkBookConfAttendeeInfo;
+import com.huawei.ecterminalsdk.base.TsdkBookConfInfo;
+import com.huawei.ecterminalsdk.base.TsdkBookConfParticipantInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfAttendeeInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfAttendeeUpdateType;
+import com.huawei.ecterminalsdk.base.TsdkConfBaseInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfEnvType;
+import com.huawei.ecterminalsdk.base.TsdkConfJoinParam;
+import com.huawei.ecterminalsdk.base.TsdkConfLanguage;
+import com.huawei.ecterminalsdk.base.TsdkConfListInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfMediaType;
+import com.huawei.ecterminalsdk.base.TsdkConfOperationResult;
+import com.huawei.ecterminalsdk.base.TsdkConfOperationType;
+import com.huawei.ecterminalsdk.base.TsdkConfParam;
+import com.huawei.ecterminalsdk.base.TsdkConfRole;
+import com.huawei.ecterminalsdk.base.TsdkConfSpeaker;
+import com.huawei.ecterminalsdk.base.TsdkConfSpeakerInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfType;
+import com.huawei.ecterminalsdk.base.TsdkJoinConfIndInfo;
+import com.huawei.ecterminalsdk.base.TsdkNotifyHandUpAttendee;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtBroadcastInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtChcekConfpwdExistedResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtConfBaseInfoInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtVerifyConfPwdResult;
+import com.huawei.ecterminalsdk.base.TsdkQueryConfListReq;
+import com.huawei.ecterminalsdk.base.TsdkSubtitleContentInfo;
+import com.huawei.ecterminalsdk.base.TsdkSubtitleService;
+import com.huawei.ecterminalsdk.base.TsdkSvcWatchInfo;
+import com.huawei.ecterminalsdk.base.TsdkTimeZoneInfoList;
+import com.huawei.ecterminalsdk.base.TsdkVmrInfo;
+import com.huawei.ecterminalsdk.base.TsdkWatchList;
+import com.huawei.ecterminalsdk.base.TsdkWatchSvcAttendees;
+import com.huawei.ecterminalsdk.base.TsdkWatchSvcAttendeesInfo;
+import com.huawei.ecterminalsdk.base.TsdkonEvtAuditDir;
+import com.huawei.ecterminalsdk.models.TsdkCommonResult;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.call.TsdkCallManager;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.BookConferenceInfo;
+import com.tengshisoft.chatmodule.beans.ConfBaseInfo;
+import com.tengshisoft.chatmodule.beans.ControlOperationsResults;
+import com.tengshisoft.chatmodule.beans.Session;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.listener.IConfNotification;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.utils.AppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.ListTools;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.TimerUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConfConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Optional;
+
+import androidx.annotation.RequiresApi;
+
+import static com.huawei.ecterminalsdk.base.TsdkConfEnvType.TSDK_E_CONF_ENV_HOSTED_CONVERGENT_CONFERENCE;
+import static com.huawei.ecterminalsdk.base.TsdkConfRecordStatus.TSDK_E_CONF_RECORD_START;
+import static com.huawei.ecterminalsdk.base.TsdkConfRecordStatus.TSDK_E_CONF_RECORD_STOP;
+import static com.huawei.ecterminalsdk.base.TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN;
+import static com.tengshisoft.chatmodule.hwclud.utils.LocContext.getString;
+
+/**
+ * This class is about meeting function management.
+ * 会议服务管理类
+ */
+public class MeetingMgrV2 {
+
+ private static final String TAG = MeetingMgrV2.class.getSimpleName();
+ /**
+ * 线程延迟时间
+ */
+ private static final int DELAY_TIME = 500;
+ private static final String VERSION_TWO = "2";
+ private static final String VERSION_THREE = "3";
+
+ private static MeetingMgrV2 mInstance;
+
+ /**
+ * UI回调
+ */
+ private IConfNotification mConfNotification;
+
+ public IConfNotification getConfNotification() {
+ return mConfNotification;
+ }
+
+ public TsdkConference getCurrentConference() {
+ return currentConference;
+ }
+
+ /**
+ * 当前正在召开的会议
+ */
+ private TsdkConference currentConference;
+
+ /**
+ * 会议基础信息
+ */
+ private ConfBaseInfo confBaseInfo;
+
+ private Member self;
+
+ /**
+ * 组网模式,为适配SMC组网配置
+ */
+ private TsdkConfEnvType confEnvType;
+
+ /**
+ * 会议协议类型
+ */
+ private ConfConstant.ConfProtocol confProtocol;
+
+ /**
+ * 时区列表
+ */
+ private TsdkTimeZoneInfoList tsdkTimeZoneInfoList;
+
+ /**
+ * 出席者
+ */
+ private Member selfAttendee;
+
+ /**
+ * 声音最大的人
+ */
+ private Member speakMember;
+
+ /**
+ * 会议详情
+ */
+ private TsdkOnEvtConfBaseInfoInd baseInfoInd;
+
+ private MediaProjection mediaProjection;
+
+ /**
+ * 与会者列表
+ */
+ private List memberList;
+
+ private List watchMember = new ArrayList<>();
+
+ /**
+ * 共享文档和白板的id
+ */
+ private List documentId = new ArrayList<>();
+
+ /**
+ * 会议中的发言人
+ */
+ private String[] speakers;
+
+ /**
+ * 会议ID
+ */
+ private String meetingId;
+
+ /**
+ * 创会发起者
+ */
+ private String meetingScheduserAccount;
+
+ private boolean isJoinConf = false;
+
+ public boolean isJoinConf() {
+ return isJoinConf;
+ }
+
+ /**
+ * SMC组网下静音会场标识
+ */
+ private boolean isMuteConf;
+
+ /**
+ * 是否是匿名入会,用于判断会议界面按钮的显示
+ */
+ private boolean isAnonymous = false;
+
+ /**
+ * 获取匿名会议临时帐号是否成功,用于判断会议界面按钮的显示
+ */
+ private boolean getTempUserSuccess = false;
+
+ /**
+ * 是否是通话转会议
+ */
+ private boolean callTransferToConference = false;
+
+ /**
+ * 是否是单向直播会议
+ */
+ private boolean flag = false;
+
+ /**
+ * 是否断网异常情况
+ */
+ private boolean isDisconnection = false;
+
+ public boolean isFlag() {
+ return flag;
+ }
+
+ public void setFlag(boolean flag) {
+ this.flag = flag;
+ }
+
+ public boolean isDisconnection() {
+ return isDisconnection;
+ }
+
+ public void setDisconnection(boolean disconnection) {
+ isDisconnection = disconnection;
+ }
+
+ public String getMeetingId() {
+ return meetingId;
+ }
+
+ public void setMeetingId(String meetingId) {
+ this.meetingId = meetingId;
+ }
+
+ public String getMeetingScheduserAccount() {
+ return meetingScheduserAccount;
+ }
+
+ public void setMeetingScheduserAccount(String meetingScheduserAccount) {
+ this.meetingScheduserAccount = meetingScheduserAccount;
+ }
+
+ public TsdkTimeZoneInfoList getTsdkTimeZoneInfoList() {
+ return tsdkTimeZoneInfoList;
+ }
+
+ public TsdkOnEvtConfBaseInfoInd getBaseInfoInd() {
+ return baseInfoInd;
+ }
+
+ public void setBaseInfoInd(TsdkOnEvtConfBaseInfoInd baseInfoInd) {
+ this.baseInfoInd = baseInfoInd;
+ }
+
+ private boolean mIsAuxData = false;
+
+ public boolean isAuxData() {
+ return mIsAuxData;
+ }
+
+ public void setIsAuxData(boolean isAuxData) {
+ this.mIsAuxData = isAuxData;
+ }
+
+ private MeetingMgrV2() {
+ this.confBaseInfo = new ConfBaseInfo();
+ }
+
+ public static MeetingMgrV2 getInstance() {
+ if (mInstance == null) {
+ mInstance = new MeetingMgrV2();
+ }
+ return mInstance;
+ }
+
+ public void regConfServiceNotification(IConfNotification confNotification) {
+ this.mConfNotification = confNotification;
+ }
+
+ public int getCurrentConferenceCallID() {
+ if (currentConference == null) {
+ return 0;
+ }
+
+ TsdkCall tsdkCall = currentConference.getCall();
+ if (tsdkCall != null) {
+ return tsdkCall.getCallInfo().getCallId();
+ } else {
+ return 0;
+ }
+ }
+
+ public List getCurrentConferenceMemberList() {
+ if (currentConference == null) {
+ return Collections.emptyList();
+ }
+ return getMemberList();
+ }
+
+ public Member getCurrentConferenceSelf() {
+ if (currentConference == null) {
+ return null;
+ }
+ return getSelf();
+ }
+
+ public ConfBaseInfo getCurrentConferenceBaseInfo() {
+ if (currentConference == null) {
+ return null;
+ }
+ return this.getConfBaseInfo();
+ }
+
+ /**
+ * 语音激励获取声音最大的人的信息
+ *
+ * @return
+ */
+ public Member getSpeakMember() {
+ return speakMember;
+ }
+
+ public Member getSelf() {
+ return self;
+ }
+
+ public void setSelf(Member self) {
+ this.self = self;
+ }
+
+
+ public List getMemberList() {
+ if (memberList == null) {
+ return Collections.EMPTY_LIST;
+ }
+ return memberList;
+ }
+
+ public void setMemberList(List memberList) {
+ this.memberList = memberList;
+ }
+
+
+ public ConfBaseInfo getConfBaseInfo() {
+ return confBaseInfo;
+ }
+
+ public void setConfBaseInfo(ConfBaseInfo confBaseInfo) {
+ this.confBaseInfo = confBaseInfo;
+ }
+
+
+ private boolean judgeMemberWhetherOnline(Member member, List attendeeList) {
+ for (TsdkAttendee attendeeInfo : attendeeList) {
+ if (member.getNumber().equals(attendeeInfo.getBaseInfo().getNumber())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public TsdkConfEnvType getConfEnvType() {
+ return confEnvType;
+ }
+
+ public void setConfEnvType(TsdkConfEnvType confEnvType) {
+ this.confEnvType = confEnvType;
+ }
+
+ public ConfConstant.ConfProtocol getConfProtocol() {
+ return confProtocol;
+ }
+
+ public void setConfProtocol(ConfConstant.ConfProtocol confProtocol) {
+ this.confProtocol = confProtocol;
+ }
+
+ public boolean isAnonymous() {
+ return isAnonymous;
+ }
+
+ public void setAnonymous(boolean anonymous) {
+ isAnonymous = anonymous;
+ }
+
+ public boolean isGetTempUserSuccess() {
+ return getTempUserSuccess;
+ }
+
+ public void setGetTempUserSuccess(boolean getTempUserSuccess) {
+ this.getTempUserSuccess = getTempUserSuccess;
+ }
+
+ public String[] getSpeakers() {
+ if (speakers != null) {
+ return speakers.clone();
+ }
+
+ return new String[0];
+ }
+
+ /**
+ * This method is used to update conf info
+ * 更新会议信息
+ *
+ * @param conference 会议信息
+ */
+ public void updateConfInfo(TsdkConference conference) {
+ LogUtils.d("updateConfInfo");
+ this.confEnvType = conference.getConfEnvType();
+ confBaseInfo.setSize(conference.getSize());
+ confBaseInfo.setConfID(conference.getConfId());
+ confBaseInfo.setSubject(conference.getSubject());
+ confBaseInfo.setSchedulerName(conference.getScheduserName());
+ confBaseInfo.setConfState(ConfConvertUtil.convertConfctrlConfState(conference.getConfState()));
+ confBaseInfo.setMediaType(conference.getConfMediaType());
+ confBaseInfo.setLock(conference.isLock());
+ confBaseInfo.setRecord(conference.isRecord());
+ confBaseInfo.setSupportRecord(conference.isSupportRecordBroadcast());
+ confBaseInfo.setRemainTime(conference.getRemainTime());
+ if (TSDK_E_CONF_ENV_HOSTED_CONVERGENT_CONFERENCE == conference.getConfEnvType()) {
+ confBaseInfo.setMuteAll(conference.isAllMute());
+ } else {
+ confBaseInfo.setMuteAll(this.isMuteConf);
+ }
+ if (memberList == null) {
+ memberList = new ArrayList<>();
+ }
+ List members = new ArrayList<>();
+ upMemberList(conference, members);
+ }
+
+ /**
+ * This method is used to book instant conference or reserved conference
+ *
+ * @param bookConferenceInfo 创会信息
+ * @return
+ */
+ public int bookConference(BookConferenceInfo bookConferenceInfo) {
+ LogUtils.d("bookConference");
+ if (bookConferenceInfo == null) {
+ LogUtils.d("bookConference bookConferenceInfo is null");
+ return -1;
+ }
+ TsdkBookConfInfo bookConfInfo = new TsdkBookConfInfo();
+ bookConfInfo.setIsAutoProlong(0);
+ if (bookConferenceInfo.isInstantConference()) {
+ bookConfInfo.setConfType(TsdkConfType.TSDK_E_CONF_INSTANT);
+ } else {
+ bookConfInfo.setConfType(TsdkConfType.TSDK_E_CONF_RESERVED);
+ }
+ if (bookConferenceInfo.getIs_auto()) {
+ bookConfInfo.setIsAutoRecord(1);
+ } else {
+ bookConfInfo.setIsAutoRecord(0);
+ }
+ bookConfInfo.setIsMultiStreamConf(1);
+ bookConfInfo.setSubject(bookConferenceInfo.getSubject());
+ bookConfInfo.setConfMediaType(bookConferenceInfo.getMediaType());
+ bookConfInfo.setConfMediaTypeV3(bookConferenceInfo.getMediaTypeV3());
+ bookConfInfo.setStartTime(bookConferenceInfo.getStartTime());
+ bookConfInfo.setDuration(bookConferenceInfo.getDuration());
+ bookConfInfo.setRecordMode(bookConferenceInfo.getRecordType());
+ bookConfInfo.setConfPassword(bookConferenceInfo.getConfPassword());
+ bookConfInfo.setChairmanPwd(bookConferenceInfo.getChairmanPwd());
+ bookConfInfo.setTimeZoneId(bookConferenceInfo.getTimeZoneId());
+ bookConfInfo.setTimeOffset(bookConferenceInfo.getTimeOffset());
+ bookConfInfo.setVmrNumber(bookConferenceInfo.getVmrNumber());
+ TsdkSubtitleService subtitleService = new TsdkSubtitleService();
+ subtitleService.setIsSubtitleEnable(bookConferenceInfo.getSupportSubtitle());
+ subtitleService.setSrcLang(bookConferenceInfo.getSupportSrcLang());
+ bookConfInfo.setSubtitleService(subtitleService);
+ if (UIUtil.isService3()) {
+ List participantList =
+ ConfConvertUtil.memberListToParticipantList(bookConferenceInfo.getMemberList());
+ bookConfInfo.setParticipantList(participantList);
+ bookConfInfo.setParticipantNum(participantList.size());
+ } else {
+ List attendeeList =
+ ConfConvertUtil.memberListToAttendeeList(bookConferenceInfo.getMemberList());
+ bookConfInfo.setAttendeeList(attendeeList);
+ bookConfInfo.setAttendeeNum(attendeeList.size());
+ }
+ bookConfInfo.setIsAutoMute(0);
+ bookConfInfo.setIsAutoEnd(0);
+ // 其他参数可选,使用默认值即可
+ bookConfInfo.setLanguage(TsdkConfLanguage.TSDK_E_CONF_LANGUAGE_DEFAULT);
+ bookConfInfo.setIsHdConf(1);
+ LogUtils.d("bookConference subject:" +
+ bookConfInfo.getSubject(),
+ "timeZoneId:" + bookConfInfo.getTimeZoneId(),
+ "startTime:" + bookConfInfo.getStartTime(),
+ "chairmanPwd:" + bookConfInfo.getChairmanPwd(),
+ "confPassword:" + bookConfInfo.getConfPassword(),
+ "duration:" + bookConfInfo.getDuration(),
+ "vmrNumber" + bookConferenceInfo.getVmrNumber());
+ if (TsdkManager.getInstance() == null ||
+ TsdkManager.getInstance().getConferenceManager() == null) {
+ LogUtils.d("bookConference TsdkManager or TsdkConferenceManager is null");
+ return -1;
+ }
+ int result = TsdkManager.getInstance().getConferenceManager().bookConference(bookConfInfo);
+ LogUtils.d("bookConference result", result);
+ return result;
+ }
+
+ /**
+ * 取消会议
+ */
+ public int cancelConference(String confId) {
+ LogUtils.d("cancelConference");
+ int result;
+ if (TsdkManager.getInstance() != null) {
+ result = TsdkManager.getInstance().getConferenceManager().cancelConference(confId);
+ } else {
+ result = 0;
+ }
+ LogUtils.d("cancelConference result " + result);
+ return result;
+ }
+
+
+ /**
+ * This method is used to query my conference list
+ * 查询会议列表
+ *
+ * @param myRight 会议类型
+ * @return
+ */
+ @SuppressLint("LongLogTag")
+ public int queryMyConfList(ConfConstant.ConfRight myRight, int page) {
+ TsdkQueryConfListReq queryReq = new TsdkQueryConfListReq();
+ queryReq.setPageSize(ConfConstant.PAGE_SIZE);
+ // 当前Demo只查询一页,实际可根据需要分多页查询
+ queryReq.setPageIndex(page);
+ // 查询结束时间
+ queryReq.setQueryEndTime(ConstantsV2.QUERY_END_TIME);
+ int result;
+ if (TsdkManager.getInstance() != null && TsdkManager.getInstance().getConferenceManager()
+ != null) {
+ result = TsdkManager.getInstance().getConferenceManager().queryConferenceList(queryReq);
+ } else {
+ result = 0;
+ }
+ LogUtils.d("queryMyConfList queryConferenceList result " + result);
+ return result;
+ }
+
+ /**
+ * This method is used to join conference
+ * 加入会议
+ *
+ * @param confJoinParam 会议参数
+ * @param isVideo 是否是视频
+ * @param joinNumber 加入号码
+ * @return
+ */
+ public int joinConf(TsdkConfJoinParam confJoinParam, boolean isVideo, String joinNumber) {
+ LogUtils.d("joinConf confJoinParam joinNumber ", confJoinParam, joinNumber);
+ CallMgrV2.setDefPicture();
+ int result;
+ if (TsdkManager.getInstance() != null) {
+ result = TsdkManager.getInstance().getConferenceManager()
+ .joinConference(confJoinParam, isVideo, joinNumber);
+ LogUtils.d(TAG, "joinConf result ", result);
+ if (result != 0) {
+ resetCurrentConference();
+ }
+ } else {
+ result = -1;
+ }
+ isJoinConf = true;
+ return result;
+ }
+
+ /**
+ * 查询是否需要会议密码
+ *
+ * @param accessCode 会议id
+ * @return
+ */
+ public int queryConfPw(String accessCode) {
+ LogUtils.d(TAG, "queryConfPw accessCode = ", accessCode);
+ int result;
+ if (TsdkManager.getInstance() != null) {
+ result = TsdkManager.getInstance().getConferenceManager()
+ .checkConferencePwdExisted(accessCode);
+ LogUtils.d(TAG, "queryConfPw result ", result);
+ } else {
+ result = -1;
+ }
+ return result;
+ }
+
+ /**
+ * 校验会议密码
+ *
+ * @param confJoinParam 加入会议
+ * @return
+ */
+ public int verifyConfPw(TsdkConfJoinParam confJoinParam) {
+ LogUtils.d(TAG, "verifyConfPw tsdkConfJoinParam = ", confJoinParam.toString());
+ int result;
+ if (TsdkManager.getInstance() != null) {
+ result = TsdkManager.getInstance().getConferenceManager()
+ .verifyConferencePwd(confJoinParam);
+ LogUtils.d(TAG, "verifyConfPw result ", result);
+ } else {
+ result = -1;
+ }
+ return result;
+ }
+
+ /**
+ * This method is used to reject conference
+ * 拒绝会议邀请
+ *
+ * @return
+ */
+ public int rejectConf() {
+ LogUtils.d(TAG, "rejectConf");
+ if (currentConference == null) {
+ LogUtils.d(TAG, "rejectConf, currentConference is null ");
+ return 0;
+ }
+ int result = currentConference.getCall().endCall();
+ if (result == 0) {
+ resetCurrentConference();
+ }
+ return result;
+ }
+
+ public void resetCurrentConference() {
+ currentConference = null;
+ }
+
+
+ /**
+ * This method is used to leave conf
+ * 离会
+ *
+ * @return
+ */
+ public int leaveConf() {
+ LogUtils.d(TAG, "leaveConf");
+ if (currentConference == null) {
+ LogUtils.d(TAG, "leave conf, currentConference is null");
+ if (MeetingController.getInstance().getCall() != null) {
+ return MeetingController.getInstance().getCall().endCall();
+ }
+ return -1;
+ }
+ int result = currentConference.leaveConference();
+ LogUtils.d(TAG, "leaveConf result=" + result);
+ if (result == 0) {
+ resetCurrentConference();
+ // 离开会议后将数据会议的共享状态初始化,重新进入会议后此共享状态会重新推送
+ documentId.clear();
+ }
+ setAnonymous(false);
+ setGetTempUserSuccess(false);
+ callTransferToConference = false;
+ return result;
+ }
+
+ /**
+ * This method is used to end conf
+ * 结束会议
+ *
+ * @return
+ */
+ public int endConf() {
+ LogUtils.d(TAG, "endConf");
+ if (currentConference == null) {
+ LogUtils.d(TAG, "endConf, currentConference is null ");
+ return 0;
+ }
+ int result = currentConference.endConference();
+ LogUtils.d(TAG, "endConf result=" + result);
+ if (result == 0) {
+ resetCurrentConference();
+ // 结束会议后将数据会议的共享状态初始化,重新进入会议后此共享状态会重新推送
+ documentId.clear();
+ }
+ setAnonymous(false);
+ setGetTempUserSuccess(false);
+ callTransferToConference = false;
+ return result;
+ }
+
+ /**
+ * This method is used to add attendee
+ * 添加与会者
+ *
+ * @param attendee 与会者信息
+ * @return
+ */
+ public int addAttendee(Member attendee) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "addAttendee failed, currentConference is null");
+ return -1;
+ }
+ TsdkConfAttendeeInfo attendeeBaseInfo = new TsdkConfAttendeeInfo();
+ attendeeBaseInfo.setNumber(attendee.getNumber());
+ attendeeBaseInfo.setDisplayName(attendee.getDisplayName());
+ attendeeBaseInfo.setTerminalType(Integer.parseInt(attendee.getTerminalType()));
+ List attendeeList = new ArrayList<>();
+ attendeeList.add(attendeeBaseInfo);
+ TsdkAddAttendeesInfo addAttendeeInfo = new TsdkAddAttendeesInfo();
+ addAttendeeInfo.setAttendeeList(attendeeList);
+ addAttendeeInfo.setAttendeeNum(attendeeList.size());
+ int result = currentConference.addAttendee(addAttendeeInfo);
+ LogUtils.d(TAG, "addAttendee result ", result);
+ return result;
+ }
+
+ /**
+ * This method is used to add attendee
+ * 添加与会者
+ *
+ * @param attendees 与会者信息
+ * @return
+ */
+ public int addAttendees(List attendees) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "addAttendees currentConference is null");
+ return -1;
+ }
+ TsdkAddAttendeesInfo addAttendeeInfo = new TsdkAddAttendeesInfo();
+ addAttendeeInfo.setAttendeeList(attendees);
+ addAttendeeInfo.setAttendeeNum(attendees.size());
+ int result = currentConference.addAttendee(addAttendeeInfo);
+ LogUtils.d(TAG, "addAttendees result ", result);
+ return result;
+ }
+
+ /**
+ * This method is used to remove attendee
+ * 移除与会者
+ *
+ * @param attendee 与会者信息
+ * @return
+ */
+ public int removeAttendee(Member attendee) {
+ LogUtils.d(TAG, "removeAttendee");
+ if (currentConference == null) {
+ LogUtils.d(TAG, "removeAttendee currentConference is null");
+ return -1;
+ }
+ return currentConference.removeAttendee(attendee.getUserId());
+ }
+
+ /**
+ * This method is used to hang up attendee
+ * 挂断与会者(预留,暂不使用)
+ *
+ * @param attendee 与会者信息
+ * @return
+ */
+ public int hangupAttendee(Member attendee) {
+ LogUtils.d(TAG, "hangupAttendee");
+ if (currentConference == null) {
+ LogUtils.d(TAG, "hangupAttendee currentConference is null");
+ return -1;
+ }
+ int result = currentConference.handupAttendee(attendee.getUserId());
+ LogUtils.d(TAG, "hangupAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * SC转换后画面卡住
+ */
+ public void scChangeRefreshView() {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.SCCHANGEREFRESH, null);
+ }
+
+ /**
+ * This method is used to redial attendee
+ * 重播与会者(预留,暂不使用)
+ *
+ * @param attendee 与会者信息
+ * @return
+ */
+ public int redialAttendee(Member attendee) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "redialAttendee currentConference is null");
+ return -1;
+ }
+ int result = currentConference.redialAttendee(attendee.getUserId());
+ LogUtils.d(TAG, "redialAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * This method is used to record conference
+ * 录播会议
+ *
+ * @param isRecord 是否开启录制
+ * @return
+ */
+ public int recordConf(boolean isRecord) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "recordConf currentConference is null");
+ return -1;
+ }
+ int result;
+ if (isRecord) {
+ result = currentConference.setRecordBroadcast(TSDK_E_CONF_RECORD_START);
+ } else {
+ result = currentConference.setRecordBroadcast(TSDK_E_CONF_RECORD_STOP);
+ }
+ return result;
+ }
+
+ /**
+ * 处理支持单向直播会议的通知
+ *
+ * @param result
+ * @param notify
+ */
+ public void handleUnidirectLiveBroadcastConfResult(TsdkCommonResult result,
+ TsdkonEvtAuditDir notify) {
+ LogUtils.d("handleUnidirectLiveBroadcastConfResult", result);
+ if (result == null) {
+ LogUtils.d(TAG, "handleUnidirectLiveBroadcastConfResult result is null");
+ return;
+ }
+ setFlag(true);
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_AUDIT_DIR, notify);
+ }
+
+ /**
+ * This method is used to mute conf
+ * 静音会议
+ *
+ * @param isMute 是否静音
+ * @return
+ */
+ public int muteConf(boolean isMute) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "muteConf currentConference is null");
+ return -1;
+ }
+ int result = currentConference.muteConference(isMute);
+ if (result != 0) {
+ LogUtils.d(TAG, "muteConf result " + result);
+ }
+ return result;
+ }
+
+ /**
+ * This method is used to mute attendee
+ * 静音与会者
+ *
+ * @param attendee 与会者信息
+ * @param isMute 是否静音
+ * @return
+ */
+ public int muteAttendee(Member attendee, boolean isMute) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "muteAttendee currentConference is null");
+ return -1;
+ }
+ int userId;
+ if (attendee != null) {
+ userId = attendee.getUserId();
+ } else {
+ if (self != null) {
+ userId = self.getUserId();
+ } else {
+ return -1;
+ }
+ }
+ LogUtils.d(TAG, "muteAttendee begin " + isMute);
+ int result = currentConference.muteAttendee(userId, isMute);
+ MeetingController.getInstance().isVoiceOpen = isMute;
+ if (result != 0) {
+ LogUtils.d(TAG, "muteAttendee fail result " + result);
+ }
+ return result;
+ }
+
+ /**
+ * This method is used to release chairman
+ * 释放主席
+ *
+ * @return
+ */
+ public int releaseChairman() {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "releaseChairman currentConference is null");
+ return -1;
+ }
+ int result = currentConference.releaseChairman();
+ LogUtils.d(TAG, "releaseChairman result " + result);
+ if (result == 0) {
+ MeetingController.getInstance().setSilentUnlock(true);
+ }
+ return result;
+ }
+
+ /**
+ * This method is used to request chairman
+ * 请求主席
+ *
+ * @param chairmanPassword 主席密码
+ * @return
+ */
+ public int requestChairman(String chairmanPassword) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "requestChairman currentConference is null");
+ return -1;
+ }
+ int result = currentConference.requestChairman(chairmanPassword);
+ LogUtils.d(TAG, "requestChairman result " + result);
+ return result;
+ }
+
+ /**
+ * 会议锁定
+ *
+ * @return
+ */
+ public int setMeetingLock(boolean isLock) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "meetingLock currentConference is null");
+ return -1;
+ }
+ int result = currentConference.lockConference(isLock);
+ LogUtils.d(TAG, "meetingLock result " + result);
+ return result;
+ }
+
+ /**
+ * SVC观看与会者通知
+ *
+ * @param svcWatchInfo SVC观看者信息
+ */
+ public void onEvtSvcWatchInd(TsdkSvcWatchInfo svcWatchInfo) {
+ LogUtils.d(TAG, "onEvtSvcWatchInd");
+ VideoMgr.getInstance().setSvcWatchIndUpdateSetAll(svcWatchInfo);
+ }
+
+ /**
+ * This method is used to hand up
+ * 设置举手
+ *
+ * @param handUp 是否举手
+ * @param attendee 与会者信息
+ * @return
+ */
+ public int handup(boolean handUp, Member attendee) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "handup currentConference is null");
+ return -1;
+ }
+
+ int result = currentConference.setHandup(attendee.getUserId(), handUp);
+ LogUtils.d(TAG, "handup result " + result);
+ return result;
+ }
+
+ /**
+ * This method is used to postpone conf
+ * 延长会议
+ *
+ * @param time 延长时长
+ * @return
+ */
+ public int postpone(int time) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "postpone currentConference is null");
+ return -1;
+ }
+ return currentConference.postpone(time);
+ }
+
+ /**
+ * This method is used to broadcast attendee
+ * 广播与会者
+ *
+ * @param attendee 与会者信息
+ * @param isBroadcast 是否广播
+ * @return
+ */
+ public int broadcastAttendee(Member attendee, boolean isBroadcast) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "broadcastAttendee currentConference is null");
+ return -1;
+ }
+ //取消广播在mediaX环境下必须填空 SMC下才需要写与会者号码
+ int result = currentConference.broadcastAttendee(attendee.getUserId(), isBroadcast);
+ LogUtils.d(TAG, "broadcastAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * This method is used to watch attendee
+ * 观看与会者
+ * AVC 主动选看
+ *
+ * @param attendee 与会者信息
+ * @return
+ */
+ public int watchAttendee(Member attendee) {
+ if (currentConference == null) {
+ LogUtils.d(TAG, "watchAttendee currentConference is null");
+ return -1;
+ }
+
+ int userId = 0;
+ if (attendee != null) {
+ userId = attendee.getUserId();
+ }
+
+ LogUtils.d(TAG, "watchAttendee userId " + userId);
+ int result = currentConference.watchAttendee(userId);
+ LogUtils.d(TAG, "watchAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * This method is used to watch attendee
+ * SVC观看与会者
+ *
+ * @param watchAttendeeList SVC观看与会者列表
+ * @param page
+ * @return
+ */
+ public int watchFourSvcAttendee(List watchAttendeeList, int page) {
+ if (currentConference == null) {
+ LogUtils.d("watchFourSvcAttendee currentConference is null");
+ return -1;
+ }
+ String sipNumber = LoginMangerV2.getInstance().getTerminal();
+ List attendeeList;
+ if (getMemberList() == null) {
+ attendeeList = new ArrayList<>();
+ } else {
+ attendeeList = new ArrayList<>(getMemberList());
+ }
+ Iterator it = attendeeList.iterator();
+ removeMember(sipNumber, it);
+ watchAttendeeList = getTsdkWatchSvcAttendees(watchAttendeeList, page, attendeeList);
+ TsdkWatchSvcAttendeesInfo watchSvcAttendeesInfo =
+ new TsdkWatchSvcAttendeesInfo(watchAttendeeList.size(), watchAttendeeList);
+ int result = currentConference.watchSvcAttendee(watchSvcAttendeesInfo);
+ TsdkWatchSvcAttendeesInfo newObject =
+ LogUtil.getNewObject(watchSvcAttendeesInfo, TsdkWatchSvcAttendeesInfo.class);
+ if (newObject != null && newObject.getWatchAttendeeList() != null) {
+ for (TsdkWatchSvcAttendees TsdkWatchSvcAttendees :
+ newObject.getWatchAttendeeList()) {
+ TsdkWatchSvcAttendees.setUserId(TsdkWatchSvcAttendees.getUserId());
+ }
+ }
+ LogUtil.zzz(TAG, newObject + " watchSvcAttendee result " + result);
+ return result;
+ }
+
+ private List getTsdkWatchSvcAttendees(
+ List watchAttendeeList, int page, List attendeeList) {
+ try {
+ if (attendeeList.size() > ConstantsV2.CONSTANTSV2_THREE &&
+ attendeeList.size() >= page * ConstantsV2.CONSTANTSV2_THREE) {
+ attendeeList = attendeeList.subList((page - 1) *
+ ConstantsV2.CONSTANTSV2_THREE, page * ConstantsV2.CONSTANTSV2_THREE);
+ }
+ if (attendeeList.size() > ConstantsV2.CONSTANTSV2_THREE && attendeeList.size() <
+ page * ConstantsV2.CONSTANTSV2_THREE) {
+ attendeeList = attendeeList.subList((page - 1) * ConstantsV2.CONSTANTSV2_THREE,
+ attendeeList.size());
+ }
+ watchAttendeeList = watchAttendeeList.subList(0, attendeeList.size());
+ for (int i = 0; i < attendeeList.size(); i++) {
+ int userId = attendeeList.get(i).getUserId();
+ watchAttendeeList.get(i).setUserId(userId);
+ }
+ } catch (Exception e) {
+ LogUtil.zzz(TAG, "watchFourSvcAttendee error" + e.getMessage());
+ e.printStackTrace();
+ }
+ return watchAttendeeList;
+ }
+
+ public void diffMemberName(List watchLists) {
+ if (getMemberList() == null) {
+ LogUtil.zzz(TAG, "diffMemberName getMemberList is null");
+ return;
+ }
+ watchMember.clear();
+ List members = new ArrayList<>(getMemberList());
+ for (int i = 0; i < watchLists.size(); i++) {
+ int userId = watchLists.get(i).getUserId();
+ int labelId = watchLists.get(i).getLableId();
+ for (Member m : members) {
+ if (userId == (m.getUserId())) {
+ m.setDecodeSsrc(labelId);
+ watchMember.add(m);
+ }
+ }
+ }
+ }
+
+ public List getWatchMember() {
+ return watchMember;
+ }
+
+ public Member getSelfAttendee() {
+ return selfAttendee;
+ }
+
+ public int getWatchSingleUserId(Member member) {
+ if (currentConference == null || memberList == null) {
+ LogUtil.zzz(TAG, "getWatchSingleUserId currentConference or memberList is null");
+ return 0;
+ }
+ List mMemberLists = new ArrayList<>();
+ mMemberLists.addAll(memberList);
+ Iterator iterator = mMemberLists.iterator();
+ while (iterator.hasNext()) {
+ Member m = iterator.next();
+ if (ConfConstant.ParticipantStatus.IN_CONF != m.getStatus() || m.getIsAudio()) {
+ iterator.remove();
+ }
+ }
+ int userId = 0;
+ if (member == null) {
+ userId = getUserId(userId, mMemberLists);
+ } else {
+ userId = getUserId(member, userId, mMemberLists);
+ }
+ if (userId == 0 && self != null) {
+ userId = self.getUserId();
+ }
+ return userId;
+ }
+
+ /**
+ * SVC大画面选看
+ *
+ * @param watchSvcAttendees
+ * @param userId
+ * @return
+ */
+ public int watchSingleSvcAttendee(TsdkWatchSvcAttendees watchSvcAttendees, int userId) {
+ if (currentConference == null) {
+ LogUtil.zzz(TAG, "watchSingleSvcAttendee, currentConference is null ");
+ return -1;
+ }
+ List watchAttendeeList = new ArrayList<>();
+ watchAttendeeList.add(watchSvcAttendees);
+ watchAttendeeList.get(0).setUserId(userId);
+ TsdkWatchSvcAttendeesInfo watchSvcAttendeesInfo = new TsdkWatchSvcAttendeesInfo(1, watchAttendeeList);
+ if (watchSvcAttendeesInfo == null) {
+ LogUtil.zzz(TAG, "watchSingleSvcAttendee, watchSvcAttendeesInfo is null");
+ return -1;
+ }
+ int result = currentConference.watchSvcAttendee(watchSvcAttendeesInfo);
+ TsdkWatchSvcAttendeesInfo newObject = LogUtil.getNewObject(watchSvcAttendeesInfo,
+ TsdkWatchSvcAttendeesInfo.class);
+ if (newObject != null && newObject.getWatchAttendeeList() != null) {
+ for (TsdkWatchSvcAttendees TsdkWatchSvcAttendees :
+ newObject.getWatchAttendeeList()) {
+ TsdkWatchSvcAttendees.setUserId(TsdkWatchSvcAttendees.getUserId());
+ }
+ }
+ LogUtil.zzz(TAG, "watchSingleSvcAttendee" +
+ newObject + " watchSingleSvcAttendee watchSvcAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * SVC辅流选看最小分辨率
+ *
+ * @param watchSvcAttendees
+ * @param userId
+ * @return
+ */
+ public int watchAuxSingleSvcAttendee(TsdkWatchSvcAttendees watchSvcAttendees, int userId) {
+ if (currentConference == null) {
+ LogUtil.zzz(TAG, "watchAuxSingleSvcAttendee, currentConference is null ");
+ return -1;
+ }
+ List watchAttendeeList = new ArrayList<>();
+ watchAttendeeList.add(watchSvcAttendees);
+ watchAttendeeList.get(0).setUserId(userId);
+ TsdkWatchSvcAttendeesInfo watchSvcAttendeesInfo = new TsdkWatchSvcAttendeesInfo(1, watchAttendeeList);
+ int result = currentConference.watchSvcAttendee(watchSvcAttendeesInfo);
+ TsdkWatchSvcAttendeesInfo newObject = LogUtil.getNewObject(watchSvcAttendeesInfo,
+ TsdkWatchSvcAttendeesInfo.class);
+ if (newObject != null && newObject.getWatchAttendeeList() != null) {
+ for (TsdkWatchSvcAttendees TsdkWatchSvcAttendees :
+ newObject.getWatchAttendeeList()) {
+ TsdkWatchSvcAttendees.setUserId(TsdkWatchSvcAttendees.getUserId());
+ }
+ }
+ LogUtil.zzz(TAG, "watchAuxSingleSvcAttendee" + newObject
+ + " watchAuxSingleSvcAttendee watchSvcAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * SVC大画面语音激励显示
+ *
+ * @param watchSvcAttendees
+ * @param member
+ * @return
+ */
+ public int watchSVCVoiceAttendee(TsdkWatchSvcAttendees watchSvcAttendees, Member member) {
+ if (member == null) {
+ LogUtil.zzz(TAG, "watchSVCVoiceAttendee member is null");
+ return -1;
+ }
+ if (currentConference == null) {
+ LogUtil.zzz(TAG, "watchSVCVoiceAttendee currentConference is null");
+ return -1;
+ }
+ List watchAttendeeList = new ArrayList<>();
+ watchAttendeeList.add(watchSvcAttendees);
+ watchAttendeeList.get(0).setUserId(member.getUserId());
+ TsdkWatchSvcAttendeesInfo watchSvcAttendeesInfo = new TsdkWatchSvcAttendeesInfo(1, watchAttendeeList);
+ int result = currentConference.watchSvcAttendee(watchSvcAttendeesInfo);
+ TsdkWatchSvcAttendeesInfo newObject =
+ LogUtil.getNewObject(watchSvcAttendeesInfo, TsdkWatchSvcAttendeesInfo.class);
+ if (newObject != null && newObject.getWatchAttendeeList() != null) {
+ for (TsdkWatchSvcAttendees TsdkWatchSvcAttendees :
+ newObject.getWatchAttendeeList()) {
+ TsdkWatchSvcAttendees.setUserId(TsdkWatchSvcAttendees.getUserId());
+ }
+ }
+ LogUtil.zzz(TAG, "watchSVCVoiceAttendee" + newObject
+ + " watchSVCVoiceAttendee watchSvcAttendee result " + result);
+ return result;
+ }
+
+ /**
+ * SVC会议mini页面显示
+ *
+ * @param watchSvcAttendees
+ * @param userId
+ * @return
+ */
+ public int watchSVCMiniAttendee(TsdkWatchSvcAttendees watchSvcAttendees, int userId) {
+ if (currentConference == null) {
+ LogUtil.zzz(TAG, "watchSVCMiniAttendee, currentConference is null ");
+ return -1;
+ }
+ List watchAttendeeList = new ArrayList<>();
+ watchAttendeeList.add(watchSvcAttendees);
+ watchAttendeeList.get(0).setUserId(userId);
+ TsdkWatchSvcAttendeesInfo watchSvcAttendeesInfo =
+ new TsdkWatchSvcAttendeesInfo(1, watchAttendeeList);
+ int result = currentConference.watchSvcAttendee(watchSvcAttendeesInfo);
+ TsdkWatchSvcAttendeesInfo newObject = LogUtil.getNewObject(watchSvcAttendeesInfo,
+ TsdkWatchSvcAttendeesInfo.class);
+ if (newObject != null && newObject.getWatchAttendeeList() != null) {
+ for (TsdkWatchSvcAttendees TsdkWatchSvcAttendees :
+ newObject.getWatchAttendeeList()) {
+ TsdkWatchSvcAttendees.setUserId(TsdkWatchSvcAttendees.getUserId());
+ }
+ }
+ LogUtil.zzz(TAG, "watchSVCMiniAttendee" + newObject
+ + " watchSVCMiniAttendee watchSvcAttendee result " + result);
+ return result;
+ }
+
+ public boolean judgeInviteFormMySelf(String confID) {
+ if ((confID == null) || (confID.equals(""))) {
+ return false;
+ }
+ return currentConference != null && getCurrentConferenceBaseInfo().getConfID().equals(confID);
+ }
+
+ /**
+ * 创会结果
+ *
+ * @param result
+ * @param confBaseInfo
+ */
+ public void handleBookConfResult(TsdkCommonResult result, TsdkConfBaseInfo confBaseInfo) {
+ LogUtil.zzz(TAG, "handleBookConfResult");
+ if (result == null) {
+ LogUtil.zzz(TAG, "handleBookConfResult result is null");
+ if (callTransferToConference) {
+ Session callSession = CallMgrV2.getInstance().
+ getCallSessionByCallID(CallMgrV2.getInstance().getOriginalCallId());
+ if (callSession != null) {
+ callSession.unHoldCall();
+ }
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.BOOK_CONF_FAILED, -1);
+ return;
+ }
+
+ if (result.getResult() != 0) {
+ LogUtil.zzz(TAG, "handleBookConfResult result " + result.getResult());
+ if (callTransferToConference) {
+ Session callSession = CallMgrV2.getInstance().
+ getCallSessionByCallID(CallMgrV2.getInstance().getOriginalCallId());
+ if (callSession != null) {
+ callSession.unHoldCall();
+ }
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.BOOK_CONF_FAILED,
+ result.getResult());
+ return;
+ }
+
+ LogUtil.zzz(TAG, "book conference is success");
+ if (UIUtil.isService3()) {
+ MeetingMgrV2.getInstance().setMeetingId(confBaseInfo.getAccessNumber());
+ MeetingMgrV2.getInstance().setMeetingScheduserAccount(confBaseInfo.getScheduserAccount());
+ EncryptedSPTool.putString(Constant.CONF_TEMPORARY, confBaseInfo.getAccessNumber());
+ }
+ if (callTransferToConference) {
+ CallMgrV2.getInstance().setResumeHold(true);
+ mConfNotification.onConfEventNotify(
+ ConfConstant.CONF_EVENT.CALL_TRANSFER_TO_CONFERENCE, result.getResult());
+ } else {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.BOOK_CONF_SUCCESS, result.getResult());
+ }
+ }
+
+ /**
+ * 查询会议列表结果
+ *
+ * @param result
+ * @param confList
+ */
+ public void handleQueryConfListResult(TsdkCommonResult result, TsdkConfListInfo confList) {
+ LogUtil.zzz(TAG, "handleQueryConfListResult");
+ if (result == null) {
+ LogUtil.zzz(TAG, "handleQueryConfListResult result is null");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_LOGOUT,
+ getString(R.string.cloudLink_accountExpired));
+ return;
+ }
+ if (result.getResult() != 0) {
+ LogUtil.zzz(TAG, "handleQueryConfListResult result.getResult() != 0, reasonDescription "
+ + result.getReasonDescription());
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.QUERY_CONF_LIST_FAILED,
+ result.getResult());
+ return;
+ }
+ if (confList.getConfInfoList() == null) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.QUERY_CONF_LIST_NULL, result.getResult());
+ return;
+ }
+ List confBaseInfoList = new ArrayList<>();
+ List tsdkConfBaseInfos = confList.getConfInfoList();
+ if (tsdkConfBaseInfos != null) {
+ for (TsdkConfBaseInfo confInfo : tsdkConfBaseInfos) {
+ ConfBaseInfo baseInfo = new ConfBaseInfo();
+ baseInfo.setConfID(confInfo.getConfId());
+ baseInfo.setSubject(confInfo.getSubject());
+ LogUtil.zzz(TAG, "ConfBaseInfo length " + confInfo.getSubject().length() +
+ ", subject: " + LogUtil.commonDisplay(confInfo.getSubject()));
+ baseInfo.setAccessNumber(confInfo.getAccessNumber());
+ baseInfo.setChairmanPwd(confInfo.getChairmanPwd());
+ baseInfo.setGuestPwd(confInfo.getGuestPwd());
+ baseInfo.setConfIdV3(confInfo.getConfIdV3());
+ baseInfo.setScheduleUserAccount(confInfo.getScheduleUserAccount());
+ baseInfo.setScheduleUserName(confInfo.getScheduleUserName());
+ baseInfo.setStartTime(confInfo.getStartTime());
+ baseInfo.setEndTime(confInfo.getEndTime());
+ baseInfo.setGuestLink(confInfo.getGuestLink());
+ if (UIUtil.isService3()) {
+ baseInfo.setGuestPwd(confInfo.getGuestPwd());
+ baseInfo.setChairmanPwd(confInfo.getChairmanPwd());
+ baseInfo.setActive(confInfo.getActive());
+ baseInfo.setConferenceType(confInfo.getConferenceType());
+ baseInfo.setConferenceTimeType(confInfo.getConferenceTimeType());
+ baseInfo.setAttendeesNum(confInfo.getAttendeesNum());
+ baseInfo.setAttendees(confInfo.getAttendees());
+ baseInfo.setScheduleUserName(confInfo.getScheduleUserName());
+ baseInfo.setScheduleUserAccount(confBaseInfo.getScheduleUserAccount());
+ }
+ confBaseInfoList.add(baseInfo);
+ }
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.QUERY_CONF_LIST_SUCCESS, confBaseInfoList);
+ }
+
+ /**
+ * 请求vmr列表结果
+ *
+ * @param result vmr列表请求返回的结果
+ * @param vmrInfo vmr列表请求的详细信息
+ */
+ public void handleGetVmrListResult(TsdkCommonResult result, TsdkVmrInfo vmrInfo) {
+ LogUtil.zzz(TAG, "handleGetVmrListResult");
+ if (result == null) {
+ LogUtil.zzz(TAG, "handleGetVmrListResult result is null");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.GET_VMR_LIST_FAILED, -1);
+ return;
+ }
+ if (result.getResult() != 0) {
+ LogUtil.zzz(TAG, "handleGetVmrListResult result reasonDescription " + result.getReasonDescription());
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.GET_VMR_LIST_FAILED, result.getResult());
+ return;
+ }
+ if (TextUtils.isEmpty(vmrInfo.getAccessNumber())) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.GET_VMR_LIST_FAILED, result.getResult());
+ return;
+ }
+ EncryptedSPTool.putString(Constant.VMR_ACCESSNUMBER, vmrInfo.getAccessNumber());
+ EncryptedSPTool.putString(Constant.VMR_CONF_ID, vmrInfo.getConfId());
+ EncryptedSPTool.putString(Constant.VMR_SIP_NUMBER, vmrInfo.getConfId());
+ if (vmrInfo.getChairmanPwd() == null) {
+ EncryptedSPTool.putString(Constant.VMR_CHAIRMANPWD, "");
+ } else {
+ EncryptedSPTool.putString(Constant.VMR_CHAIRMANPWD, vmrInfo.getChairmanPwd());
+ EncryptedSPTool.putBoolean(Constant.IS_CHAIRMAN_SWITCH_OPEN, true);
+ }
+ if (vmrInfo.getGuestPwd() == null) {
+ EncryptedSPTool.putString(Constant.VMR_GUESTPWD, "");
+ } else {
+ EncryptedSPTool.putBoolean(Constant.IS_GUEST_SWITCH_OPEN, true);
+ EncryptedSPTool.putString(Constant.VMR_GUESTPWD, vmrInfo.getGuestPwd());
+ }
+ EncryptedSPTool.putBoolean(Constant.IS_VMR, true);
+ }
+
+ /**
+ * 加入会议结果
+ *
+ * @param tsdkConference
+ * @param commonResult
+ * @param tsdkJoinConfIndInfo
+ */
+
+ public void handleJoinConfResult(TsdkConference tsdkConference, TsdkCommonResult commonResult,
+ TsdkJoinConfIndInfo tsdkJoinConfIndInfo) {
+ if ((tsdkConference == null) || (commonResult == null)) {
+ return;
+ }
+ CallMgrV2.getInstance().setResumeHold(false);
+ int result = commonResult.getResult();
+ if (result == 0) {
+ // 如果该事件重复上报且会议id相同,不进行后续处理,避免 mcu 倒换等出现异常。
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_OPTION_SUCCESS, null);
+ int callId = tsdkConference.getCall().getCallInfo().getCallId();
+ if (MeetingController.getInstance().getCacheCallId() == callId) {
+ this.currentConference = tsdkConference;
+
+ return;
+ }
+ if (!TextUtils.isEmpty(tsdkJoinConfIndInfo.getConfId()) && AppUtil.isInt(tsdkJoinConfIndInfo.getConfId())
+ && Integer.valueOf(tsdkJoinConfIndInfo.getConfId()) > 0) {
+ MeetingController.getInstance().setCacheCallId(callId);
+ }
+ meetingJoining(tsdkConference, tsdkJoinConfIndInfo);
+ } else {
+ if (callTransferToConference) {
+ Session callSession = CallMgrV2.getInstance()
+ .getCallSessionByCallID(CallMgrV2.getInstance().getOriginalCallId());
+ if (callSession != null) {
+ callSession.unHoldCall();
+ }
+ }
+ LogUtil.zzz(TAG, "handleJoinConfResult joining meeting failed");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.JOIN_CONF_FAILED, result);
+ }
+ }
+
+ private void meetingJoining(TsdkConference tsdkConference, TsdkJoinConfIndInfo tsdkJoinConfIndInfo) {
+ this.currentConference = tsdkConference;
+ this.memberList = null;
+ this.self = null;
+ if (isGetTempUserSuccess()) {
+ setAnonymous(true);
+ }
+ // 加入会议视图的初始化
+ TsdkCall tsdkCall = tsdkConference.getCall();
+ if (tsdkCall != null) {
+ Session newSession = CallMgrV2.getInstance().getCallSessionByCallID(tsdkCall.getCallInfo().getCallId());
+ LogUtil.zzz(TAG, "handleJoinConfResult getCallSessionByCallID");
+ if (newSession == null) {
+ newSession = new Session(tsdkCall);
+ }
+ CallMgrV2.getInstance().putCallSessionToMap(newSession);
+ }
+
+ if (!MeetingController.getInstance().isScChange()) {
+ if (MeetingController.getInstance().isSponsorConf()) { // 发起,预约等
+ MeetingController.getInstance().isVideoOpen = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_VIDEO");
+ } else {
+ if (MeetingController.getInstance().isMeetingDuration) {
+ MeetingController.getInstance().isVideoOpen = EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_VIDEO");
+ } else {
+ MeetingController.getInstance().isVideoOpen = false;
+ }
+ }
+ } else {
+ MeetingController.getInstance().setScChange(false);
+ }
+
+ if (!MeetingController.getInstance().isMeetingDuration) {
+ MeetingController.getInstance().isMeetingDuration = true;
+ MeetingController.getInstance().meetingStartTime = System.currentTimeMillis();
+ }
+ //入会操作
+ TsdkConfMediaType tsdkConfMediaType = ConfConvertUtil.
+ convertConfMediaType(tsdkJoinConfIndInfo.getConfMediaType());
+ if (TsdkConfMediaType.TSDK_E_CONF_MEDIA_VOICE == tsdkConfMediaType
+ || TsdkConfMediaType.TSDK_E_CONF_MEDIA_VOICE_DATA == tsdkConfMediaType) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.JOIN_VOICE_CONF_SUCCESS, tsdkConference);
+ LogUtil.zzz(TAG, "handleJoinConfResult voice meeting joining success");
+ } else {
+ if (isFlag()) {
+ tsdkConference.setLiveBroadcast(true);
+ }
+ LogUtil.zzz(TAG, "handleJoinConfResult: video meeting joining success");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.JOIN_VIDEO_CONF_SUCCESS, tsdkConference);
+ }
+ }
+
+
+ /**
+ * 获取数据会议参数结果
+ *
+ * @param tsdkConference
+ * @param commonResult
+ */
+ public void handleGetDataConfParamsResult(TsdkConference tsdkConference, TsdkCommonResult commonResult) {
+ if (tsdkConference == null || commonResult == null) {
+ return;
+ }
+ int result = commonResult.getResult();
+ LogUtil.zzz(TAG, "handleGetDataConfParamsResult result " + result);
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.GET_DATA_CONF_PARAM_RESULT, result);
+ }
+
+ /**
+ * This method is used to conf ctrl operation result.
+ * 会控操作结果
+ *
+ * @param conference Indicates conference info.
+ * 会议信息
+ * @param result Indicates conference info.
+ * 会控操作结果
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void handleConfctrlOperationResult(TsdkConference conference, TsdkConfOperationResult result) {
+ LogUtil.zzz(TAG, "handleConfctrlOperationResult");
+ if (conference == null || result == null) {
+ return;
+ }
+ int ret = result.getReasonCode();
+ LogUtil.zzz(TAG, "handleConfctrlOperationResult ret " + ret);
+ LogUtil.zzz(TAG, "handleConfctrlOperationResult getOperationType " + result.getOperationType());
+ switch (TsdkConfOperationType.enumOf(result.getOperationType())) {
+ // 升级会议
+ case TSDK_E_CONF_UPGRADE_CONF:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.UPGRADE_CONF_RESULT, ret);
+ break;
+ // 闭音会场
+ case TSDK_E_CONF_MUTE_CONF:
+ this.isMuteConf = true;
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.MUTE_CONF_RESULT, ret);
+ break;
+ // 取消闭音
+ case TSDK_E_CONF_UNMUTE_CONF:
+ this.isMuteConf = false;
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.UN_MUTE_CONF_RESULT, ret);
+ break;
+ // 锁定会议
+ case TSDK_E_CONF_LOCK_CONF:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.LOCK_CONF_RESULT, ret);
+ break;
+ // 取消锁定
+ case TSDK_E_CONF_UNLOCK_CONF:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.UN_LOCK_CONF_RESULT, ret);
+ break;
+ // 添加与会者
+ case TSDK_E_CONF_ADD_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.ADD_ATTENDEE_RESULT, ret);
+ break;
+ // 删除与会者
+ case TSDK_E_CONF_REMOVE_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.DEL_ATTENDEE_RESULT, ret);
+ break;
+ // 闭音与会者
+ case TSDK_E_CONF_MUTE_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.MUTE_ATTENDEE_RESULT, ret);
+ break;
+ // 取消闭音与会者
+ case TSDK_E_CONF_UNMUTE_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.UN_MUTE_ATTENDEE_RESULT, ret);
+ break;
+ // 字幕开关
+ case TSDK_E_CONF_CONF_SUBTITLE_SWITCH:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.CONF_SUBTITLE_SWITCH, ret);
+ break;
+ default:
+ handleConfctrlOperationResultOther(conference, result);
+ break;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void handleConfctrlOperationResultOther(TsdkConference conference,
+ TsdkConfOperationResult result) {
+ int ret = result.getReasonCode();
+ switch (TsdkConfOperationType.enumOf(result.getOperationType())) {
+ // 设置举手
+ case TSDK_E_CONF_SET_HANDUP:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.HAND_UP_RESULT, ret);
+ break;
+ // 取消设置举手
+ case TSDK_E_CONF_CANCLE_HANDUP:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.CANCEL_HAND_UP_RESULT, ret);
+ break;
+ // 设置会议视频模式
+ case TSDK_E_CONF_SET_VIDEO_MODE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.SET_CONF_MODE_RESULT, ret);
+ break;
+ // 选看与会者
+ case TSDK_E_CONF_WATCH_ATTENDEE:
+ case TSDK_E_CONF_WATCH_SVC_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.WATCH_ATTENDEE_RESULT, ret);
+ break;
+ // 广播与会者
+ case TSDK_E_CONF_BROADCAST_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.BROADCAST_ATTENDEE_RESULT, ret);
+ break;
+ // 取消广播与会者
+ case TSDK_E_CONF_CANCEL_BROADCAST_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.CANCEL_BROADCAST_RESULT, ret);
+ break;
+ // 申请主席权限
+ case TSDK_E_CONF_REQUEST_CHAIRMAN:
+ notifyRequestChairmanResult(ret, conference);
+ break;
+ // 释放主席权限
+ case TSDK_E_CONF_RELEASE_CHAIRMAN:
+ notifyReleaseChairmanResult(ret);
+ break;
+ // 开始录制会议
+ case TSDK_E_CONF_START_RECORD_BROADCAST:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.START_RECORD_RESULT, ret);
+ break;
+ // 停止录制会议
+ case TSDK_E_CONF_STOP_RECORD_BROADCAST:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.STOP_RECORD_RESULT, ret);
+ break;
+ // 延长会议
+ case TSDK_E_CONF_POSTPONE_CONF:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.POSTPONE_CONF_RESULT, result);
+ break;
+ // 申请主讲人
+ case TSDK_E_CONF_REQUEST_PRESENTER:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.REQUEST_PRESENTER_RESULT, ret);
+ break;
+ // 挂断与会者
+ case TSDK_E_CONF_HANG_UP_ATTENDEE:
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.CONF_HANG_UP_ATTENDEE, ret);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void notifyReleaseChairmanResult(int ret) {
+ LogUtil.zzz(TAG, "notifyReleaseChairmanResult" + ret);
+ ControlOperationsResults.ChairmanResult chairmanResult =
+ new ControlOperationsResults.ChairmanResult();
+ chairmanResult.setResult(ret);
+ chairmanResult.setName("");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.RELEASE_CHAIRMAN_RESULT, chairmanResult);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void notifyRequestChairmanResult(int ret, TsdkConference conference) {
+ ControlOperationsResults.ChairmanResult chairmanResult =
+ new ControlOperationsResults.ChairmanResult();
+ chairmanResult.setResult(ret);
+ if (ret == 0) {
+ String name = Optional.ofNullable(conference)
+ .map(TsdkConference::getSelf)
+ .map(TsdkAttendee::getBaseInfo)
+ .map(TsdkAttendeeBaseInfo::getDisplayName)
+ .orElse("");
+ chairmanResult.setName(name);
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.REQUEST_CHAIRMAN_RESULT, chairmanResult);
+ }
+
+ /**
+ * This method is used to end conf ctrl result.
+ * 结束会议操作结果
+ *
+ * @param conference Indicates conference info.
+ * 会议信息
+ */
+ public void handleConfEndInd(TsdkConference conference) {
+ LogUtil.zzz(TAG, "handleConfEndInd" + conference.getHandle());
+ resetCurrentConference();
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.LEAVE_CONF, conference.getHandle());
+ }
+
+
+ /**
+ * 与会者状态信息和状态更新处理
+ *
+ * @param conference
+ */
+ public void handleInfoAndStatusUpdate(TsdkConference conference) {
+ if ((currentConference == null) || (conference == null)) {
+ LogUtil.zzz(TAG, "handleInfoAndStatusUpdate currentConference is null");
+ return;
+ }
+ LogUtil.zzz(TAG, "handleInfoAndStatusUpdate isHasChairman " + conference.isHasChairman());
+ MeetingController.getInstance().setHasChairman(conference.isHasChairman());
+ if (currentConference.getHandle() != conference.getHandle()) {
+ return;
+ }
+ String subject = conference.getSubject();
+ LogUtil.zzz(TAG, "handleInfoAndStatusUpdate subject : " + subject);
+ String handle = conference.getHandle() + "";
+ this.updateConfInfo(conference);
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.STATE_UPDATE, handle);
+ handleEvtConfctrlStatusInfoInd(conference.isSubtitleEnable() ? 1 : 0);
+ }
+
+ /**
+ * 发言人处理
+ *
+ * @param speakerList
+ */
+ public void handleSpeakerInd(TsdkConfSpeakerInfo speakerList) {
+ speakers = new String[ConstantsV2.CONSTANTSV2_TWO];
+ if (speakerList == null || speakerList.getSpeakerNum() == 0) {
+ speakMember = null;
+ } else {
+ speakMember(speakerList);
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.SPEAKER_LIST_IND, speakerList);
+ }
+
+ private void speakMember(TsdkConfSpeakerInfo speakerList) {
+ List confSpeakers = speakerList.getSpeakers();
+ Collections.sort(confSpeakers, (o1, o2) -> o2.getSpeakingVolume() - o1.getSpeakingVolume());
+ int count = Math.min(ConstantsV2.CONSTANTSV2_TWO, speakerList.getSpeakerNum());
+ for (int i = 0; i < count; i++) {
+ speakers[i] = confSpeakers.get(i).getBaseInfo().getDisplayName();
+ }
+ if (ListTools.isThan(speakerList.getSpeakers(), 0)) {
+ Member member = new Member();
+ String terminal = LoginMangerV2.getInstance().getTerminal();
+ String number = UIUtil.getFormatNumber(confSpeakers.get(0).getBaseInfo().getNumber());
+ if (terminal.equals(confSpeakers.get(0).getBaseInfo().getNumber()) || number.equals(terminal)) {
+ if (confSpeakers.size() > 1) {
+ member.setNumber(confSpeakers.get(1).getBaseInfo().getNumber());
+ member.setDisplayName(confSpeakers.get(1).getBaseInfo().getDisplayName());
+ member.setUserId(confSpeakers.get(1).getBaseInfo().getUserId());
+ } else {
+ speakMember = null;
+ }
+ } else {
+ member.setNumber(confSpeakers.get(0).getBaseInfo().getNumber());
+ member.setDisplayName(confSpeakers.get(0).getBaseInfo().getDisplayName());
+ member.setUserId(confSpeakers.get(0).getBaseInfo().getUserId());
+ }
+ speakMember = member;
+ }
+ }
+
+ public void handleBandChanged() {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.BAND_WATCH, null);
+ }
+
+ /**
+ * 会议来电处理
+ *
+ * @param conference
+ */
+ public void handleConfIncomingInd(TsdkConference conference) {
+ LogUtil.zzz(TAG, "handleConfIncomingInd");
+ if (conference == null) {
+ return;
+ }
+ currentConference = conference;
+ TsdkCall tsdkCall = conference.getCall();
+ if (tsdkCall != null) {
+ Session newSession = CallMgrV2.getInstance().getCallSessionByCallID(tsdkCall.getCallInfo().getCallId());
+ if (newSession == null) {
+ newSession = new Session(tsdkCall);
+ CallMgrV2.getInstance().putCallSessionToMap(newSession);
+ }
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.CONF_INCOMING_TO_CALL_INCOMING, conference);
+ }
+
+ /**
+ * [en]This method is used to get temporary user results for anonymous meetings.
+ * [cn]获取用于匿名方式加入会议的临时用户结果通知.
+ *
+ * @param userId [en]Indicates user id
+ * [cn]用户ID
+ * @param result [en]Indicates operation result.
+ * [cn]操作结果
+ */
+ public void handleGetTempUserResult(int userId, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "handleGetTempUserResult");
+ if (result == null) {
+ return;
+ }
+ if (0 == result.getResult()) {
+ setGetTempUserSuccess(true);
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.GET_TEMP_USER_RESULT, result);
+ }
+
+ /**
+ * 辅流状态改变通知
+ *
+ * @param auxDataStateChange 辅流状态
+ */
+ public void handleAuxDataStateChange(boolean auxDataStateChange) {
+ LogUtil.zzz(TAG, "handleAuxDataStateChange");
+ setIsAuxData(auxDataStateChange);
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.AUX_DATA_STATE, auxDataStateChange);
+ }
+
+ /**
+ * 辅流发送成功通知
+ *
+ * @param callId 辅流状态
+ */
+ public void onEvtAuxSending(int callId) {
+ LogUtil.zzz(TAG, "onEvtAuxSending");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.AUX_DATA_SEND, callId);
+ }
+
+ /**
+ * 辅流发送失败通知
+ *
+ * @param errorType 辅流状态
+ */
+ public void onEvtAuxShareFailed(int errorType) {
+ LogUtil.zzz(TAG, "onEvtAuxShareFailed errorType " + errorType);
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.AUX_DATA_FAILED, errorType);
+ }
+
+ /**
+ * 降噪处理,已静音成功
+ * app需要
+ * 1,更新语音图标为闭麦
+ * 2,弹出 toast给用户提示
+ */
+ public void onEvtHowlAutoMute(boolean b) {
+ LogUtil.zzz(TAG, "onEvtHowlAutoMute");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.MUTESUCCESS_AND_UPDATEUI, b);
+ }
+
+ public void setMediaProjection(MediaProjection mediaProjection) {
+ this.mediaProjection = mediaProjection;
+ }
+
+ public MediaProjection getMediaProjection() {
+ return mediaProjection;
+ }
+
+ /**
+ * 观众会场方向类型。0:单向;1:双向
+ */
+ public int switchAuditSitesDir(int directionType) {
+ LogUtil.zzz(TAG, "switchAuditSitesDir");
+ if (currentConference == null) {
+ LogUtil.zzz(TAG, "switchAuditSitesDir currentConference is null");
+ return -1;
+ }
+ return currentConference.switchAuditSitesDir(directionType);
+ }
+
+ /**
+ * 获取会议大参数结果事件通知.
+ *
+ * @param confParam 会议大参数结果
+ */
+ public void onEvtGetConfParamInd(TsdkConfParam confParam) {
+ String accessCode = confParam.getAccessCode();
+ String confPwd = confParam.getConfPwd();
+ // 收到获取会议大参数结果事件通知后去调加入会议接口
+ String terminal = LoginMangerV2.getInstance().getTerminal();
+ TsdkConfJoinParam confJoinParam = new TsdkConfJoinParam();
+ confJoinParam.setConfPassword(confPwd);
+ confJoinParam.setAccessNumber(accessCode);
+ CallMgrV2.setDefPicture();
+ if (TsdkManager.getInstance() != null) {
+ int result = TsdkManager.getInstance().getConferenceManager().joinConference(confJoinParam, true, terminal);
+ LogUtil.zzz(TAG, "joinConf result " + result);
+ }
+ }
+
+ /**
+ * 处理获取到的时区列表结果
+ *
+ * @param result
+ * @param timeZoneInfoList
+ */
+ public void handleTimeZoneListResult(TsdkCommonResult result, TsdkTimeZoneInfoList timeZoneInfoList) {
+ int res = result.getResult();
+ LogUtil.zzz(TAG, "handleTimeZoneListResult result " + res);
+ if (res == 0) {
+ this.tsdkTimeZoneInfoList = timeZoneInfoList;
+ }
+ }
+
+ /**
+ * SMC上报会议签到结果(3.0及以后版本)通知处理
+ *
+ * @param result 上报结果
+ */
+ public void handleCheckInResult(TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "handleCheckInResult result " + result.getResult());
+ if (result.getResult() == 0) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CHECKINRESULT, 0);
+ }
+ }
+
+ public void setPeerNum(String peerNum) {
+ this.peerNum = peerNum;
+ }
+
+ public String getPeerNum() {
+ return peerNum;
+ }
+
+ String peerNum = "";
+
+ public String getSubject() {
+ return subject;
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+ String subject = "";
+
+ /**
+ * VMR密码更改成功
+ */
+ public void onEvtUpdateVmrInfoSucceed(TsdkCommonResult tsdkCommonResult) {
+ LogUtil.zzz(TAG, "onEvtUpdateVmrInfoSucceed");
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.UPDATE_VMR_SUCCEED, tsdkCommonResult);
+ }
+
+ /**
+ * 加入会议结果,onJoinConfResult后执行
+ *
+ * @param tsdkOnEvtConfBaseInfoInd
+ */
+ public void onEvtConfBaseInfoInd(TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd) {
+ LogUtil.zzz(TAG, "onEvtConfBaseInfoInd " + tsdkOnEvtConfBaseInfoInd.param.toString());
+
+ if (MeetingController.getInstance().isMeetingSessionModified()) {
+ LogUtil.zzz(TAG, "onEvtConfBaseInfoInd 刷新选看");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SESSION_MODIFIED_REFRESH, null);
+ MeetingController.getInstance().setMeetingSessionModified(false);
+ }
+ MeetingController.getInstance().setRealInConf(true);
+ String infoSubject = tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getSubject();
+ setSubject(infoSubject);
+ if (infoSubject != null) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.GET_SUBJECT_SUCCEED, subject);
+ }
+ MeetingController.getInstance().setConfId(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getAccessNumber());
+ MeetingController.getInstance().setStartTime(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getStartTime());
+ MeetingController.getInstance().setEndTime(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getEndTime());
+ MeetingController.getInstance().setScheduleUserName(
+ tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getScheduserName());
+ if (!TextUtils.isEmpty(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getChairmanPwd())) {
+ MeetingController.getInstance().setChairmanPwd(
+ tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getChairmanPwd());
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getChairmanPwd());
+ }
+ MeetingController.getInstance().setGuestPwd(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestPwd());
+ MeetingController.getInstance().setGuestLin(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestLink());
+ // 拿到会议详情,申请主席 只有自己创建的会议才默认申请主席
+ String account = EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT);
+ String password = EncryptedSPTool.getString(account);
+ hasReqChairMan(tsdkOnEvtConfBaseInfoInd, account, password);
+ MeetingController.getInstance().isOnBaseInfoDo = true;
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_STATE_UPDATE, null);
+ TimerUtil.delay(DELAY_TIME, () -> {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_BASE_INFO,
+ tsdkOnEvtConfBaseInfoInd);
+ MeetingController.getInstance().setCallComing(false);
+ MeetingController.getInstance().setCallOutGoing(false);
+ });
+ setMeetingHistory(tsdkOnEvtConfBaseInfoInd);
+ try {
+ if (UIUtil.isService3()) {
+ String peerNumber = tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getAccessNumber()
+ + "*" + tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestPwd();
+ setPeerNum(peerNumber);
+ }
+ } catch (Exception e) {
+ LogUtil.zzz(TAG, "onEvtConfBaseInfoInd getAccessNumber getGuestPwd Exception ",
+ e.toString());
+ }
+ }
+
+ private void hasReqChairMan(TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd, String account, String password) {
+ if (MeetingController.getInstance().isCallOutGoing() || MeetingController.getInstance().isCallComing()) {
+ MeetingController.getInstance().isShowDialog = false;
+ if ((account.equals(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getScheduserAccount())
+ && !TextUtils.isEmpty(password))) {
+ int result = requestChairman(tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getChairmanPwd());
+ if (result == 0) {
+ MeetingController.getInstance().hasReqChairMan = true;
+ }
+ MeetingController.getInstance().setChairman3Pwd(
+ tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getChairmanPwd());
+ }
+ // 2.0 如果是自己发起的会议
+ if (!UIUtil.isService3() && EncryptedSPTool.getBoolean(Constant.CREATE_MEETING_SMC_2, false)
+ && !MeetingController.getInstance().isOnBaseInfoDo) {
+ int result = requestChairman(EncryptedSPTool.getString(Constant.CHAIRMANPWD_SMC_2));
+ if (result == 0) {
+ MeetingController.getInstance().hasReqChairMan = true;
+ }
+ EncryptedSPTool.putString(Constant.CHAIRMANPWD_SMC_2, "");
+ EncryptedSPTool.putBoolean(Constant.CREATE_MEETING_SMC_2, false);
+ }
+ }
+ }
+
+ /**
+ * 加入会议历史记录
+ *
+ * @param tsdkOnEvtConfBaseInfoInd
+ */
+ private void setMeetingHistory(TsdkOnEvtConfBaseInfoInd tsdkOnEvtConfBaseInfoInd) {
+// TsdkConfBaseInfo tsdkConfBaseInfo = tsdkOnEvtConfBaseInfoInd.param.confBaseInfo;
+// JoinMeetingHistoryUtils.deleteItemMeetingHistory(BaseAppContext.getInstance(),
+// tsdkConfBaseInfo.getAccessNumber());
+// ContentValues contentValues = new ContentValues();
+// contentValues.put(JoinMeetingHistoryTable.ACCOUNT, LoginMangerV2.getInstance().getAccount());
+// if (UIUtil.isService3()) {
+// contentValues.put(JoinMeetingHistoryTable.VERSION, VERSION_THREE);
+// } else {
+// contentValues.put(JoinMeetingHistoryTable.VERSION, VERSION_TWO);
+// }
+// contentValues.put(JoinMeetingHistoryTable.TITLE, tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getSubject());
+// contentValues.put(JoinMeetingHistoryTable.ACCOUNT_NUMBER,
+// tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getAccessNumber());
+// contentValues.put(JoinMeetingHistoryTable.MEETING_PSW,
+// tsdkOnEvtConfBaseInfoInd.param.confBaseInfo.getGuestPwd());
+// contentValues.put(JoinMeetingHistoryTable.ADD_TIME, System.currentTimeMillis());
+// JoinMeetingHistoryUtils.addJoinMeetingHistory(App.getContext(), contentValues);
+// contentValues.clear();
+ }
+
+ public TsdkCall getTSdkCallByMeetingMgr() {
+ TsdkManager tsdkManager = TsdkManager.getInstance();
+ if (tsdkManager == null) {
+ LogUtil.zzz(TAG, "TsdkManager is null");
+ return null;
+ }
+ TsdkCallManager tsdkCallManager = tsdkManager.getCallManager();
+ if (tsdkCallManager == null) {
+ LogUtil.zzz(TAG, "TsdkCallManager is null");
+ return null;
+ }
+ return tsdkCallManager.getCallByCallId(MeetingMgrV2.getInstance().getCurrentConferenceCallID());
+ }
+
+ public void onEvtHandUpInd(TsdkNotifyHandUpAttendee tsdkNotifyHandUpAttendee) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.HAS_HANDUP_MEMBER, tsdkNotifyHandUpAttendee);
+ }
+
+ /**
+ * 转换SC
+ *
+ * @param param
+ */
+ public void onEvtDeviceStateNotify(TsdkOnEvtDeviceStateNotify.Param param) {
+ if (param != null) {
+ MeetingController.getInstance().setMuteState(param.getDeviceState().getSiteMicState());
+ }
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.CHANGE_SC, param);
+ }
+
+ /**
+ * 判断是否有广播
+ *
+ * @param param
+ */
+ public void onEvtBroadcastInd(TsdkOnEvtBroadcastInd.Param param) {
+ if (param.getIsBroadcast() == 1) {
+ MeetingController.getInstance().setBroadMember(true);
+ Member member = param.getBroadcastAttendee() != null ?
+ ConfConvertUtil.convertAttendeeInfo(param.getBroadcastAttendee()) : null;
+ MeetingController.getInstance().setBroadIngMember(member);
+ } else {
+ LogUtil.zzz(TAG, "onEvtBroadcastInd no broadcast param.getIsBroadcast() != 1");
+ MeetingController.getInstance().setBroadMember(false);
+ MeetingController.getInstance().setBroadIngMember(null);
+ }
+ }
+
+ /**
+ * 更新与会者列表
+ *
+ * @param conference
+ * @param members
+ */
+ private void upMemberList(TsdkConference conference, List members) {
+ for (TsdkAttendee attendee : conference.getAttendeeList()) {
+ Member member = ConfConvertUtil.convertAttendeeInfo(attendee);
+ if (attendee.getStatusInfo().getIsSelf() == 1) {
+ member.setSelf(true);
+ this.setSelf(member);
+ if (MeetingController.getInstance().needUpdateMuteState) {
+ MeetingController.getInstance().needUpdateMuteState = false;
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.NEED_UPDATE_STATE, member);
+ }
+ }
+ if (member.getRole() == TSDK_E_CONF_ROLE_CHAIRMAN) {
+ members.add(0, member);
+ } else {
+ members.add(member);
+ }
+ }
+ memberList = members;
+ LogUtil.zzz(TAG, "upMemberList memberList = " + memberList.size());
+ // 暂只支持全量更新
+ if (conference.getConfAttendeeUpdateType() == TsdkConfAttendeeUpdateType.TSDK_E_CONF_ATTENDEE_UPDATE_ALL) {
+ for (Member member : memberList) {
+ boolean isOnline = judgeMemberWhetherOnline(member, conference.getAttendeeList());
+ if (!isOnline) {
+ member.setStatus(ConfConstant.ParticipantStatus.LEAVED);
+ member.setRole(TsdkConfRole.TSDK_E_CONF_ROLE_ATTENDEE);
+ member.setHost(false);
+ member.setPresent(false);
+ }
+ }
+ }
+ }
+
+ /**
+ * 移除音频入会或者已离会的与会者
+ *
+ * @param sipNumber
+ * @param it
+ */
+ private void removeMember(String sipNumber, Iterator it) {
+ while (it.hasNext()) {
+ Member attendee = it.next();
+ // 移除音频入会或者已离会的与会者
+ if (attendee.getIsAudio() || attendee.getStatus() != ConfConstant.ParticipantStatus.IN_CONF) {
+ it.remove();
+ continue;
+ }
+ if (attendee.isSelf() || UIUtil.getFormatNumber(attendee.getNumber()).equals(sipNumber)) {
+ selfAttendee = attendee;
+ it.remove();
+ }
+ }
+ }
+
+ private int getUserId(int userId, List mMemberLists) {
+ if (mMemberLists.size() == 1) {
+ userId = mMemberLists.get(0).getUserId();
+ } else {
+ for (Member members : mMemberLists) {
+ if (members.isSelf() || UIUtil.getFormatNumber(members.getNumber())
+ .equals(LoginMangerV2.getInstance().getTerminal())) {
+ continue;
+ }
+ userId = members.getUserId();
+ }
+ }
+ return userId;
+ }
+
+ private int getUserId(Member member, int userId, List mMemberLists) {
+ if (mMemberLists.contains(member)) {
+ userId = member.getUserId();
+ } else {
+ if (mMemberLists.size() == 1) {
+ userId = mMemberLists.get(0).getUserId();
+ } else {
+ userId = memberGetUserId(userId, mMemberLists);
+ }
+ }
+ return userId;
+ }
+
+ private int memberGetUserId(int userId, List mMemberLists) {
+ boolean isNumber;
+ for (Member members : mMemberLists) {
+ isNumber = !UIUtil.getFormatNumber(members.getNumber())
+ .equals(LoginMangerV2.getInstance().getTerminal());
+ if (!members.isSelf() || isNumber) {
+ userId = members.getUserId();
+ }
+ }
+ return userId;
+ }
+
+ /**
+ * 入会查询是否需要会议密码通知
+ *
+ * @param param 入会密码查询信息
+ */
+ public void onEvtCheckConfPwdExistedResult(TsdkOnEvtChcekConfpwdExistedResult.Param param) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.QUERY_CONF_PWD, param);
+ }
+
+ /**
+ * 入会验证会议密码通知
+ *
+ * @param param 入会密码验证信息
+ */
+ public void onEvtVerifyConfPwdResult(TsdkOnEvtVerifyConfPwdResult.Param param) {
+ mConfNotification.onConfEventNotify(ConfConstant.CONF_EVENT.VERIFY_CONF_PWD, param);
+ }
+
+ public void handleEvtSubtitleContentInfo(TsdkSubtitleContentInfo param) {
+ if (param == null || TextUtils.isEmpty(param.getText())) {
+ return;
+ }
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_INFO,
+ param);
+ }
+
+ public void handleEvtSubtitleCapabilityInd(int param) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_ENABLE,
+ param);
+ }
+
+ private void handleEvtConfctrlStatusInfoInd(int subtitleStatus) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_STATUS,
+ subtitleStatus);
+ }
+
+ public int setConfSubtitle(boolean isEnable) {
+ LogUtil.zzz(TAG, "setConfSubtitle isEnable = " + isEnable);
+ if (currentConference == null) {
+ LogUtil.zzz(TAG, "setConfSubtitle, currentConference is null ");
+ return 0;
+ }
+ return currentConference.setConfSubtitle(isEnable);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/SaveRecentCalls.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/SaveRecentCalls.kt
new file mode 100644
index 0000000..57b9a1e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/SaveRecentCalls.kt
@@ -0,0 +1,139 @@
+package com.tengshisoft.chatmodule.hwclud.manager
+
+import com.huawei.ecterminalsdk.models.call.TsdkCall
+import com.tengshisoft.chatmodule.hwclud.utils.DateUtil
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil
+
+/**
+ * @Author WangBO
+ * @create 2020/12/21 14:02
+ */
+class SaveRecentCalls{
+
+ private var onConnectedTime = 0L
+ private var startTime :String = ""
+ var time:String = ""
+ var callIncomingTime = ""
+ /**
+ * 接听状态
+ */
+ private lateinit var status:CallStatus
+
+ private lateinit var state:Caller
+
+ companion object {
+ val instance: SaveRecentCalls by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
+ SaveRecentCalls()
+ }
+ }
+
+ /**
+ *TODO 通过call对象保存通话记录
+ * @param call call
+ * @param subject 会议主题
+ */
+ fun saveRecentCalls(call: TsdkCall?,subject:String) {
+ LogUtil.zzz("saveRecentCalls ",LogUtil.commonDisplay(call?.callInfo?.peerDisplayName))
+ LogUtil.zzz("saveRecentCalls ",LogUtil.commonDisplay(call?.callInfo?.peerNumber))
+// val callInfo = CallMgrV2.getInstance().getCallInfo(call)
+// val c = ContentValues()
+// LogUtil.i(LogUtil.CLOUNDLINK, "call end!")
+// val currentTime = System.currentTimeMillis()
+// if (onConnectedTime != 0L) {
+// val l = currentTime - onConnectedTime
+// //接听
+// status = CallStatus.ANSWER
+// val talkTime = DateUtil.fromLongToDate(DateUtil.FMT_YMDHM_3, l)
+// c.put(RecentCallsTable.TALKTIME, talkTime)
+//
+// c.put(RecentCallsTable.TIME, DateUtil.fromLongToDate1(DateUtil.FMT_YMDHM_4, onConnectedTime))
+// } else {
+// status = if (callInfo.isCaller){
+// //主叫-> 未接通
+// CallStatus.MISSED_IS_CALLER
+// }else{
+// //被叫-> 未接来电
+// CallStatus.MISSED_IS_NOT_CALLER
+// }
+// c.put(RecentCallsTable.TALKTIME, "00:00:00")
+// if (!UIUtil.isForeground()) {
+// NotifyUtil.callEndNotify(App.getContext(), callInfo.peerNumber)
+// }
+// if (state == Caller.IS_CALLER) {
+// c.put(RecentCallsTable.TIME, startTime)
+// } else {
+// c.put(RecentCallsTable.TIME, callIncomingTime)
+// }
+// }
+//
+// if (call?.callInfo?.isFocus==1){
+// c.put(RecentCallsTable.NAME, subject)
+// }else{
+// val formatPeerNumber = Platform.getFormatName(callInfo.peerNumber)
+// val realName = if (TextUtils.isEmpty(callInfo.peerDisplayName)){
+// formatPeerNumber
+// } else {
+// Platform.getFormatName(callInfo.peerDisplayName)
+// }
+// c.put(RecentCallsTable.NAME,realName)
+// }
+// c.put(RecentCallsTable.IS_CONF,call?.callInfo?.isFocus)
+// if (UIUtil.isService3()){
+// c.put(RecentCallsTable.SERVICE_TYPE,3)
+// }else{
+// c.put(RecentCallsTable.SERVICE_TYPE,2)
+// }
+// c.put(RecentCallsTable.ACCOUNT, LoginMangerV2.getInstance().account)
+// c.put(RecentCallsTable.NUMBER, callInfo.peerNumber)
+// c.put(RecentCallsTable.ISVIDEO, if (callInfo.isVideoCall) "1" else "0")
+// c.put(RecentCallsTable.STATUS, status.index.toString())
+// c.put(RecentCallsTable.ISCALLER, if (callInfo.isCaller) 1 else 0)
+// LogUtil.zzz("SaveRecentCalls", "saveRecentCalls: "+subject)
+// RencentCallsUtils.addRecentCalls(App.getContext(), c)
+// c.clear()
+// onConnectedTime = 0L
+// time = ""
+// startTime = ""
+ }
+
+ /**
+ * 连接时间
+ */
+ fun onEvtCallConnected(){
+ onConnectedTime = System.currentTimeMillis()
+ time = DateUtil.fromLongToDate1(DateUtil.FMT_YMDHM_4, System.currentTimeMillis())
+ }
+
+ /**
+ * 主动呼出时间
+ */
+ fun onEvtCallOutgoing(){
+ state = Caller.IS_CALLER
+ startTime = DateUtil.fromLongToDate1(DateUtil.FMT_YMDHM_4, System.currentTimeMillis())
+ }
+
+ /**
+ * 来电响铃时间
+ */
+ fun onEvtCallIncoming(){
+ state = Caller.NOT_CALLER
+ callIncomingTime = DateUtil.fromLongToDate1(DateUtil.FMT_YMDHM_4, System.currentTimeMillis())
+ }
+
+ enum class CallStatus(val index: Int) {
+ //正常接听
+ ANSWER(1),
+ //对方未接
+ MISSED_IS_CALLER(2),
+ //被叫未接
+ MISSED_IS_NOT_CALLER(3);
+ }
+
+ enum class Caller{
+ //主叫
+ IS_CALLER,
+ //被叫
+ NOT_CALLER
+ }
+
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/SwitchAudioRouteManager.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/SwitchAudioRouteManager.kt
new file mode 100644
index 0000000..ba6fb31
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/SwitchAudioRouteManager.kt
@@ -0,0 +1,154 @@
+package com.tengshisoft.chatmodule.hwclud.manager
+
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute
+import com.huawei.ecterminalsdk.models.TsdkManager
+import com.tenlionsoft.baselib.utils.LogUtils
+
+/**
+ * @description 音頻路由切換管理類
+ * @author jackson
+ * @time 2020/10/30
+ */
+open class SwitchAudioRouteManager {
+ /**
+ * 上次设置的值
+ */
+ var lastAudioRoute: TsdkMobileAuidoRoute = TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT
+ private val beforeCallingAudioRoute = TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER
+ private val voiceAudioRoute = TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT
+ private val videoAudioRoute = TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER
+ var callComing: Boolean = true
+
+ var isvideo : Boolean = false;
+ /**
+ * 用户设置的状态
+ */
+ lateinit var userSettingAudioRoute: TsdkMobileAuidoRoute
+
+ /**
+ * 蓝牙耳机连接状态
+ */
+ var bluetoothHeadset = false
+
+ /**
+ * 有线耳机连接状态
+ */
+ var wiredHeadset = false
+
+ /**
+ * 耳机是否插入
+ * bluetoothHeadset|wiredHeadset
+ */
+ var headsetPlug: Boolean = wiredHeadset or bluetoothHeadset
+
+ /**
+ * 用户点击设置
+ */
+ var isSettingAudioRoute: Boolean = false
+
+ companion object {
+ val instance: SwitchAudioRouteManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
+ SwitchAudioRouteManager()
+ }
+ }
+
+ /**
+ * 来电前扬声器 TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER
+ */
+ fun settingBeforeCallingAudioRoute() = setAudioRoute(beforeCallingAudioRoute)
+
+
+ /**
+ * 语音默认听筒 TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT
+ */
+ @Deprecated("this function is deprecated! pls use setRoute().")
+ fun setVoiceAudioRoute() {
+ setAudioRoute(voiceAudioRoute)
+ }
+
+ /**
+ * 视频默认扬声器 TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER
+ */
+ @Deprecated("this function is deprecated! pls use setRoute().", ReplaceWith("setRoute()"),level = DeprecationLevel.WARNING)
+ fun setVideoAudioRoute() {
+ setAudioRoute(videoAudioRoute)
+ }
+
+ /**
+ * @param headsetState 是否有耳机
+ * @param isVideo 是否是视频
+ * @param hasSetting 用户是否设置过
+ */
+ fun setRoute(headsetState: Boolean, isVideo: Boolean, hasSetting: Boolean):Boolean {
+ return if (hasSetting) {
+ instance.setAudioRoute(instance.userSettingAudioRoute)
+ } else {
+ headSetStateChange(headsetState, isVideo)
+ }
+ }
+ /**
+ * 会议连接时有无耳机声音路由设置状态
+ * @param headsetState 耳机是否连接
+ * @param isVideo 是否是视频
+ */
+ private fun headSetStateChange(headsetState: Boolean, isVideo: Boolean):Boolean {
+ isvideo = isVideo
+ return if (headsetState) {
+ setAudioRoute(voiceAudioRoute)
+ } else {
+ if (isVideo) {
+ setAudioRoute(videoAudioRoute)
+ }else{
+ setAudioRoute(voiceAudioRoute)
+ }
+ }
+ }
+ /**
+ * 获取当前声音路由状态
+ * 默认 TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT(0),
+ * 扬声器 TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER(1),
+ * 蓝牙 TSDK_E_MOBILE_AUDIO_ROUTE_BLUETOOTH(2),
+ * 听筒 TSDK_E_MOBILE_AUDIO_ROUTE_EARPIECE(3),
+ * 耳机 TSDK_E_MOBILE_AUDIO_ROUTE_HEADSET(4);
+ */
+ fun getAudioRoute():TsdkMobileAuidoRoute{
+ when(CallMgrV2.getInstance().currentAudioRoute){
+ 0 -> return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT
+ 1 -> return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER
+ 2 -> return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_BLUETOOTH
+ 3 -> return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_EARPIECE
+ 4 -> return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_HEADSET
+ }
+ return TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT
+ }
+
+ @Deprecated("this function is deprecated! pls use setRoute().", ReplaceWith("setRoute()"),level = DeprecationLevel.WARNING)
+ fun switchAudioRoute(audioRoute: TsdkMobileAuidoRoute){
+ if (audioRoute == TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER){
+ setAudioRoute(TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_LOUDSPEAKER)
+ }else{
+ setAudioRoute(TsdkMobileAuidoRoute.TSDK_E_MOBILE_AUDIO_ROUTE_DEFAULT)
+ }
+
+ }
+
+ /**
+ * This method is used to sets audio route.
+ * 设置音频路由
+ *
+ * @param audioSwitch the audio switch
+ * @return the audio route
+ */
+ fun setAudioRoute(audioSwitch: TsdkMobileAuidoRoute): Boolean {
+ LogUtils.d("SwitchAudioRouteManager setAudioRoute = $audioSwitch")
+ return if (TsdkManager.getInstance() != null) {
+ LogUtils.d("切换声音路由成功+$audioSwitch")
+ TsdkManager.getInstance().callManager.mobileAudioRoute = audioSwitch
+ true
+ } else {
+ LogUtils.d("切换声音路由失败")
+ false
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/VideoMgr.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/VideoMgr.java
new file mode 100644
index 0000000..435a908
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/VideoMgr.java
@@ -0,0 +1,1201 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.view.OrientationEventListener;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import com.huawei.ecterminalsdk.base.TsdkAllSvcWndInfoBean;
+import com.huawei.ecterminalsdk.base.TsdkCallInfo;
+import com.huawei.ecterminalsdk.base.TsdkDeviceInfo;
+import com.huawei.ecterminalsdk.base.TsdkDeviceType;
+import com.huawei.ecterminalsdk.base.TsdkSvcWatchInfo;
+import com.huawei.ecterminalsdk.base.TsdkVideoCtrlInfo;
+import com.huawei.ecterminalsdk.base.TsdkVideoOrient;
+import com.huawei.ecterminalsdk.base.TsdkVideoRenderInfo;
+import com.huawei.ecterminalsdk.base.TsdkVideoWndDisplayMode;
+import com.huawei.ecterminalsdk.base.TsdkVideoWndInfo;
+import com.huawei.ecterminalsdk.base.TsdkVideoWndMirrorType;
+import com.huawei.ecterminalsdk.base.TsdkVideoWndType;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.call.TsdkCallManager;
+import com.huawei.videoengine.ViERenderer;
+import com.tengshisoft.chatmodule.beans.CallConstant;
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is about video management
+ * 视频管理类
+ */
+// TODO 将这个类划分成更小更专业的类,以将它对其他类的依赖性从24个减少到最多授权的20个或更少。
+public class VideoMgr {
+
+ private static final String TAG = VideoMgr.class.getSimpleName();
+ private Context context;
+ private Handler handler;
+ private TsdkCallManager callManager;
+ List cameraList;
+ private int currentCameraIndex = CallConstant.FRONT_CAMERA;
+
+
+ private int currentCallId;
+ private boolean isInitVideoRenderer;
+ private boolean isSvcInitVideo;
+ private boolean isAvcInitVideo;
+
+ /**
+ * 本地隐藏窗口(只能创建一个)
+ */
+ private SurfaceView localHideView;
+ /**
+ * 本地窗口(只能创建一个)
+ */
+ private SurfaceView localVideoView;
+ /**
+ * 远端窗口(可以创建多个)
+ */
+ private SurfaceView remoteVideoView;
+ /**
+ * 辅流窗口(只能创建一个,创建方法和远端窗口一致)
+ */
+ private SurfaceView auxDataView;
+ /**
+ * SVC 画廊View集合
+ */
+ private ArrayList svcViewList = new ArrayList<>();
+ /**
+ * SVC大画面
+ */
+ private SurfaceView svcBigView;
+ /**
+ * SVC 画廊View是否添加至TSDK解码器
+ */
+ // TODO 删除未使用私有字段
+ private Map svcIsAddList;
+ /**
+ * SVC大画面是否添加至TSDK解码器
+ */
+ // TODO 删除未使用私有字段
+ private WatchInfo svcBigWatchInfo;
+ private OrientationDetector orientationDetector;
+
+ public static final int LAYOUT_PORTRAIT = 1;
+
+ public static final int LAYOUT_LANDSCAPE = 2;
+
+ private final Runnable mRefreshMeetingView = () ->
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_REFRESH_SVC_VIEW, null);
+
+ private static class ClassHolder {
+ @SuppressLint("StaticFieldLeak")
+ private static VideoMgr instance;
+ }
+
+ public void clearLocGroup() {
+ LogUtils.d("clearLocGroup");
+ if (locGroup != null) {
+ LogUtils.d("clearLocGroup", "删除");
+ locGroup.removeView(getInstance().getLocalVideoView());
+ }
+ locGroup = null;
+ }
+
+ public void setLocGroup(ViewGroup locGroup) {
+ LogUtils.d(TAG, "setLocGroup");
+ this.locGroup = locGroup;
+ }
+
+ public ViewGroup locGroup = null;
+
+
+ private VideoMgr() {
+ context = BaseAppContext.getInstance();
+ if (context == null) {
+ throw new NullPointerException("BaseApp not initialized.");
+ }
+ handler = new Handler(context.getMainLooper());
+ if (null != TsdkManager.getInstance() && null != TsdkManager.getInstance().getCallManager()) {
+ callManager = TsdkManager.getInstance().getCallManager();
+ }
+ if (callManager != null) {
+ cameraList = callManager.getDevices(TsdkDeviceType.TSDK_E_DEVICE_CAMERA);
+ }
+ }
+
+ public static VideoMgr getInstance() {
+ if (ClassHolder.instance == null) {
+ ClassHolder.instance = new VideoMgr();
+ }
+ return ClassHolder.instance;
+ }
+
+ /**
+ * 创建视频Renderer
+ * Create video renderer
+ */
+ private void createVideoRenderer(TsdkCallInfo callInfo) {
+ LogUtils.d(TAG, "createVideoRenderer");
+ // 创建本地视频窗口(本地窗口只能创建一个,底层可以直接获取到这个窗口)
+ // 必须存在,否则远端视频无法显示
+ localHideView = ViERenderer.createLocalRenderer(context);
+ localHideView.setZOrderOnTop(false);
+ localHideView.getHolder().addCallback(new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ LogUtils.d("localHideView surfaceCreated");
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ LogUtils.d("localHideView surfaceChanged");
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ LogUtils.d("localHideView surfaceDestroyed");
+ }
+ });
+ // 本端小窗口显示
+ localVideoView = ViERenderer.createRenderer(context, true);
+ LogUtils.d("closeOrOpenFloat", "LocalVideoView創建,callInfo=" + callInfo.getIsFocus());
+ localVideoView.setZOrderMediaOverlay(false);
+ if (callInfo.getIsFocus() == 0) {
+ localVideoView.setZOrderMediaOverlay(true);
+ }
+ auxDataView = ViERenderer.createRenderer(context, true);
+ auxDataView.setZOrderMediaOverlay(true);
+ //保存辅流窗口的Index
+ int bfcpCallIndex = ViERenderer.getIndexOfSurface(auxDataView);
+ TsdkManager.getInstance().getCallManager().setBfcpCallIndex(bfcpCallIndex);
+ isInitVideoRenderer = true;
+ }
+
+ /**
+ * switch camera
+ * 切换摄像头
+ *
+ * @param call 呼叫信息
+ * @param cameraIndex 设备下标
+ * @return 设置结果
+ */
+ public int switchCamera(TsdkCall call, int cameraIndex) {
+ LogUtils.d(TAG, "switchCamera", cameraIndex);
+ return setVideoOrient(call.getCallInfo().getCallId(), cameraIndex);
+ }
+
+ /**
+ * open camera
+ * 打开摄像头
+ *
+ * @param call 呼叫信息
+ * @return 结果
+ */
+ public int openCamera(TsdkCall call, int cameraIndex) {
+ LogUtils.d(TAG, "openCamera", cameraIndex);
+ return controlLocalCameraMode1(call, true, cameraIndex);
+ }
+
+ /**
+ * close camera
+ * 关闭摄像头
+ *
+ * @param call 呼叫信息
+ * @return 结果
+ */
+ public int closeCamera(TsdkCall call) {
+ LogUtils.d(TAG, "closeCamera");
+ return controlLocalCameraMode1(call, false, 0);
+ }
+
+ public void reOpenRomteCameraStream(int Callid) {
+ LogUtils.d(TAG, "reOpenRomteCameraStream");
+ TsdkCall call = TsdkManager.getInstance().getCallManager().getCallByCallId(Callid);
+ if (call != null) {
+ TsdkVideoCtrlInfo tsdkVideoCtrlInfo = new TsdkVideoCtrlInfo(4, 4, 1);
+ call.videoControl(tsdkVideoCtrlInfo);
+ }
+ }
+
+ /**
+ * Local video capture control
+ * 本地视频采集控制
+ *
+ * @param call 呼叫信息
+ * @param isOpen 是否打开摄像头
+ * @return
+ */
+ private int controlLocalCameraMode1(TsdkCall call, boolean isOpen, int cameraIndex) {
+ LogUtils.d(TAG, "controlLocalCameraMode1", isOpen, cameraIndex);
+ int result = 0;
+ if (isOpen) {
+ //重新设置摄像头采集角度
+ if (orientationDetector != null) {
+ result = call.setCaptureRotation(cameraIndex, orientationDetector.cameraCaptureRotation);
+ } else {
+ result = call.setCaptureRotation(cameraIndex, 3);
+ }
+ LogUtils.d("setCaptureRotation", "重新设置摄像头采集角度", result);
+ if (result != 0) {
+ LogUtils.e(TAG, "setCaptureRotation is failed, result -->" + result);
+ } else {
+ if (MeetingController.getInstance().isMinimize()) {
+ // TODO 200使用常量
+ SystemClock.sleep(200);
+ }
+ setCurrentCameraIndex(cameraIndex);
+ }
+ } else {
+ //采用发送默认图版本方式,替代关闭摄相头动作
+ LogUtils.d("默认图片路径 = " + ConstantsV2.BMP_FILE_PATH);
+ result = call.setCameraPicture(ConstantsV2.BMP_FILE_PATH);
+ LogUtils.d("setCameraPicture", "采用发送默认图版本方式", result);
+ if (result != 0) {
+ LogUtils.e(TAG, "setVideoCaptureFile is failed, result -->" + result);
+ }
+ setCurrentCameraIndex(CallConstant.CAMERA_NON);
+ }
+ LogUtils.d("setCaptureRotation", "是否打开摄像头:" + isOpen, result);
+ return result;
+ }
+
+ // TODO 删除掉这个未使用的私有方法
+ private int controlLocalCameraMode2(TsdkCall call, boolean isOpen) {
+ LogUtils.d(TAG, "controlLocalCameraMode2");
+ int result;
+
+ /**
+ * operation, value :open 0x01,close 0x02,start 0x04,stop 0x08, value can be linked by "|"
+ * 操作,取值: open 0x01,close 0x02,start 0x04,stop 0x08,可以使用逻辑运算符"|"连接,open|start,close|stop
+ */
+ int operation;
+
+ /**
+ * module,value:0x01 display remote window,0x02 display local window,0x04 video,0x08 coder,0x10 decoder
+ * 模式,取值: 0x01显示远端窗口 0x02显示本端窗口 0x04摄相头 0x08编码器 0x10解码器
+ */
+ int module;
+
+ if (isOpen) {
+ module = 0x02 | 0x04;
+ operation = 0x04;
+
+ TsdkVideoCtrlInfo tsdkVideoCtrlInfo = new TsdkVideoCtrlInfo(0, operation, module);
+ result = call.videoControl(tsdkVideoCtrlInfo);
+ if (result != 0) {
+ LogUtils.e(TAG, "videoControl is failed, result --> " + result);
+ return result;
+ }
+ } else {
+ module = 0x02 | 0x04;
+ operation = 0x08;
+
+ TsdkVideoCtrlInfo tsdkVideoCtrlInfo = new TsdkVideoCtrlInfo(0, operation, module);
+ result = call.videoControl(tsdkVideoCtrlInfo);
+ if (result != 0) {
+ LogUtils.e(TAG, "videoControl is failed, result --> " + result);
+ return result;
+ }
+ result = setVideoOrient(call.getCallInfo().getCallId(), CallConstant.FRONT_CAMERA);
+ if (result != 0) {
+ LogUtils.e(TAG, "setVideoOrient is failed, result --> " + result);
+ return result;
+ }
+ }
+
+ return 0;
+ }
+
+ /**
+ * 设置视频窗口方向
+ *
+ * @param callId 0表示全局设置,不为0表示 会话中设置
+ * @param cameraIndex 摄像头index
+ * @return int result 视频角度
+ */
+ public int setVideoOrient(int callId, int cameraIndex) {
+ LogUtils.d(TAG, "setVideoOrient", callId, cameraIndex);
+ int result = 0;
+ int orient;
+ int portrait;
+ int landscape;
+ int seascape;
+
+ Configuration configuration = BaseAppContext.getInstance().getResources().getConfiguration();
+ if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
+ orient = 1;
+ } else {
+ // TODO 2使用常量
+ orient = 2;
+ }
+
+ if (cameraIndex == CallConstant.FRONT_CAMERA) {
+ // TODO 3使用常量
+ portrait = 3;
+ landscape = 0;
+ // TODO 2使用常量
+ seascape = 2;
+ } else if (cameraIndex == CallConstant.BACK_CAMERA) {
+ portrait = 1;
+ landscape = 0;
+ // TODO 2使用常量
+ seascape = 2;
+ } else {
+ return -1;
+ }
+
+ /**
+ * 横竖屏信息stOrient 设置标志位
+ * @param int callId 0表示全局设置,不为0表示 会话中设置
+ * @param int index 摄像头index
+ *
+ * @param int orient 视频横竖屏情况 1:竖屏;2:横屏;3:反向横屏
+ * @param int portrait 竖屏视频捕获(逆时针旋转)角度 0:0度;1:90度;2:180度;3:270度; 3
+ * @param int landscape 横屏视频捕获(逆时针旋转)角度 0:0度;1:90度;2:180度;3:270度; 0
+ * @param int seascape 反向横屏视频捕获(逆时针旋转)角度 0:0度;1:90度;2:180度;3:270度; 2
+ * @return int result 视频角度
+ */
+ TsdkVideoOrient videoOrient = new TsdkVideoOrient(portrait, seascape, landscape, orient);
+ TsdkCall tsdkCall = callManager.getCallByCallId(callId);
+
+ if (tsdkCall != null) {
+ result = tsdkCall.setVideoOrient(cameraIndex, videoOrient);
+ LogUtils.d("setVideoOrient", "切换摄像头:result=" + result);
+ if (result != 0) {
+ LogUtils.e(TAG, "set video orient is failed. result --> " + result);
+ } else {
+ setCurrentCameraIndex(cameraIndex);
+ }
+ }
+
+ if (orientationDetector != null) {
+ orientationDetector.updateRotation(true);
+ }
+
+ return result;
+ }
+
+ private void initSvcVideoView() {
+ LogUtils.d(TAG, "initSvcVideoView");
+ svcIsAddList = new HashMap<>();
+ svcBigWatchInfo = new WatchInfo();
+ svcBigView = ViERenderer.createRenderer(context, true);
+ svcBigView.setZOrderMediaOverlay(false);
+ if (null == svcViewList) {
+ svcViewList = new ArrayList<>();
+ }
+ // svc创建三个远端surfaceView,本地窗口用localVideoView
+ // TODO 3使用常量
+ for (int i = 0; i < 3; i++) {
+ // 创建SVC远端视频窗口(可以创建多个)
+ SurfaceView svcVideoView = ViERenderer.createRenderer(context, true);
+ svcVideoView.setZOrderMediaOverlay(false);
+ svcViewList.add(svcVideoView);
+ }
+ isSvcInitVideo = true;
+ }
+
+ private void initAvcVideoView() {
+ LogUtils.d(TAG, "initAvcVideoView");
+ remoteVideoView = ViERenderer.createRenderer(context, true);
+ remoteVideoView.setZOrderMediaOverlay(false);
+ isAvcInitVideo = true;
+ }
+
+ /**
+ * Initializing the video window
+ * 初始化视频窗口
+ *
+ * @param callId 呼叫id
+ */
+ public void initVideoWindow(final int callId) {
+ LogUtils.d(TAG, "initVideoWindow", "callId=" + callId);
+ // TODO 将这个匿名类的行数减少到最多20行,或者定义为一个命名类。
+ // TODO 这个匿名内部类使用lambda表达式
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ List list = new ArrayList<>();
+ TsdkCall tsdkCall = callManager.getCallByCallId(callId);
+// if (MeetingMgr.getInstance().getMemberList() != null) {
+// MeetingMgr.getInstance().getMemberList().clear();
+// }
+ if (null != tsdkCall) {
+ TsdkCallInfo callInfo = tsdkCall.getCallInfo();
+ if (!isInitVideoRenderer) {
+ createVideoRenderer(callInfo);
+ }
+ //locvideo长久持有,在界面销毁的时候未能销毁,所以每次创建的时候判断,先清空
+
+ if (localVideoView != null && localVideoView.getParent() != null) {
+ ((ViewGroup) localVideoView.getParent()).removeAllViews();
+ }
+ if (callInfo.getIsSvcCall() == 0) {
+ // 创建远端视频窗口(可以创建多个)
+ if (!isAvcInitVideo) {
+ initAvcVideoView();
+ }
+ } else {
+ if (!isSvcInitVideo) {
+ initSvcVideoView();
+ }
+ }
+ setCurrentCallId(callId);
+ removeAllSvcVideoWindow();
+// setVideoOrient(callId, CallConstant.FRONT_CAMERA);
+ // 设置本地视频窗口
+ TsdkVideoWndInfo localWndInfo = new TsdkVideoWndInfo();
+ localWndInfo.setVideoWndType(TsdkVideoWndType.TSDK_E_VIDEO_WND_LOCAL);
+ localWndInfo.setRender(ViERenderer.getIndexOfSurface(localVideoView));
+ localWndInfo.setDisplayMode(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_FULL);
+ list.add(localWndInfo); // 添加本地视频窗口信息到列表
+ if (callInfo.getIsSvcCall() == 0) {
+ // 不是SVC会议
+ // 设置远端视频窗口
+ TsdkVideoWndInfo remoteWndInfo = new TsdkVideoWndInfo();
+ remoteWndInfo.setVideoWndType(TsdkVideoWndType.TSDK_E_VIDEO_WND_REMOTE);
+ remoteWndInfo.setRender(ViERenderer.getIndexOfSurface(remoteVideoView));
+ remoteWndInfo.setDisplayMode(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_CUT);
+ // 添加远端视频窗口信息到列表
+ list.add(remoteWndInfo);
+ }
+ //设置辅流窗口
+ TsdkVideoWndInfo auxDataWndInfo = new TsdkVideoWndInfo();
+ auxDataWndInfo.setVideoWndType(TsdkVideoWndType.TSDK_E_VIDEO_WND_AUX_DATA);
+ auxDataWndInfo.setRender(ViERenderer.getIndexOfSurface(auxDataView));
+ auxDataWndInfo.setDisplayMode(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_CUT);
+ list.add(auxDataWndInfo);
+ }
+ if (tsdkCall != null) {
+ tsdkCall.setVideoWindow(list);
+ }
+ }
+ });
+ }
+
+ /**
+ * Clear data
+ */
+ public void clearCallVideo() {
+ LogUtils.d(TAG, "clearCallVideo");
+ // TODO 将这个匿名类行数减少为最多20行,或者定义为命名类
+ // TODO 这个匿名类使用lambda表达式
+ handler.post(() -> {
+ ViERenderer.freeLocalRenderResource();
+ if (localVideoView != null) {
+ ViERenderer.setSurfaceNull(localVideoView);
+ LogUtils.d("closeOrOpenFloat", "LocalVideoView赋值null");
+ localVideoView = null;
+ }
+ if (remoteVideoView != null) {
+ ViERenderer.setSurfaceNull(remoteVideoView);
+ remoteVideoView = null;
+ isAvcInitVideo = false;
+ }
+ if (auxDataView != null) {
+ ViERenderer.setSurfaceNull(auxDataView);
+ auxDataView = null;
+ }
+ if (localHideView != null) {
+ localHideView = null;
+ }
+ isInitVideoRenderer = false;
+ });
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public void setSvcWatchIndUpdateSetAll(TsdkSvcWatchInfo svcWatchInfo) {
+ LogUtils.d(TAG, "setSvcWatchIndUpdateSetAll", svcWatchInfo);
+ int callId = getCurrentCallId();
+ TsdkCall tsdkCall = callManager.getCallByCallId(callId);
+ if (tsdkCall.getCallInfo().getIsSvcCall() == 0) {
+ return;
+ }
+ int totalCount = svcWatchInfo.getTotalCount();
+ if (totalCount > 0) {
+ ArrayList allSvcWndInfoBeans = new ArrayList();
+ // 大画面或者悬浮窗选看或辅流选看
+ if (MeetingController.getInstance().getSvcWatchStatus() == SvcWatchStatus.PIP_WATCH
+ || MeetingController.getInstance().getSvcWatchStatus() == SvcWatchStatus.MINI_WATCH
+ || MeetingController.getInstance().getSvcWatchStatus() == SvcWatchStatus.AUX_DATA) {
+ int labelId = svcWatchInfo.getWatchList().get(0).getLableId();
+ TsdkAllSvcWndInfoBean bean = svcVideoWindowInfo(labelId, svcBigView, totalCount);
+ allSvcWndInfoBeans.add(bean);
+ } else {
+ for (int i = 0; i < totalCount; i++) {
+ int labelId = svcWatchInfo.getWatchList().get(i).getLableId();
+ TsdkAllSvcWndInfoBean bean = svcVideoWindowInfo(labelId, getSvcViewList().get(i), totalCount);
+ allSvcWndInfoBeans.add(bean);
+ }
+ }
+ int result = tsdkCall.setAllSvcVideoWindow(allSvcWndInfoBeans);
+ MeetingMgrV2.getInstance().diffMemberName(svcWatchInfo.getWatchList());
+ handler.removeCallbacks(mRefreshMeetingView);
+ handler.postDelayed(mRefreshMeetingView, ConstantsV2.DELAY_MILLIS_1500);
+ LogUtils.d("SVC 选看回调 " + svcWatchInfo + "setAllSvcVideoWindow result == " + result);
+ }
+ }
+
+ public void removeAllSvcVideoWindow() {
+ LogUtils.d(TAG, "removeAllSvcVideoWindow");
+ int callId = getCurrentCallId();
+ TsdkCall tsdkCall = callManager.getCallByCallId(callId);
+ if (tsdkCall == null) {
+ LogUtils.d(TAG, "removeAllSvcVideoWindow tsdkCall == null");
+ return;
+ }
+ if (tsdkCall.getCallInfo().getIsSvcCall() == 0) {
+ LogUtils.d(TAG, "removeAllSvcVideoWindow tsdkCall.getCallInfo().getIsSvcCall() == 0");
+ return;
+ }
+ ArrayList allSvcWndInfoBeans = new ArrayList();
+ TsdkAllSvcWndInfoBean svcVideoWndInfo = new TsdkAllSvcWndInfoBean();
+ allSvcWndInfoBeans.add(svcVideoWndInfo);
+ try {
+ int result = tsdkCall.setAllSvcVideoWindow(allSvcWndInfoBeans);
+ LogUtils.d("removeAllSvcVideoWindow: result = " + result);
+ } catch (Exception e) {
+ LogUtils.d("setAllSvcVideoWindow Exception:" + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ private TsdkAllSvcWndInfoBean svcVideoWindowInfo(int labelId, SurfaceView view, int totalCount) {
+ LogUtils.d(TAG, "svcVideoWindowInfo");
+ TsdkAllSvcWndInfoBean svcVideoWndInfo = new TsdkAllSvcWndInfoBean();
+ svcVideoWndInfo.setLable(labelId);
+ svcVideoWndInfo.setRender(ViERenderer.getIndexOfSurface(view));
+ svcVideoWndInfo.setIsSharpness(1);
+ int[] videoSize = MeetingController.getInstance().getVideoSize(totalCount);
+ svcVideoWndInfo.setWidth(videoSize[0]);
+ svcVideoWndInfo.setHeight(videoSize[1]);
+ if (videoSize[2] > 0) {
+ svcVideoWndInfo.setMaxBandwidth(videoSize[2]);
+ }
+ return svcVideoWndInfo;
+ }
+
+ public ArrayList getSvcViewList() {
+ LogUtils.d(TAG, "getSvcViewList", svcViewList.size());
+ return svcViewList;
+ }
+
+ /**
+ * Gets local hide view.
+ *
+ * @return the local hide view
+ */
+ public SurfaceView getLocalHideView() {
+ LogUtils.d(TAG, "getLocalHideView");
+ return localHideView;
+ }
+
+
+ /**
+ * Gets local call view.
+ *
+ * @return the local call view
+ */
+ public SurfaceView getLocalVideoView() {
+ LogUtils.d(TAG, "getLocalVideoView");
+ if (localVideoView == null) {
+ LogUtils.d("closeOrOpenFloat", "LocalVideoView=null");
+ }
+ return localVideoView;
+ }
+
+ public SurfaceView getAuxDataView() {
+ LogUtils.d(TAG, "getAuxDataView");
+ return auxDataView;
+ }
+
+ public SurfaceView getSvcBigView() {
+ LogUtils.d(TAG, "getSvcBigView");
+ if (svcBigView == null) {
+ svcBigView = ViERenderer.createRenderer(context, true);
+ svcBigView.setZOrderMediaOverlay(false);
+ }
+ return svcBigView;
+ }
+
+
+ /**
+ * Gets remote call view.
+ *
+ * @return the remote call view
+ */
+ public SurfaceView getRemoteVideoView() {
+ LogUtils.d(TAG, "getRemoteVideoView");
+ return remoteVideoView;
+ }
+
+ public int getCurrentCameraIndex() {
+ return currentCameraIndex;
+ }
+
+ public void setCurrentCameraIndex(int currentCameraIndex) {
+ this.currentCameraIndex = currentCameraIndex;
+ }
+
+ public int getCurrentCallId() {
+ return currentCallId;
+ }
+
+ public void setCurrentCallId(int currentCallId) {
+ this.currentCallId = currentCallId;
+ }
+
+ /**
+ * is support video calls
+ *
+ * @return the boolean
+ */
+ public boolean isSupportVideo() {
+ if (cameraList != null) {
+ return cameraList.size() > 0;
+ }
+ return false;
+ }
+
+ /**
+ * Set the video automatic rotation
+ * 设置视频自动旋转
+ *
+ * @param object 调用者对象
+ * @param isOpen 是否开启方向调整
+ */
+ public void setAutoRotation(Object object, boolean isOpen, int layoutDirect) {
+ if (orientationDetector == null) {
+ orientationDetector = new OrientationDetector();
+ }
+ orientationDetector.setLayoutDirect(layoutDirect);
+ orientationDetector.autoOrientationAdjust(object, isOpen);
+ }
+
+ private int tendorientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ public void changeRotation(int mOrientation) {
+ tendorientation = mOrientation;
+ }
+
+ public void cleanOrientationListener() {
+ // 去注册监听,移除摄像头旋转角度监听
+ //来电未接听挂断,子线程崩溃问题
+ if (null != orientationDetector) {
+ orientationDetector.orientationEventListenerList.clear();
+ orientationDetector.destroyOrientationListener();
+ }
+ }
+
+ /**
+ * 视频角度探测类
+ * 用于设备在旋转时,调整摄相头采集方向,以及视频窗口显示方向
+ * 1.根据角度划4象限:竖向上【0-45、315-360】;横向右【45-135】;竖向下【135-225】;横向左【225-315】
+ * 2.根据返回的页面布局方向分三种场景:横向布局、竖向布局、横向布局翻转
+ * 3.根据摄像头情况分两种场景:前置摄像头、后置摄像头
+ * 4.根据视频窗口情况分两种场景:远端窗口(对方看到自己的图像)、本端窗口(本端右下角小视频窗口)
+ */
+ private class OrientationDetector {
+ /**
+ * 布局方向,默认为竖向布局
+ */
+ private int layoutDirect = LAYOUT_PORTRAIT;
+ /**
+ * 摄相头采集旋转方向,一般是设置本端显示方向选择
+ * 比如摄像头在上时,远端和本端窗口显示的图像都是图像朝下的。
+ * 修改此参数可以旋转本端窗口显示状态。(0:0度,1:90度,2:180度,3:270度)
+ */
+ private int cameraCaptureRotation;
+
+ /**
+ * 视频窗口显示旋转方向,一般是设置远端显示方向选择
+ * 比如摄像头在上时,远端和本端窗口显示的图像都是图像朝下的。
+ * 修改此参数可以旋转远端窗口显示状态。(0:0度,1:90度,2:180度,3:270度)
+ */
+ private int windowsDisplayRotation;
+
+ /**
+ * 记录上一次的旋转方向
+ * 默认unknown
+ */
+ private int lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ /**
+ * 当前角度
+ */
+ private int curOriginalOrientation;
+
+ /**
+ * 设备摄像头旋转角度监听
+ */
+ private OrientationEventListener orientationEventListener;
+
+ /**
+ * 监听列表
+ */
+ private final List orientationEventListenerList = new ArrayList<>();
+
+ /**
+ * 构造方法
+ */
+ public OrientationDetector() {
+ // 创建监听
+ // TODO 删除注释掉的代码
+// createOrientationListener();
+ }
+
+ /**
+ * 设置布局的方向
+ *
+ * @param layoutDirect 布局方向值
+ */
+ public void setLayoutDirect(int layoutDirect) {
+ this.layoutDirect = layoutDirect;
+ }
+
+ /**
+ * 设置视频自动方向调整
+ *
+ * @param object 调用者对象
+ * @param isOpen 是否开启方向调整
+ */
+ public void autoOrientationAdjust(Object object, boolean isOpen) {
+ LogUtils.d(TAG, "autoOrientationAdjust", isOpen);
+ if (isOpen) {
+ if (orientationEventListenerList.size() == 0) {
+ // 创建监听
+ createOrientationListener();
+ }
+ // 添加调用者到监听列表
+ if (!orientationEventListenerList.contains(object)) {
+ orientationEventListenerList.add(object);
+ }
+
+ updateRotation(true);
+ } else {
+ this.lastOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
+ // 去注册监听,移除摄像头旋转角度监听
+ orientationEventListenerList.remove(object);
+ if (orientationEventListenerList.size() == 0) {
+ destroyOrientationListener();
+ }
+ }
+ }
+
+
+ /**
+ * 更新摄相头采集方向和视频窗口显示方向
+ * 为解决平放手机无法监听设备角度问题,添加参数isForce,不管是否平放,先强制主动旋转一次
+ *
+ * @param isForce 是否是强制更新
+ */
+ public void updateRotation(boolean isForce) {
+ // TODO "updateRotation"定义常量,出现超过2次
+ LogUtils.d(TAG, "updateRotation", isForce);
+ int deviceOrientation = getOrientation(curOriginalOrientation);
+ // 强制设置旋转,或与上一次不一样的区间,则进行更新设置旋转角度
+ if (deviceOrientation == -1) {
+ lastOrientation = -1;
+ return;
+ }
+ if (isForce || deviceOrientation != lastOrientation) {
+ // 更新旋转角度, 包括摄相头和显示窗口
+ updateRotation(deviceOrientation);
+ // 根据旋转角度,调用TUP接口旋转视频方向
+ setRotation(cameraCaptureRotation, windowsDisplayRotation);
+ }
+ }
+
+ /**
+ * 创建并启动设备旋转监听
+ */
+ private void createOrientationListener() {
+ // 启一个新的监听,监听设备旋转角度
+ orientationEventListener = new OrientationEventListener(BaseAppContext.getInstance()) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ curOriginalOrientation = orientation;
+ // 旋转处理,更新摄相头采集角度和视频窗口显示角度
+ if (!orientationEventListenerList.isEmpty()) {
+ updateRotation(false);
+ }
+ }
+ };
+ // 启动监听
+ orientationEventListener.enable();
+ }
+
+ /**
+ * 停止并销毁设备旋转监听
+ */
+ private void destroyOrientationListener() {
+ if (orientationEventListener != null) {
+ orientationEventListener.disable();
+ }
+ orientationEventListener = null;
+ orientationDetector = null;
+ }
+
+ // TODO 删除这个未使用的字段
+ private int temp = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ /**
+ * 盲区角度
+ */
+ public int blindSize = 25;
+
+ /**
+ * 根据捕捉到的摄像头方向划分四个象限,不属于四象限则返回unknown不做处理
+ * 四个象限分别是:竖向上【0-60、300-360】;横向右【60-120】;竖向下【120-240】;横向左【240-300】
+ *
+ * @param orientation 捕捉到的设备摄像头角度
+ * @return 角度所属象限
+ */
+
+ // TODO 重构此方法以将其认知复杂性从19降低到允许的15
+ private int getOrientation(int orientation) {
+ // TODO "updateRotation"定义常量,出现超过2次
+ LogUtils.d(TAG, "updateRotation", orientation);
+ // TODO 45/315/135/225/定义为常量使用
+ if ((orientation >= 0 && orientation <= (45 - blindSize)) || (orientation >= (315 + blindSize) && orientation <= 360)/* || orientation == -1*/) {
+ //竖屏
+ if (tendorientation != 1) {
+ temp = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ // TODO 删除注释掉的代码
+// temp = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ }
+ return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ } else if (orientation > (45 + blindSize) && orientation < (135 - blindSize)) {
+ //反向横屏 底部在右边,摄像头在右边
+ if (tendorientation != 1) {
+ temp = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+ }
+ return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+ } else if (orientation >= (135 + blindSize) && orientation <= (225 - blindSize)) {
+ //反向竖屏,摄像头在下方
+ if (tendorientation != 1) {
+ temp = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ }
+ return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+ } else if (orientation > (225 + blindSize) && orientation < 315 - blindSize) {
+ //横屏
+ if (tendorientation != 1) {
+ temp = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ }
+ return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ } else {
+ // TODO 删除注释掉的代码
+// LogUtil.zzz(TAG, "ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED getOrientation: " + tendorientation + "--" + orientation);
+// return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+ return -1;
+ }
+ }
+
+ /**
+ * 根据不同的象限区间设置旋转角度
+ *
+ * @param deviceOrientation 象限区间
+ */
+ // TODO 方法圈复杂度为12,大于10,重构使代码圈复杂度不超过10
+ private void updateRotation(int deviceOrientation) {
+ // TODO "updateRotation"定义常量,出现超过2次
+ LogUtils.d(TAG, "updateRotation", deviceOrientation);
+ int callId = getCurrentCallId();
+ TsdkCall tsdkCall = callManager.getCallByCallId(callId);
+ if (tsdkCall == null) {
+ return;
+ }
+ int isSvcCall = tsdkCall.getCallInfo().getIsSvcCall();
+ int isFocus = tsdkCall.getCallInfo().getIsFocus();
+ switch (deviceOrientation) {
+ // TODO 将代码提取到方法中,将这个switch case的行数从11减少到最多5行
+ case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT:
+ // AVC会议不进行竖屏象限处理
+ if (isFocus == 1 && isSvcCall == 0) {
+ if (currentCameraIndex == CallConstant.FRONT_CAMERA) {
+ cameraCaptureRotation = 3;
+ // TODO "if ... else if"应该以else子语句结束
+ } else if (currentCameraIndex == CallConstant.BACK_CAMERA) {
+ cameraCaptureRotation = 1;
+ }
+ LogUtils.d(TAG, "设备竖向,摄像头在上时的旋转角度设置 , AVC会议 不旋转: " + deviceOrientation);
+ } else {
+ //设备竖向,摄像头在上时的旋转角度设置
+ setOrientationPortraitUp();
+ }
+ break;
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE:
+ //设备反向横向,摄像头在右时的旋转角度设置
+ setOrientationLandscapeRight();
+ break;
+ case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT:
+ // AVC会议不进行竖屏象限处理
+ if (isFocus == 1 && isSvcCall == 0) {
+ LogUtils.d(TAG, "设备反向竖向,摄像头在下时的旋转角度设置 , AVC会议 不旋转: " + deviceOrientation);
+ } else {
+ //设备反向竖向,摄像头在下时的旋转角度设置
+ setOrientationPortraitDown();
+ }
+ break;
+ case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE:
+ //设备横向,摄像头在左时的旋转角度设置
+ setOrientationLandscapeLeft();
+ break;
+ default:
+ break;
+ }
+ // 记录上一次的象限区间
+ lastOrientation = deviceOrientation;
+ }
+
+ /**
+ * 设备横向,摄像头在左时的旋转角度设置
+ */
+ private void setOrientationLandscapeLeft() {
+ LogUtils.d(TAG, "setOrientationLandscapeLeft");
+ if (isLayoutPortrait()) {
+ // 前置后置摄像头旋转角度一致
+ cameraCaptureRotation = 0;
+ windowsDisplayRotation = 0;
+ } else {
+ // 前置后置摄像头旋转角度一致
+ cameraCaptureRotation = 0;
+ windowsDisplayRotation = 0;
+ }
+ }
+
+ /**
+ * 设备反向横向,摄像头在右时的旋转角度设置
+ */
+ private void setOrientationLandscapeRight() {
+ LogUtils.d(TAG, "setOrientationLandscapeRight");
+ if (isLayoutPortrait()) {
+ // 前置后置摄像头旋转角度一致
+ // TODO 2使用常量
+ cameraCaptureRotation = 2;
+ windowsDisplayRotation = 0;
+ } else {
+ // 前置后置摄像头旋转角度一致
+ // TODO 2使用常量
+ cameraCaptureRotation = 2;
+ windowsDisplayRotation = 0;
+ }
+ }
+
+
+ /**
+ * 设备竖向,摄像头在上时的旋转角度设置
+ */
+ private void setOrientationPortraitUp() {
+ LogUtils.d(TAG, "setOrientationPortraitUp");
+ if (isLayoutPortrait()) {
+ if (VideoMgr.getInstance().getCurrentCameraIndex() == CallConstant.FRONT_CAMERA) {
+ // TODO 3使用常量
+ cameraCaptureRotation = 3;
+ windowsDisplayRotation = 0;
+ } else {
+ cameraCaptureRotation = 1;
+ windowsDisplayRotation = 0;
+ }
+ } else {
+ if (VideoMgr.getInstance().getCurrentCameraIndex() == CallConstant.FRONT_CAMERA) {
+ // TODO 3使用常量
+ cameraCaptureRotation = 3;
+ windowsDisplayRotation = 0;
+ } else {
+ cameraCaptureRotation = 1;
+ windowsDisplayRotation = 0;
+ }
+ }
+ }
+
+ /**
+ * 设备反向竖向,摄像头在下时的旋转角度设置
+ */
+ private void setOrientationPortraitDown() {
+ LogUtils.d(TAG, "setOrientationPortraitDown", isLayoutPortrait());
+ // TODO 将System.out或System.err的这种用法替换为Log。
+ System.out.println("isLayoutPortrait" + isLayoutPortrait());
+ if (isLayoutPortrait()) {
+ if (VideoMgr.getInstance().getCurrentCameraIndex() == CallConstant.FRONT_CAMERA) {
+ // TODO 3使用常量
+ cameraCaptureRotation = 3;
+ // TODO 2使用常量
+ windowsDisplayRotation = 2;
+ } else {
+ cameraCaptureRotation = 1;
+ // TODO 2使用常量
+ windowsDisplayRotation = 2;
+ }
+ } else {
+ if (VideoMgr.getInstance().getCurrentCameraIndex() == CallConstant.FRONT_CAMERA) {
+ cameraCaptureRotation = 0;
+ // TODO 2使用常量
+ windowsDisplayRotation = 2;
+ } else {
+ cameraCaptureRotation = 0;
+ // TODO 2使用常量
+ windowsDisplayRotation = 2;
+ }
+ }
+ }
+
+ /**
+ * 判断是否是竖向布局
+ *
+ * @return true:是;false:否
+ */
+ private boolean isLayoutPortrait() {
+ return layoutDirect == LAYOUT_PORTRAIT;
+ }
+
+ /**
+ * 设置旋转角度
+ *
+ * @param cameraCaptureRotation 摄像头采集方向
+ * @param windowsDisplayRotation 窗口显示方向
+ */
+ private void setRotation(int cameraCaptureRotation, int windowsDisplayRotation) {
+ LogUtils.d(TAG, "setRotation", cameraCaptureRotation, windowsDisplayRotation);
+ currentCallId = VideoMgr.getInstance().getCurrentCallId();
+ if (VideoMgr.getInstance().getCurrentCameraIndex() == CallConstant.FRONT_CAMERA) {
+ if (MeetingController.getInstance().isVideoOpen) {
+ // 1表示前置摄像头
+ LogUtils.d("isAux" + MeetingController.getInstance().isAux());
+ setCaptureRotation(1, cameraCaptureRotation);
+ }
+ setLocalVideoDisplayRotation(1, windowsDisplayRotation);
+ } else if (VideoMgr.getInstance().getCurrentCameraIndex() == CallConstant.BACK_CAMERA) {
+ if (MeetingController.getInstance().isVideoOpen) {
+ // 0表示后置摄像头
+ LogUtils.d("isAux" + MeetingController.getInstance().isAux());
+ setCaptureRotation(0, cameraCaptureRotation);
+ }
+ setLocalVideoDisplayRotation(0, windowsDisplayRotation);
+ } else {
+ // -1表示摄相头关闭
+ // do nothing
+ }
+
+ setRemoteVideoDisplayRotation(windowsDisplayRotation);
+
+ }
+
+
+ /**
+ * This method is used to set camera capture rotation
+ * 设置视频采集方向
+ *
+ * @param index
+ * @param rotation
+ * @return
+ */
+ public boolean setCaptureRotation(int index, int rotation) {
+ LogUtils.d(TAG, "setCaptureRotation", index, rotation);
+ TsdkCall tsdkCall = TsdkManager.getInstance().getCallManager().getCallByCallId(currentCallId);
+ if (null == tsdkCall) {
+ return false;
+ }
+ LogUtils.d("setCaptureRotation");
+ tsdkCall.setCaptureRotation(index, rotation);
+
+ return true;
+ }
+
+
+ /**
+ * This method is used to set local video window display rotation
+ * 设置本地视频窗口显示方向
+ *
+ * @param cameraIndex
+ * @param rotation
+ * @return
+ */
+ public boolean setLocalVideoDisplayRotation(int cameraIndex, int rotation) {
+ LogUtils.d(TAG, "setLocalVideoDisplayRotation", cameraIndex, rotation);
+ TsdkCall tsdkCall = TsdkManager.getInstance().getCallManager().getCallByCallId(currentCallId);
+ if (null == tsdkCall) {
+ return false;
+ }
+
+ // 前置摄像头
+ if (1 == cameraIndex) {
+ // 窗口镜像模式 0:不做镜像(默认值) 1:上下镜像(目前未支持) 2:左右镜像
+ // 本地视频前置摄像头做左右镜像,所以设置mirror type为 2
+ TsdkVideoRenderInfo videoRenderInfo = new TsdkVideoRenderInfo();
+ videoRenderInfo.setRenderType(TsdkVideoWndType.TSDK_E_VIDEO_WND_LOCAL);
+ videoRenderInfo.setMirrorType(TsdkVideoWndMirrorType.TSDK_E_VIDEO_WND_MIRROR_HORIZONTAL);
+ videoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_FULL);
+// videoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_CUT);//小窗口黑边模式
+
+ tsdkCall.setVideoRender(videoRenderInfo);
+ } else {
+ TsdkVideoRenderInfo videoRenderInfo = new TsdkVideoRenderInfo();
+ videoRenderInfo.setRenderType(TsdkVideoWndType.TSDK_E_VIDEO_WND_LOCAL);
+ videoRenderInfo.setMirrorType(TsdkVideoWndMirrorType.TSDK_E_VIDEO_WND_MIRROR_DEFAULE);
+ videoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_FULL);
+// videoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_CUT);//小窗口黑边模式
+
+ tsdkCall.setVideoRender(videoRenderInfo);
+ }
+
+ tsdkCall.setDisplayRotation(TsdkVideoWndType.TSDK_E_VIDEO_WND_LOCAL, rotation);
+
+ return true;
+ }
+
+ /**
+ * This method is used to set remote video window display rotation
+ * 设置远端视频显示方向
+ *
+ * @param rotation
+ * @return
+ */
+ public boolean setRemoteVideoDisplayRotation(int rotation) {
+ LogUtils.d(TAG, "setRemoteVideoDisplayRotation", rotation);
+ TsdkCall tsdkCall = TsdkManager.getInstance().getCallManager().getCallByCallId(currentCallId);
+ if (null == tsdkCall) {
+ return false;
+ }
+
+ TsdkVideoRenderInfo remoteVideoRenderInfo = new TsdkVideoRenderInfo();
+
+ //todo 修改远端视频流方向
+
+ if (isLayoutPortrait()) {
+ remoteVideoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_CUT);
+// remoteVideoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_FULL);//演示版本,裁剪模式
+ } else {
+ remoteVideoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_CUT);
+// remoteVideoRenderInfo.setDisplayType(TsdkVideoWndDisplayMode.TSDK_E_VIDEO_WND_DISPLAY_FULL);//演示版本,裁剪模式
+ }
+ remoteVideoRenderInfo.setRenderType(TsdkVideoWndType.TSDK_E_VIDEO_WND_REMOTE);
+ remoteVideoRenderInfo.setMirrorType(TsdkVideoWndMirrorType.TSDK_E_VIDEO_WND_MIRROR_DEFAULE);
+
+ tsdkCall.setVideoRender(remoteVideoRenderInfo);
+
+ tsdkCall.setDisplayRotation(TsdkVideoWndType.TSDK_E_VIDEO_WND_REMOTE, rotation);
+
+ return true;
+ }
+
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/WatchInfo.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/WatchInfo.java
new file mode 100644
index 0000000..3c0bf4c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/manager/WatchInfo.java
@@ -0,0 +1,36 @@
+package com.tengshisoft.chatmodule.hwclud.manager;
+
+import android.view.SurfaceView;
+
+public class WatchInfo {
+
+ private int sSrc;
+
+ private SurfaceView view;
+
+ private boolean isAddToWindow;
+
+ public int getsSrc() {
+ return sSrc;
+ }
+
+ public void setsSrc(int sSrc) {
+ this.sSrc = sSrc;
+ }
+
+ public SurfaceView getView() {
+ return view;
+ }
+
+ public void setView(SurfaceView view) {
+ this.view = view;
+ }
+
+ public boolean isAddToWindow() {
+ return isAddToWindow;
+ }
+
+ public void setAddToWindow(boolean addToWindow) {
+ isAddToWindow = addToWindow;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/notification/CallFunc.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/notification/CallFunc.java
new file mode 100644
index 0000000..13d15fa
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/notification/CallFunc.java
@@ -0,0 +1,464 @@
+package com.tengshisoft.chatmodule.hwclud.notification;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.base.TsdkCallInfo;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.tengshisoft.chatmodule.activity.InvitedPointCallActivity;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.beans.CallConstant;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.IntentConstant;
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.listener.ICallNotification;
+import com.tengshisoft.chatmodule.hwclud.listener.ICtdNotification;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.ActivityUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.AppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.FileUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.LocContext;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.NotifyUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.PermissionUtils;
+import com.tengshisoft.chatmodule.hwclud.utils.RomUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.io.File;
+
+import androidx.annotation.RequiresApi;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.CONF_VIDEO_COMING;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.VOICE_ACCESS_CONF_ING;
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.VOICE_CONF_COMING;
+
+
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class CallFunc implements ICallNotification, ICtdNotification {
+ private static final int UPGRADE_FAILED = 100;
+ private static final int CTD_FAILED = 101;
+ private static final int CTD_SUCCESS = 102;
+ public static final String RINGING_FILE = "ringing.wav";
+ public static final String RING_BACK_FILE = "ring_back.wav";
+
+ private boolean mMuteStatus;
+ private long startTime = -1L;
+ private int status = -1;
+ private static volatile CallFunc mInstance;
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_CONF_INCOMING_TO_CALL_INCOMING};
+
+ private CallFunc() {
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ }
+
+ private LocalBroadcastReceiver receiver = (broadcastName, obj) -> {
+ switch (broadcastName) {
+ // todo 暂无作用
+ case BroadcastConstant.ACTION_CONF_INCOMING_TO_CALL_INCOMING:
+ if (obj instanceof TsdkConference) {
+ TsdkConference tsdkConference = (TsdkConference) obj;
+ TsdkCallInfo tsdkcallInfo = tsdkConference.getCall().getCallInfo();
+ CallInfo callInfo = new CallInfo();
+ callInfo.setCallID(tsdkcallInfo.getCallId());
+ callInfo.setFocus(true);
+ callInfo.setCaller(false);
+ callInfo.setPeerNumber(tsdkcallInfo.getPeerNumber());
+ callInfo.setPeerDisplayName(tsdkcallInfo.getPeerDisplayName());
+ callInfo.setVideoCall(tsdkcallInfo.getIsVideoCall() != 0);
+ callInfo.setConfID(tsdkConference.getHandle() + "");
+ String mFilePath = Environment.getExternalStorageDirectory() + File.separator + RINGING_FILE;
+ CallMgrV2.getInstance().startPlayRingingTone(mFilePath);
+ if (callInfo.isVideoCall()) {
+ // 传入参数
+ String confID = ((TsdkConference) obj).getConfId();
+ Intent intent = new Intent(BaseAppContext.getInstance(),
+ InvitedPointCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Constant.CONF_ID, confID);
+ LogUtil.zzz("CallFunc", "confID", LogUtil.commonDisplay(confID));
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, CONF_VIDEO_COMING);
+ ActivityUtil.startActivity(LocContext.getContext(), intent);
+ } else {
+ Intent intent = new Intent(BaseAppContext.getInstance(),
+ InvitedPointCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.addCategory(IntentConstant.DEFAULT_CATEGORY);
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, 1);
+ intent.putExtra(Constant.CALL_INFO, callInfo);
+ ActivityUtil.startActivity(LocContext.getContext(), intent);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ };
+
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case UPGRADE_FAILED:
+ break;
+ case CTD_FAILED:
+ break;
+ case CTD_SUCCESS:
+
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ public static CallFunc getInstance() {
+ if (mInstance == null) {
+ synchronized (CallFunc.class) {
+ if (mInstance == null) {
+ mInstance = new CallFunc();
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ private static final String TAG = "CallFunc";
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ @Override
+ public void onCallEventNotify(CallConstant.CallEvent event, Object obj) {
+ switch (event) {
+ // 来电
+ case CALL_COMING:
+ LogUtils.d(LogUtil.CLOUNDLINK, "call coming!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_COMING, obj);
+ if (obj instanceof CallInfo) {
+ CallInfo callInfo = (CallInfo) obj;
+ boolean isConf = callInfo.isFocus();
+ boolean isVideo = callInfo.isVideoCall();
+ LogUtils.d(UIUtil.isForeground());
+ if (!UIUtil.isForeground()) {
+ MeetingController.getInstance().setTempCallInfo(callInfo);
+ LogUtils.d(BaseAppContext.getInstance().isScreenOn());
+ if (!BaseAppContext.getInstance().isScreenOn()) {
+ String name = TextUtils.isEmpty(callInfo.getPeerDisplayName()) ?
+ callInfo.getPeerNumber() : callInfo.getPeerDisplayName();
+ NotifyUtil.showCallPointNotify(name, isConf, isVideo);
+ return;
+ }
+ if (RomUtil.isMiui()) {
+ if (!RomUtil.canBackStartForXiaoMi(BaseAppContext.getInstance())) {
+ return;
+ }
+
+ } else if (RomUtil.isVivo()) {
+ if (!RomUtil.canBackStartForVivo(BaseAppContext.getInstance())) {
+ return;
+ }
+ } else {
+ if (!PermissionUtils.checkFloatWindowPermission(BaseAppContext.getInstance())) {
+ return;
+ }
+ }
+ }
+
+ Intent intent = new Intent(BaseAppContext.getInstance(), InvitedPointCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Constant.CALL_INFO, callInfo);
+ if (isConf) {
+ // 是否是会议
+ if (isVideo) {
+ LogUtil.zzz("视频会议来电");
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, CONF_VIDEO_COMING);
+ } else {
+ LogUtil.zzz("语音会议来电");
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, VOICE_CONF_COMING);
+ }
+ } else {
+ LogUtils.e("点对点来电");
+ LogUtil.zzz("点呼来电");
+ if (callInfo.isVideoCall()) {
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.POINT_VIDEOED);
+ } else {
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.POINT_CALLED);
+ }
+ }
+ // 接听前,如果会议页面存在,销毁
+ if (AppUtil.isActivityTop(SponsorMeetingActivity.class, BaseAppContext.getInstance())) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_END, null);
+ }
+ LogUtils.d("界面启动start");
+ BaseAppContext.getInstance().startActivity(intent);
+ LogUtils.d("界面启动end");
+ UIUtil.wakeUpAndUnlock();
+ }
+ break;
+ // 去电
+ case CALL_GOING:
+ LogUtil.i(LogUtil.CLOUNDLINK, "call going!");
+ // 2.0 vmr帐号 建立连接后将会议id存在本地
+ if (obj instanceof CallInfo) {
+ CallInfo callInfo = (CallInfo) obj;
+ if (!TextUtils.isEmpty(callInfo.getPeerNumber())) {
+ LogUtil.zzz("CALL_GOING", "peerNumber:" + LogUtil.commonDisplay(callInfo.getPeerNumber()));
+ if (!UIUtil.isService3() && !CallMgrV2.getInstance().getReferCall()) {
+ String peerNumber = callInfo.getPeerNumber();
+ String[] peerArray = peerNumber.split("\\*");
+ EncryptedSPTool.putString(Constant.IS_VMR_2_ID, peerArray[0]);
+ LogUtil.zzz("CALL_GOING", "VMR:" + LogUtil.commonDisplay(peerArray[0]));
+ }
+ }
+ }
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_GOING, obj);
+ break;
+ // 播放回铃音
+ case PLAY_RING_BACK_TONE:
+ LogUtil.i(LogUtil.CLOUNDLINK, "play ring back!");
+ if (FileUtil.isSdCardExist()) {
+ String mFilePath = Environment.getExternalStorageDirectory() + File.separator + RING_BACK_FILE;
+ CallMgrV2.getInstance().startPlayRingBackTone(mFilePath);
+ }
+ break;
+
+ // 媒体通道建立
+ case RTP_CREATED:
+ if (obj instanceof CallInfo) {
+ // 对方点接听
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_MEDIA_CONNECTED, obj);
+ }
+
+ break;
+
+ // 呼叫建立成功
+ case CALL_CONNECTED:
+ // 对方点之后走这个
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "call connected ");
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ if (obj instanceof TsdkCall) {
+ TsdkCall call = (TsdkCall) obj;
+ CallInfo callInfo = CallMgrV2.getInstance().getCallInfo(call);
+ String peerNumber = callInfo.getPeerNumber();
+ String[] peerArray = peerNumber.split("\\*");
+ EncryptedSPTool.putString(Constant.IS_VMR_3_ID, peerArray[0]);
+ if (callInfo.isFocus()) {
+ LogUtil.zzz(TAG, "会话连接 handleCallConnected: ");
+ VideoMgr.getInstance().initVideoWindow(call.getCallInfo().getCallId());
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_CALL_CONNECTED, obj);
+ if (callInfo.isVideoCall()) {
+ LogUtil.zzz(TAG, "CALL_CONNECTED isVideoCall");
+ Intent intent = new Intent(LocContext.getContext(), SponsorMeetingActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(Constant.MY_CONF_INFO,
+ new MyTsdkCallInfo(call.getCallInfo().getConfId(),
+ "",
+ callInfo.getPeerNumber(),
+ true,
+ call.getCallInfo().getIsSvcCall() != 0,
+ Integer.parseInt(call.getCallInfo().getConfId()),
+ call.getCallInfo().getSsrcTableStart(),
+ call.getCallInfo().getSsrcTableEnd(),
+ call.getCallInfo().getCallId()));
+ intent.putExtras(bundle);
+ if (!MeetingController.getInstance().isAux()) {
+ LogUtil.zzz(TAG, "start SponsorMeetingActivity");
+ LocContext.getContext().startActivity(intent);
+ }
+ Intent it = new Intent();
+ it.setAction(BroadcastConstant.ACTION_JOIN_CONF_SUCESSS);
+ LocalBroadcastManager.getInstance(LocContext.getContext()).sendBroadcast(it);
+ } else {
+ LogUtil.zzz(TAG, "CALL_CONNECTED is no video");
+ // 如果是语音会议,来电和接听在一个界面,不用再启动界面,(目前是又启动了一次界面)或者就要在OnNewIntent中处理这些逻辑
+ Intent intent = new Intent(BaseAppContext.getInstance(), InvitedPointCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, VOICE_ACCESS_CONF_ING);
+ intent.putExtra(Constant.ID, callInfo.getPeerNumber());
+ intent.putExtra(Constant.CONF_ID, String.valueOf(callInfo.getConfID()));
+ intent.putExtra(Constant.IS_VIDEO_CONF, false);
+ BaseAppContext.getInstance().startActivity(intent);
+ Intent it = new Intent();
+ it.setAction(BroadcastConstant.ACTION_JOIN_CONF_SUCESSS);
+ LocalBroadcastManager.getInstance(LocContext.getContext()).sendBroadcast(it);
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_JOIN_CONF_SUCCESS_FINISH_SPONSOR, 0);
+ }
+ } else {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_CONNECTED, callInfo);
+ }
+ }
+ break;
+
+ // 呼叫结束
+ case CALL_ENDED:
+ if (obj instanceof CallInfo) {
+ MeetingController.getInstance().setTempCallInfo(null);
+ // 呼叫可能没有接通,结束时停止可能存在的振铃音和回铃音
+ CallMgrV2.getInstance().stopPlayRingingTone();
+ CallMgrV2.getInstance().stopPlayRingBackTone();
+ CallInfo params = (CallInfo) obj;
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_END, params);
+ resetData();
+ }
+ break;
+
+ // 语音呼叫保持成功
+ case AUDIO_HOLD_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_HOLD_CALL_RESULT, "HoldSuccess");
+ break;
+
+ // 语音呼叫保持失败
+ case AUDIO_HOLD_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_HOLD_CALL_RESULT, "HoldFailed");
+ break;
+
+ // 视频呼叫保持成功
+ case VIDEO_HOLD_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_HOLD_CALL_RESULT, "VideoHoldSuccess");
+ break;
+
+ // 视频呼叫保持失败
+ case VIDEO_HOLD_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_HOLD_CALL_RESULT, "VideoHoldFailed");
+ break;
+
+ // 取消保持(恢复)成功
+ case UN_HOLD_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_HOLD_CALL_RESULT, "UnHoldSuccess");
+ break;
+
+ // 取消保持(恢复)失败
+ case UN_HOLD_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_HOLD_CALL_RESULT, "UnHoldFailed");
+ break;
+
+ // 偏转失败
+ case DIVERT_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_DIVERT_RESULT, "DivertFailed");
+ break;
+
+ // 盲转成功
+ case BLD_TRANSFER_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_BLD_TRANSFER_RESULT, "BldTransferSuccess");
+ break;
+
+ // 盲转失败
+ case BLD_TRANSFER_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_BLD_TRANSFER_RESULT, "BldTransferFailed");
+ break;
+
+ // 关闭视频
+ case CLOSE_VIDEO:
+ LogUtil.i(LogUtil.CLOUNDLINK, "close video.");
+ if (obj instanceof CallInfo) {
+ CallInfo callInfo = (CallInfo) obj;
+ boolean isFocus = callInfo.isFocus();
+ if (!isFocus) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_VIDEO_TO_AUDIO, obj);
+ }
+ }
+ break;
+
+ // 打开视频
+ case OPEN_VIDEO:
+ LogUtil.i(LogUtil.CLOUNDLINK, "open video.");
+
+ if (obj instanceof CallInfo) {
+ CallInfo callInfo = (CallInfo) obj;
+ LogUtil.zzz("对方同意打开视频", callInfo);
+
+ boolean isFocus = callInfo.isFocus();
+
+ if (!isFocus) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_OPEN_VIDEO, obj);
+ }
+ }
+ break;
+ // 远端拒绝增加视频请求
+ case REMOTE_REFUSE_ADD_VIDEO_SREQUEST:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_REFUSE_OPEN_VIDEO, obj);
+ break;
+ case ADD_LOCAL_VIEW:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_ADD_LOCAL_VIEW, obj);
+ break;
+
+ case DEL_LOCAL_VIEW:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_DEL_LOCAL_VIEW, obj);
+ break;
+
+
+ // 收到远端增加视频请求
+ case RECEIVED_REMOTE_ADD_VIDEO_REQUEST:
+ LogUtil.i(LogUtil.CLOUNDLINK, "Add video call!");
+ LogUtil.i("zzz", "对方请求打开视频");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_UPGRADE_ACTION, obj);
+ break;
+ case SESSION_MODIFIED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SESSION_MODIFIED_RESULT, obj);
+ break;
+
+ case CALL_ENDED_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_END_FAILED, obj);
+ break;
+
+ case DEVICE_CHANGED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_DEVICES_STATUS_CHANGE, obj);
+ break;
+ case REFRESH_ROUTE:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_ROUTE_CHANGE, obj);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void resetData() {
+ // mPlayHandle = -1;
+ mMuteStatus = false;
+ }
+
+ // false是开音 true是闭音
+ public boolean isMuteStatus() {
+ return mMuteStatus;
+ }
+
+ public void setMuteStatus(boolean mMuteStatus) {
+ this.mMuteStatus = mMuteStatus;
+ }
+
+ @Override
+ public void onStartCtdCallResult(int result, String description) {
+ LogUtil.i(LogUtil.CLOUNDLINK, "onStartCtdCallResult");
+ if (result == 0) {
+ mHandler.sendEmptyMessage(CTD_SUCCESS);
+ } else {
+ mHandler.sendEmptyMessage(CTD_FAILED);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/notification/ConfFunc.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/notification/ConfFunc.java
new file mode 100644
index 0000000..ef761ed
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/notification/ConfFunc.java
@@ -0,0 +1,403 @@
+package com.tengshisoft.chatmodule.hwclud.notification;
+
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import com.huawei.ecterminalsdk.base.TsdkCallInfo;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.hwclud.listener.IConfNotification;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.ActivityUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LocContext;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.TimerUtil;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConfConstant;
+
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+
+public class ConfFunc implements IConfNotification {
+ private static final int BOOK_CONF_SUCCESS = 100;
+ private static final int BOOK_CONF_FAILED = 101;
+ private static final int QUERY_CONF_LIST_SUCCESS = 102;
+ private static final int QUERY_CONF_LIST_FAILED = 103;
+ private static final int QUERY_CONF_DETAIL_FAILED = 104;
+ private static final int QUERY_CONF_DETAIL_SUCCESS = 105;
+ private static final int GET_VMR_LIST_FAILED = 106;
+
+ private static final int JOIN_VOICE_CONF_SUCCESS = 109;
+ private static final int JOIN_VIDEO_CONF_SUCCESS = 110;
+ private static final int JOIN_CONF_FAILED = 111;
+ private static final int QUERY_CONF_LIST_NULL = 112;
+ private static final String TAG = "ConfFunc";
+ private static ConfFunc mInstance = new ConfFunc();
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_CONF_INFO_PARAM};
+
+ private ConfFunc() {
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ }
+
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case QUERY_CONF_LIST_FAILED:
+ LogUtil.zzz("query conf list failed.");
+ break;
+
+ case QUERY_CONF_DETAIL_FAILED:
+ LogUtil.zzz("query conf detail failed.");
+ break;
+
+ case JOIN_VOICE_CONF_SUCCESS:
+ Log.d(TAG, "语音通道建立 handleMessage: JOIN_VOICE_CONF_SUCCESS ");
+ if (msg.obj instanceof TsdkConference) {
+ TsdkConference tsdkConference = (TsdkConference) msg.obj;
+// Intent intent = new Intent(App.getContext(), InvitedPointCallActivity.class);
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.VOICE_ACCESS_CONF_ING);
+ intent.putExtra(Constant.ID, tsdkConference.getCall().getCallInfo().getPeerNumber());
+ intent.putExtra(Constant.CONF_ID, String.valueOf(tsdkConference.getHandle()));
+ intent.putExtra(Constant.IS_VIDEO_CONF, false);
+ ActivityUtil.startActivity(LocContext.getContext(), intent);
+ Intent it = new Intent();
+ it.setAction(BroadcastConstant.ACTION_JOIN_CONF_SUCESSS);
+ LocalBroadcastManager.getInstance(LocContext.getContext()).sendBroadcast(it);
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_JOIN_CONF_SUCCESS_FINISH_SPONSOR, 0);
+ }
+ break;
+ case JOIN_VIDEO_CONF_SUCCESS:
+ Log.d(TAG, "视频通道建立 handleMessage: JOIN_VIDEO_CONF_SUCCESS ");
+ // todo 视频处理方式
+ if (msg.obj instanceof TsdkConference) {
+ TsdkConference tsdkConference = (TsdkConference) msg.obj;
+ Intent intent = new Intent(LocContext.getContext(), SponsorMeetingActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ Bundle bundle = new Bundle();
+ TsdkCallInfo callInfo = tsdkConference.getCall().getCallInfo();
+// LogUtil.zzz("aaaaaa","原始",callInfo);
+ MyTsdkCallInfo myTsdkCallInfo = new MyTsdkCallInfo(tsdkConference.getConfId(),
+ tsdkConference.getSubject(),
+ callInfo.getPeerNumber(),
+ true,
+ callInfo.getIsSvcCall() != 0,
+ tsdkConference.getHandle(),
+ callInfo.getSsrcTableStart(),
+ callInfo.getSsrcTableEnd(),
+ callInfo.getCallId());
+ bundle.putSerializable(Constant.MY_CONF_INFO,
+ myTsdkCallInfo);
+// LogUtil.zzz("aaaaaa","赋值",myTsdkCallInfo);
+ intent.putExtras(bundle);
+// intent.putExtra("Meeting_Type", tsdkConference.isLiveBroadcast());
+ LocContext.getContext().startActivity(intent);
+ Intent it = new Intent();
+ it.setAction(BroadcastConstant.ACTION_JOIN_CONF_SUCESSS);
+ LocalBroadcastManager.getInstance(LocContext.getContext()).sendBroadcast(it);
+ }
+ break;
+ case JOIN_CONF_FAILED:
+// ToastUtil.toast("join conf failed.");
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+
+ private LocalBroadcastReceiver receiver = new LocalBroadcastReceiver() {
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+// switch (broadcastName)
+// {
+// default:
+// break;
+// }
+ }
+ };
+
+
+ public static ConfFunc getInstance() {
+ return mInstance;
+ }
+
+ @Override
+ public void onConfEventNotify(ConfConstant.CONF_EVENT confEvent, Object params) {
+ LogUtil.zzz(TAG, "confEvent" + confEvent);
+ switch (confEvent) {
+ case BOOK_CONF_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_BOOK_CONF_SUCCESS, params);
+ break;
+
+ case BOOK_CONF_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_BOOK_CONF_FAILED, params);
+ break;
+
+ case QUERY_CONF_LIST_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_CONF_LIST_FAILED, params);
+ break;
+ case QUERY_CONF_LIST_NULL:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_CONF_LIST_NULL, params);
+ break;
+
+ case QUERY_CONF_LIST_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_CONF_LIST_RESULT, params);
+ break;
+
+ case QUERY_CONF_DETAIL_FAILED:
+ mHandler.sendEmptyMessage(QUERY_CONF_DETAIL_FAILED);
+ break;
+
+ case JOIN_VOICE_CONF_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_CALL_CONNECTED, params);
+ mHandler.sendMessage(mHandler.obtainMessage(JOIN_VOICE_CONF_SUCCESS, params));
+ break;
+ case GET_VMR_LIST_FAILED:
+ mHandler.sendEmptyMessage(GET_VMR_LIST_FAILED);
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_VMR_LIST_FAIL, params);
+ break;
+
+ case GET_VMR_LIST_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_VMR_LIST_RESULT, params);
+ break;
+ case JOIN_VIDEO_CONF_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_CALL_CONNECTED, params);
+ try {
+ mHandler.sendMessage(mHandler.obtainMessage(JOIN_VIDEO_CONF_SUCCESS, params));
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ break;
+
+ /*case JOIN_ACCESS_VOICE_CONF_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_JOIN_ACCESS_VOICE_CONF_SUCCESS, params);
+ break;
+
+ case JOIN_ACCESS_VIDEO_CONF_SUCCESS:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_JOIN_ACCESS_VIDEO_CONF_SUCCESS, params);
+ break;*/
+
+ case JOIN_CONF_FAILED:
+ TimerUtil.delay(500, () -> {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_CALL_FAILED, params);
+ });
+ mHandler.sendEmptyMessage(JOIN_CONF_FAILED);
+ break;
+
+ case REQUEST_RIGHT_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_REQUEST_CONF_RIGHT_RESULT, params);
+ break;
+
+ case STATE_UPDATE:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_STATE_UPDATE, params);
+ break;
+
+ case ADD_YOURSELF_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_ADD_SELF_RESULT, params);
+ break;
+
+ case ADD_ATTENDEE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_ADD_ATTENDEE_RESULT, params);
+ break;
+
+ case DEL_ATTENDEE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_DEL_ATTENDEE_RESULT, params);
+ break;
+
+ case MUTE_ATTENDEE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_MUTE_ATTENDEE_RESULT, params);
+ break;
+
+ case UN_MUTE_ATTENDEE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_UN_MUTE_ATTENDEE_RESULT, params);
+ break;
+
+ case MUTE_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_MUTE_CONF_RESULT, params);
+ break;
+
+ case UN_MUTE_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_UN_MUTE_CONF_RESULT, params);
+ break;
+
+ case LOCK_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_LOCK_CONF_RESULT, params);
+ break;
+
+ case UN_LOCK_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_UN_LOCK_CONF_RESULT, params);
+ break;
+ // 举手
+ case HAND_UP_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_HAND_UP_RESULT, params);
+ break;
+ // 取消举手
+ case CANCEL_HAND_UP_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CANCEL_HAND_UP_RESULT, params);
+ break;
+
+ case REQUEST_CHAIRMAN_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_REQUEST_CHAIRMAN_RESULT, params);
+ break;
+
+ case RELEASE_CHAIRMAN_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_RELEASE_CHAIRMAN_RESULT, params);
+ break;
+
+ case WILL_TIMEOUT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_WILL_TIMEOUT, params);
+ break;
+
+ case POSTPONE_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_POSTPONE_CONF_RESULT, params);
+ break;
+
+ case REQUEST_PRESENTER_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_REQUEST_PRESENTER_RESULT, params);
+ break;
+
+ case SPEAKER_LIST_IND:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SPEAKER_LIST_IND, params);
+ break;
+ case BAND_WATCH:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_BAND_CHANGED_WATCH, params);
+ break;
+ case SET_CONF_MODE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_SET_CONF_MODE_RESULT, params);
+ break;
+ // 选看
+ case WATCH_ATTENDEE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_WATCH_ATTENDEE_CONF_RESULT, params);
+ break;
+ // 广播
+ case BROADCAST_ATTENDEE_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_BROADCAST_ATTENDEE_CONF_RESULT, params);
+ break;
+ // 取消广播?
+ case CANCEL_BROADCAST_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_CANCEL_BROADCAST_CONF_RESULT, params);
+ break;
+ // 开始录制?
+ case START_RECORD_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_START_RECORD_RESULT, params);
+ break;
+ // 停止录制?
+ case STOP_RECORD_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_STOP_RECORD_RESULT, params);
+ break;
+
+ case GET_DATA_CONF_PARAM_RESULT:
+// LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_DATA_CONF_PARAM_RESULT, params);
+ int result = (int) params;
+ if (result != 0) {
+ return;
+ }
+ // MeetingMgr.getInstance().joinDataConf();
+ break;
+
+ case UPGRADE_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_UPGRADE_CONF_RESULT, params);
+ break;
+
+ case JOIN_DATA_CONF_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_DATA_CONFERENCE_JOIN_RESULT, params);
+ break;
+
+ case CAMERA_STATUS_UPDATE:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_DATA_CONFERENCE_CAMERA_STATUS_UPDATE, params);
+ break;
+
+ case CONF_INCOMING_TO_CALL_INCOMING:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_CONF_INCOMING_TO_CALL_INCOMING, params);
+ break;
+
+ case LEAVE_CONF:
+ LogUtil.zzz("离开会议");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_CONF_END, params);
+ break;
+
+ case CONF_CHAT_MSG:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_DATE_CONFERENCE_CHAT_MSG, params);
+ break;
+ case GET_TEMP_USER_RESULT:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_TEMP_USER_RESULT, params);
+ break;
+
+ case CALL_TRANSFER_TO_CONFERENCE:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_CALL_TRANSFER_TO_CONFERENCE, params);
+ break;
+ // 辅流发送状态
+ case AUX_DATA_STATE:
+ LocalBroadcast.getInstance().sendBroadcast
+ (BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE, params);
+ break;
+ // 辅流发送成功
+ case AUX_DATA_SEND:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_AUX_DATA_SEND, params);
+ break;
+ // 辅流发送失败
+ case AUX_DATA_FAILED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_AUX_DATA_FAILED, params);
+ break;
+ case MUTESUCCESS_AND_UPDATEUI:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI, params);
+ break;
+
+ // 更显VMR成功
+ case UPDATE_VMR_SUCCEED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CONF_UPDATE_VMR_SUCCEED, params);
+ break;
+ case GET_SUBJECT_SUCCEED:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_GET_SUBJECT_SUCCEED, params);
+ break;
+ case HAS_HANDUP_MEMBER:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_HAS_HANDUP_MEMBER, params);
+ break;
+ case CHANGE_SC:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CHANGE_SC, params);
+ break;
+ case SCCHANGEREFRESH:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SCCHANGEREFRESH, params);
+ break;
+ // 入会会议密码
+ case VERIFY_CONF_PWD:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_VERIFY_CONF_PWD, params);
+ break;
+ // 入会密码验证
+ case QUERY_CONF_PWD:
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_QUERY_CONF_PWD, params);
+ break;
+ case CONF_SUBTITLE_SWITCH:
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_MEETING_SUBTITLE_SWITCH, params);
+ break;
+ default:
+ break;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LocalBroadcast.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LocalBroadcast.java
new file mode 100644
index 0000000..120c975
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LocalBroadcast.java
@@ -0,0 +1,199 @@
+package com.tengshisoft.chatmodule.hwclud.receiver;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * This class is about local broadcast registration cancellation category.
+ * 本地广播注册取消类
+ */
+public class LocalBroadcast {
+
+ private static final String TAG = LocalBroadcast.class.getSimpleName();
+
+ /**
+ * Instance object of LocalBroadcast component.
+ * 获取一个LocBroadcast对象
+ */
+ private final static LocalBroadcast INS = new LocalBroadcast();
+
+ /**
+ * Store a collection of broadcast objects and accepted classes
+ * 存储广播对象和接受类的集合
+ */
+ protected final Map> broadcasts = new HashMap<>();
+
+ /**
+ * Variable lock
+ * 可变锁
+ */
+ protected final Object broadcastLock = new Object();
+
+ /**
+ * Register the broadcast name
+ * 注册的广播名称
+ */
+ private String broadcastName;
+
+ /**
+ * This is a constructor of LocalBroadcast class.
+ * 构造方法
+ */
+ private LocalBroadcast() {
+ }
+
+ /**
+ * This method is used to get instance object of ImMgr.
+ * 获取ImMgr对象实例
+ *
+ * @return ImMgr Return instance object of ImMgr
+ * 返回一个ImMgr对象实例
+ */
+ public static LocalBroadcast getInstance() {
+ return INS;
+ }
+
+ public String getBroadcastName() {
+ return broadcastName;
+ }
+
+ /**
+ * This method is used to registered broadcast.
+ * 注册广播
+ *
+ * @param receiver Indicates receiver
+ * 广播接收对象
+ * @param actions Indicates accept the action
+ * 注册的广播名称数组
+ * @return boolean Return true:registered success;false:registered failed
+ * 返回TRUE表示注册成功,false表示注册失败
+ */
+ public boolean registerBroadcast(LocalBroadcastReceiver receiver, String[] actions) {
+ if (null == receiver || null == actions) {
+ return false;
+ }
+
+ synchronized (broadcastLock) {
+ LinkedList list;
+
+ for (String action : actions) {
+ list = broadcasts.get(action);
+
+ if (null == list) {
+ list = new LinkedList<>();
+ broadcasts.put(action, list);
+ }
+
+ if (!list.contains(receiver)) {
+ list.add(receiver);
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This method is used to unregistered broadcast.
+ * 去注册广播(注销广播)
+ *
+ * @param receiver Indicates receiver
+ * 广播接收对象
+ * @param actions Indicates accept the action
+ * 注册的广播名称数组
+ * @return boolean Return true:unregistered success;false:unregistered failed
+ * 返回TRUE表示注册成功,false表示注册失败
+ */
+ public boolean unRegisterBroadcast(LocalBroadcastReceiver receiver, String[] actions) {
+ if (null == receiver || null == actions) {
+ return false;
+ }
+
+ List list;
+
+ synchronized (broadcastLock) {
+ for (String action : actions) {
+ list = broadcasts.get(action);
+
+ if (null == list) {
+ return false;
+ }
+ list.remove(receiver);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * This method is used to send a broadcast message.
+ * 发送广播消息
+ *
+ * @param action Indicates registered broadcast name
+ * 注册的广播名称数组
+ * @param data Indicates sent data
+ * 要发送的数据
+ */
+ public void sendBroadcast(String action, Object data) {
+ if (null == action) {
+ return;
+ }
+
+ this.broadcastName = action;
+
+ synchronized (broadcastLock) {
+ List receivers = broadcasts.get(action);
+ if (null == receivers || receivers.isEmpty()) {
+ Log.i(TAG, "no receiver for action#" + action);
+ return;
+ }else {
+ Collections.reverse(receivers);
+ }
+
+ for (LocalBroadcastReceiver receiver : receivers) {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ HANDLER.post(new OnReceiver(receiver, action, data));
+ } else {
+ EXECUTOR.execute(new OnReceiver(receiver, action, data));
+ }
+ }
+ }
+ }
+
+ private static final Handler HANDLER = new Handler(Looper.getMainLooper());
+ private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(6);
+
+ private static class OnReceiver implements Runnable {
+ private final LocalBroadcastReceiver receiver;
+
+ private final String action;
+ private final Object data;
+
+ public OnReceiver(LocalBroadcastReceiver receiver, String action, Object data) {
+ this.receiver = receiver;
+ this.action = action;
+ this.data = data;
+ }
+
+ @Override
+ public void run() {
+ if (receiver == null) {
+ return;
+ }
+ receiver.onReceive(action, data);
+ }
+ }
+
+ public static void destroy() {
+ if (!EXECUTOR.isShutdown()) {
+ EXECUTOR.shutdownNow();
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LocalBroadcastReceiver.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LocalBroadcastReceiver.java
new file mode 100644
index 0000000..83121b9
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LocalBroadcastReceiver.java
@@ -0,0 +1,18 @@
+package com.tengshisoft.chatmodule.hwclud.receiver;
+
+/**
+ * This interface is about local broadcast callback reception.
+ * 本地广播接收回调接口
+ */
+public interface LocalBroadcastReceiver {
+ /**
+ * This method is used to receive a broadcast message.
+ * 接收广播消息
+ *
+ * @param broadcastName Indicates registered broadcast name
+ * 注册的广播名称
+ * @param obj Indicates sent data
+ * 要接收的数据
+ */
+ void onReceive(String broadcastName, Object obj);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LoginReceiver.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LoginReceiver.java
new file mode 100644
index 0000000..8f63648
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/LoginReceiver.java
@@ -0,0 +1,149 @@
+package com.tengshisoft.chatmodule.hwclud.receiver;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.tengshisoft.chatmodule.hwclud.listener.LoginEventNotifyUi;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+
+import static com.tengshisoft.chatmodule.hwclud.utils.LogUtil.CLOUNDLINK;
+
+
+/**
+ * 实现登录接口逻辑的分发
+ */
+public class LoginReceiver implements LoginEventNotifyUi, LocalBroadcastReceiver {
+ private static final int VOIP_LOGIN_SUCCESS = 100;
+ private static final int IM_LOGIN_SUCCESS = 101;
+ private static final int LOGIN_FAILED = 102;
+ private static final int LOGOUT = 103;
+ private static final int FIREWALL_DETECT_FAILED = 104;
+ private static final int BUILD_STG_FAILED = 105;
+ private static final int AUTH_FAILED = 106;
+ private static final int FIRST_CHANGE_PWD=107;
+ private static final int CHANGE_PWD=108;
+
+ private static LoginReceiver INSTANCE = new LoginReceiver();
+
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_ENTERPRISE_GET_SELF_RESULT};
+ private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ parallelHandleMessage(msg);
+ }
+ };
+
+ private LoginReceiver() {
+ LocalBroadcast.getInstance().registerBroadcast(this, broadcastNames);
+ }
+
+ public static LoginEventNotifyUi getInstance() {
+ return INSTANCE;
+ }
+
+ private void sendHandlerMessage(int what, Object object) {
+ if (mMainHandler == null) {
+ return;
+ }
+ Message msg = mMainHandler.obtainMessage(what, object);
+ mMainHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onLoginEventNotify(Constant.LoginUIEvent evt, int reason, String description) {
+ switch (evt) {
+ case VOIP_LOGIN_SUCCESS:
+ LogUtil.i(CLOUNDLINK, "voip login success");
+ sendHandlerMessage(VOIP_LOGIN_SUCCESS, description);
+ break;
+ case LOGIN_FAILED:
+ LogUtil.i(CLOUNDLINK, "login fail");
+ sendHandlerMessage(LOGIN_FAILED, reason+"-"+description);
+ break;
+ case FIREWALL_DETECT_FAILED:
+ LogUtil.i(CLOUNDLINK, "firewall detect fail");
+ sendHandlerMessage(FIREWALL_DETECT_FAILED, description);
+ break;
+ case BUILD_STG_FAILED:
+ LogUtil.i(CLOUNDLINK, "build stg fail");
+ sendHandlerMessage(BUILD_STG_FAILED, description);
+ break;
+ case LOGOUT:
+ LogUtil.i(CLOUNDLINK, "logout");
+ sendHandlerMessage(LOGOUT, description);
+ break;
+ case AUTH_FAILED:
+ LogUtil.i(CLOUNDLINK, "auth" + reason);
+ sendHandlerMessage(AUTH_FAILED, reason);
+ break;
+ case FIRST_CHANGE_PWD:
+ LogUtil.i(CLOUNDLINK, "firstchangepwd" + reason);
+ sendHandlerMessage(FIRST_CHANGE_PWD, reason);
+ break;
+ case CHANGE_PWD:
+ LogUtil.i(CLOUNDLINK, "CHANGE_PWD" + reason);
+ sendHandlerMessage(CHANGE_PWD, reason+"-"+description);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * handle message
+ *
+ * @param msg
+ */
+ private void parallelHandleMessage(Message msg) {
+ switch (msg.what) {
+ case VOIP_LOGIN_SUCCESS:
+ LogUtil.i(CLOUNDLINK, "voip login success,notify UI!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_LOGIN_SUCCESS, msg.obj);
+ break;
+ case IM_LOGIN_SUCCESS:
+ LogUtil.i(CLOUNDLINK, "im login success,notify UI!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_IM_LOGIN_SUCCESS, null);
+ break;
+ case LOGIN_FAILED:
+ LogUtil.i(CLOUNDLINK, "login failed,notify UI!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_LOGIN_FAILED, msg.obj);
+ break;
+ case LOGOUT:
+ LogUtil.i(CLOUNDLINK, "logout success,notify UI!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_LOGOUT, msg.obj);
+// LocalBroadcast.getInstance().sendBroadcast(CustomBroadcastConstants.HWCLOUDLINK_LOGOUT, msg.obj);
+ break;
+ case FIREWALL_DETECT_FAILED:
+ LogUtil.i(CLOUNDLINK, "firewall detect failed,notify UI!");
+ break;
+ case BUILD_STG_FAILED:
+ LogUtil.i(CLOUNDLINK, "build stg failed,notify UI!");
+ break;
+ case AUTH_FAILED:
+ LogUtil.i(CLOUNDLINK, "logout auth ,notify UI!" + msg.obj);
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_AUTH_FAILED, msg.obj);
+ break;
+ case FIRST_CHANGE_PWD:
+ LogUtil.i(CLOUNDLINK, "first change pwd ,notify UI!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_FIRST_CHANGE_PWD, msg.obj);
+ break;
+ case CHANGE_PWD:
+ LogUtil.i(CLOUNDLINK, "change pwd ,notify UI!");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CHANGE_PWD, msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+ switch (broadcastName) {
+ default:
+ break;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/NetWorkStateReceiver.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/NetWorkStateReceiver.java
new file mode 100644
index 0000000..c534600
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/NetWorkStateReceiver.java
@@ -0,0 +1,70 @@
+package com.tengshisoft.chatmodule.hwclud.receiver;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+
+import com.huawei.ecterminalsdk.base.TsdkLocalAddress;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.DeviceManager;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.NetUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+public class NetWorkStateReceiver extends BroadcastReceiver {
+
+ public static String TAG = "NetWork";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ ConnectivityManager connMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ Network[] networks = connMgr.getAllNetworks();
+ if (networks.length == 0) {
+ LogUtil.zzz(TAG, "本地是否有网络", false);
+ if (TsdkManager.getInstance() != null && MeetingController.getInstance().isLogin()) {
+ LogUtil.zzz(TAG, "离线设置本地空IP");
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_NET_WORK_IS_CONNECT, false);
+ MeetingController.getInstance().setHaveNet(false);
+ if (null != TsdkManager.getInstance()) {
+ TsdkManager.getInstance().setConfigParam(new TsdkLocalAddress(""));
+ }
+ }
+ } else {
+ //网络连接后,不已本地为准,以SDK上报为准
+ //此处需判断IP是否发生变化,如果变化,下设SDK
+ LogUtil.zzz(TAG, "本地是否有网络", true);
+ String ipAddr = EncryptedSPTool.getString(UIUtil.getContext(), Constant.LOGIN_SERVER_ADDRESS);
+ if (TsdkManager.getInstance() != null && MeetingController.getInstance().isLogin()) {
+ if (UIUtil.isIPv6(ipAddr)) {
+ //设置IPV6
+ String localIpAddressIPV6 = DeviceManager.getLocalIpAddressIPV6();
+ TsdkLocalAddress localAddressIPV6 = new TsdkLocalAddress(localIpAddressIPV6);
+ LogUtil.zzz(TAG, "设置本地IPV6", LogUtil.commonDisplay(localAddressIPV6.getIpAddress()));
+ if (null != TsdkManager.getInstance()) {
+ TsdkManager.getInstance().setConfigParam(localAddressIPV6);
+ }
+ } else {
+ //设置IPV4
+ String localIpAddress = DeviceManager.getLocalIpAddress(false);
+ TsdkLocalAddress localAddress = new TsdkLocalAddress(localIpAddress);
+ LogUtil.zzz(TAG, "设置本地IPV4", LogUtil.commonDisplay(localAddress.getIpAddress()));
+ if (null != TsdkManager.getInstance()) {
+ TsdkManager.getInstance().setConfigParam(localAddress);
+ }
+ }
+ }
+
+ }
+
+ NetUtil.getInstance(BaseAppContext.getInstance()).recordNetworkStateChanges();
+ }
+}
+
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/PhoneReceiver.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/PhoneReceiver.java
new file mode 100644
index 0000000..3e80e9a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/receiver/PhoneReceiver.java
@@ -0,0 +1,64 @@
+package com.tengshisoft.chatmodule.hwclud.receiver;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+
+
+public class PhoneReceiver extends BroadcastReceiver {
+ private boolean isListener = false;
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ //来电 去电判断
+ if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
+ String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
+ LogUtil.userAction("用户去电");
+ } else {
+ // 设置监听
+ if (!isListener) {
+ TelephonyManager tm = (TelephonyManager) context.
+ getSystemService(Service.TELEPHONY_SERVICE);
+ tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
+ isListener = true;
+ }
+ }
+ }
+
+ static PhoneStateListener listener = new PhoneStateListener() {
+ private int currentState = -1;
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ super.onCallStateChanged(state, incomingNumber);
+ if (currentState == state) {
+ return;
+ }
+ currentState = state;
+ switch (state) {
+ case TelephonyManager.CALL_STATE_IDLE:
+ LogUtil.userAction("挂断sim卡来电");
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_CALL_STATE_IDLE, incomingNumber);
+ break;
+ case TelephonyManager.CALL_STATE_OFFHOOK:
+ LogUtil.userAction("接听sim卡来电");
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_CALL_STATE_OFFHOOK, incomingNumber);
+ break;
+ case TelephonyManager.CALL_STATE_RINGING:
+ LogUtil.userAction("sim卡响铃来电");
+ LocalBroadcast.getInstance().sendBroadcast(
+ BroadcastConstant.ACTION_CALL_STATE_RINGING, incomingNumber);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/AuxSendService.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/AuxSendService.java
new file mode 100644
index 0000000..5a6953d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/AuxSendService.java
@@ -0,0 +1,406 @@
+package com.tengshisoft.chatmodule.hwclud.serivce;
+
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.BitmapFactory;
+import android.graphics.PixelFormat;
+import android.os.Build;
+import android.os.IBinder;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.beans.Session;
+import com.tengshisoft.chatmodule.beans.SponsorMeetingConstant;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.ConfigAppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Platform;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.core.beans.Member;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+import kotlinx.coroutines.Job;
+
+import static com.tengshisoft.chatmodule.hwclud.utils.DeviceManager.isSilentState;
+
+/**
+ * @Time: 2021/9/9
+ * @Author: isoftstone
+ * @Description: 屏幕共享辅流服务
+ */
+public class AuxSendService extends Service {
+ private static final String TAG = "AuxSendService";
+ private boolean isConf = false;
+ /**
+ * 会议类型
+ */
+ private String type;
+ private boolean isHasAux = false;
+ private long time;
+ private Job mStopJob;
+ private boolean mIsCallEnd;
+ /**
+ * 定义浮动窗口布局
+ */
+ private LinearLayout mFloatLayout;
+ private WindowManager.LayoutParams wmParams;
+ /**
+ * 定义浮动窗口初始竖坐标值
+ */
+ private final int INITIAL_Y = 150;
+ /**
+ * 创建浮动窗口设置布局参数的对象
+ */
+ private WindowManager mWindowManager;
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_NET_WORK_IS_CONNECT,
+ BroadcastConstant.ACTION_CALL_STATE_RINGING,
+ BroadcastConstant.ACTION_CALL_STATE_IDLE, BroadcastConstant.ACTION_CALL_STATE_OFFHOOK,
+ BroadcastConstant.ACTION_CALL_END, BroadcastConstant.ACTION_EVT_VIDEO_STOP,
+ BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE};
+ private LocalBroadcastReceiver receiver = (broadcastName, obj) -> {
+ LogUtil.zzz(TAG, "AuxSendService", "broadcastName == " + broadcastName);
+ switch (broadcastName) {
+ case BroadcastConstant.ACTION_CALL_END:
+ mIsCallEnd = true;
+ actionStopAux();
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_IDLE:
+ endMobileCall();
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_RINGING:
+ receivedMobileCall();
+ break;
+ case BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE:
+ isHasAux = (boolean) obj;
+ LogUtil.zzz(TAG, "AuxSendService: share = " + isHasAux);
+ actionStopAux();
+ break;
+ case BroadcastConstant.ACTION_EVT_VIDEO_STOP:
+ int callId = (int) obj;
+ LogUtil.zzz(TAG, "EVT_VIDEO_STOP: callId" + callId);
+ Platform.runUi(() -> ToastUtils.show(getString(R.string.cloudLink_evt_stop)));
+ stopAuxData(callId);
+ break;
+ default:
+ break;
+ }
+ };
+
+ private void actionStopAux() {
+ if (mStopJob != null) {
+ mStopJob.cancel(null);
+ }
+ mStopJob = Platform.runUiDelay(this::stopAux, ConstantsV2.DELAY_MILLIS_500);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtil.zzz(TAG, "onCreate:");
+ wmParams = new WindowManager.LayoutParams();
+ // 通过getApplication获取的是WindowManagerImpl.CompatModeWrapper
+ mWindowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE);
+ // 设置window type
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ } else {
+ wmParams.type = WindowManager.LayoutParams.TYPE_PHONE;
+ }
+ // 设置图片格式,效果为背景透明
+ wmParams.format = PixelFormat.RGBA_8888;
+ // 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
+ wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ // 调整悬浮窗显示的停靠位置为左侧底部
+ wmParams.gravity = Gravity.LEFT | Gravity.BOTTOM;
+ // 以屏幕左上角为原点,设置x、y初始值,相对于gravity
+ wmParams.x = 0;
+ wmParams.y = INITIAL_Y;
+ // 设置悬浮窗口长宽数据
+ wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
+ wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ // 获取浮动窗口视图所在布局
+ mFloatLayout = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.share_stop, null);
+ // 添加mFloatLayout
+ mWindowManager.addView(mFloatLayout, wmParams);
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ Intent dataIntent;
+
+ @SuppressLint("WrongConstant")
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ LogUtil.zzz(TAG, "onStartCommand()" + "flags = " + flags + "startId = " + startId);
+ if (flags > 0) {
+ LogUtil.zzz(TAG, "onStartCommand: Meeting Exit Unexpectedly Service restart");
+ }
+ dataIntent = intent;
+ if (null != intent) {
+ isConf = intent.getBooleanExtra("isConf", false);
+ type = intent.getStringExtra("type");
+ isHasAux = intent.getBooleanExtra("isHasAux", false);
+ time = intent.getLongExtra("time", 0);
+ }
+ mIsCallEnd = false;
+ NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+ startForegroundService(null, manager);
+ createFloatView();
+ return START_REDELIVER_INTENT;
+ }
+
+ @SuppressLint("WrongConstant")
+ private void startForegroundService(PendingIntent pi, NotificationManager manager) {
+ int notificationId = 1;
+ Notification mNotification;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ // 当大于Android8.0
+ String id = getString(R.string.cloudLink_meeting_share);
+ String description = getString(R.string.cloudLink_meeting_share);
+ int importance = NotificationManager.IMPORTANCE_HIGH;
+ NotificationChannel channel = new NotificationChannel(id, description, importance);
+ manager.createNotificationChannel(channel);
+ mNotification = new Notification.Builder(AuxSendService.this, id)
+ .setCategory(Notification.CATEGORY_MESSAGE)
+ .setContentTitle(getString(R.string.cloudLink_meeting_AuxSharing))
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
+ .setAutoCancel(true)
+ .setStyle(new Notification.BigTextStyle().bigText(getString(R.string.cloudLink_meeting_AuxSharing)))
+ .build();
+ } else {
+ // 当小于Android8.0
+ mNotification = new NotificationCompat.Builder(this)
+ .setContentTitle(getString(R.string.cloudLink_meeting_AuxSharing))
+ .setWhen(System.currentTimeMillis())
+ .setSmallIcon(R.mipmap.ic_launcher)
+ .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
+ .setAutoCancel(true)
+ .setDefaults(NotificationCompat.DEFAULT_ALL)
+ .setStyle(new NotificationCompat.BigTextStyle().bigText(getString(R.string.cloudLink_meeting_AuxSharing)))
+ .setPriority(NotificationCompat.PRIORITY_MAX)
+ .build();
+ }
+ startForeground(notificationId, mNotification);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @SuppressLint({"InflateParams", "ClickableViewAccessibility"})
+ private void createFloatView() {
+ LogUtil.zzz(TAG, "createFloatView start");
+ // 浮动窗口按钮
+ // 设置监听浮动窗口的触摸移动监听
+ FloatingOnTouchListener floatingOnTouchListener = new FloatingOnTouchListener();
+ mFloatLayout.setOnTouchListener(floatingOnTouchListener);
+ }
+
+ private void onStopEvent() {
+ int callId = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ if (callId == 0) {
+ LogUtil.zzz(TAG, "onStopEvent callId == 0");
+ }
+ stopAuxData(callId);
+ }
+
+ /**
+ * 关闭辅流
+ *
+ * @param callId
+ */
+ private void stopAuxData(int callId) {
+ Session session = CallMgrV2.getInstance().getCallSessionByCallID(callId);
+ if (session != null) {
+ TsdkCall tsdkCall = session.getTsdkCall();
+ // 停止发送辅流接口
+ int result = tsdkCall.stopAuxData();
+ LogUtil.zzz(TAG, "screen share", "stop aux data failed. result = " + result);
+ if (result != 0) {
+ return;
+ }
+ } else {
+ LogUtil.zzz(TAG, "stopAuxData session is null");
+ }
+ stopAux();
+ }
+
+ private void stopAux() {
+// TODO
+ Intent intent = new Intent(this, SponsorMeetingActivity.class);
+ intent.putExtra("isMiniback", true);
+ intent.putExtra(Constant.AUX, true);
+ intent.putExtra("isHasAux", false);
+ intent.putExtra("isConf", isConf);
+ intent.putExtra("time", time);
+ intent.putExtra("isConfConnect", dataIntent.getBooleanExtra("isConfConnect", false));
+ intent.putExtra("isSVC", dataIntent.getBooleanExtra("isSVC", false));
+ intent.putExtra("callInfo", dataIntent.getSerializableExtra("callInfo"));
+ intent.putExtra(Constant.MY_CONF_INFO, dataIntent.getSerializableExtra(Constant.MY_CONF_INFO));
+ intent.putExtra("type", type);
+ intent.putExtra("isCallEnd", mIsCallEnd);
+ intent.putExtra(Constant.CALLED_NAME, dataIntent.getStringExtra(Constant.CALLED_NAME));
+ intent.putExtra("isMute", MeetingController.getInstance().isVoiceOpen);
+ intent.putExtra(SponsorMeetingConstant.CONF_ID, dataIntent.getStringExtra(SponsorMeetingConstant.CONF_ID));
+ intent.putExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME, dataIntent.getStringExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME));
+ intent.putExtra(SponsorMeetingConstant.VOICE_NUMBER, dataIntent.getStringExtra(SponsorMeetingConstant.VOICE_NUMBER));
+ intent.putExtra(SponsorMeetingConstant.IS_VOICE_TO_VIDEO_FORM_MINIMIZE, false);
+ intent.putExtra(Constant.MEETING_CAPTION_ENABLE, dataIntent.getBooleanExtra(Constant.MEETING_CAPTION_ENABLE, false));
+ intent.putExtra(Constant.MEETING_CONF_CAPTION_ENABLE, dataIntent.getBooleanExtra(Constant.MEETING_CONF_CAPTION_ENABLE, false));
+ intent.putExtra(Constant.MEETING_CONF_CAPTION_SHOWING, dataIntent.getBooleanExtra(Constant.MEETING_CONF_CAPTION_SHOWING, false));
+ intent.putExtra(Constant.MEETING_CONF_CONTROL_ENABLE, dataIntent.getBooleanExtra(Constant.MEETING_CONF_CONTROL_ENABLE, false));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ VideoMgr.getInstance().removeAllSvcVideoWindow();
+ startActivity(intent);
+ stopSelf();
+ }
+
+ /**
+ * 获取截屏的图像流
+ */
+ private class FloatingOnTouchListener implements View.OnTouchListener {
+ private float rawX;
+ private float rawY;
+
+ private int mLastParamsX;
+ private int mLastParamsY;
+ // view移动精度误差
+ private int mTouchSlop = 30;
+
+ public FloatingOnTouchListener() {
+ mLastParamsX = wmParams.x;
+ mLastParamsY = wmParams.y;
+ ViewConfiguration configuration = ViewConfiguration.get(AuxSendService.this);
+ mTouchSlop = configuration.getScaledTouchSlop();
+ LogUtil.zzz(TAG, "FloatingOnTouchListener", "mTouchSlop = " + mTouchSlop);
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ mFloatLayout.setBackgroundResource(R.color.share_stop_touched);
+ rawX = event.getRawX();
+ rawY = event.getRawY();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标
+ int distanceX = (int) (event.getRawX() - rawX);
+ int distanceY = (int) (event.getRawY() - rawY);
+ wmParams.x = wmParams.x + distanceX;
+ wmParams.y = wmParams.y - distanceY;
+ if (mFloatLayout != null) {
+ // 移除悬浮窗口
+ if (mWindowManager != null) {
+ mWindowManager.updateViewLayout(mFloatLayout, wmParams);
+ }
+ }
+ rawX = event.getRawX();
+ rawY = event.getRawY();
+ break;
+ case MotionEvent.ACTION_UP:
+ if (Math.abs(wmParams.x - mLastParamsX) < mTouchSlop && Math.abs(wmParams.y - mLastParamsY) < mTouchSlop) {
+ onStopEvent();
+ }
+ mLastParamsX = wmParams.x;
+ mLastParamsY = wmParams.y;
+ case MotionEvent.ACTION_CANCEL:
+ mFloatLayout.setBackgroundResource(R.color.share_stop);
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ LogUtil.zzz(TAG, "onDestroy");
+ if (mStopJob != null && mStopJob.isActive()) {
+ mStopJob.cancel(null);
+ }
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ MeetingController.getInstance().setAux(false);
+ stopForeground(true);
+ ConfigAppUtil.setIsAuxServiceStopped(true);
+ if (mFloatLayout != null) {
+ // 移除悬浮窗口
+ if (mWindowManager != null) {
+ mWindowManager.removeView(mFloatLayout);
+ LogUtil.zzz(TAG, "removeView mFloatLayout");
+ }
+ }
+ super.onDestroy();
+ }
+
+ public void receivedMobileCall() {
+ LogUtil.zzz(TAG, "receivedMobileCall");
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(false);
+ } else {
+ LogUtil.zzz(TAG, "sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ isSilentState = self.isMute();
+ if (!isSilentState) {
+ MeetingMgrV2.getInstance().muteAttendee(self, !isSilentState);
+ MeetingController.getInstance().isVoiceOpen = !isSilentState;
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ }
+ }
+
+ public void endMobileCall() {
+ LogUtil.zzz(TAG, "endMobileCall");
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(true);
+ } else {
+ LogUtil.zzz(TAG, "sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ if (!isSilentState) {
+ MeetingMgrV2.getInstance().muteAttendee(self, isSilentState);
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/CloudLinkService.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/CloudLinkService.kt
new file mode 100644
index 0000000..cb3479c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/CloudLinkService.kt
@@ -0,0 +1,126 @@
+package com.tengshisoft.chatmodule.hwclud.serivce
+
+import android.app.Service
+import android.bluetooth.BluetoothA2dp
+import android.bluetooth.BluetoothAdapter
+import android.bluetooth.BluetoothHeadset
+import android.content.Intent
+import android.content.IntentFilter
+import android.media.AudioManager
+import android.os.Binder
+import android.os.IBinder
+import com.huawei.AudioDeviceAndroid
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController
+import com.tengshisoft.chatmodule.hwclud.manager.CloudLinkNotifyManager
+import com.tengshisoft.chatmodule.hwclud.utils.Constant
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil
+import com.tenlionsoft.baselib.utils.EncryptedSPTool
+
+/**
+ * @description 初始化Service
+ * @author wangBo
+ * @time 2020/12/8 15:51
+ */
+class CloudLinkService : Service() {
+
+ private val audioDeviceAndroid by lazy { AudioDeviceAndroid() }
+ private var hadRegister: Boolean = false
+ private val mNotifyManager: CloudLinkNotifyManager by lazy { CloudLinkNotifyManager(this) }
+
+ companion object {
+ val serviceProxy: CloudLinkService by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
+ CloudLinkService()
+ }
+ }
+
+ open class CloudLinkBinder : Binder() {
+ open fun getService(): CloudLinkService {
+ return serviceProxy
+ }
+ }
+
+ override fun onCreate() {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "CloudLinkService onCreate: ")
+ hadRegister = false
+ super.onCreate()
+ mNotifyManager.startForegroundNotification()
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "CloudLinkService onStartCommand: ")
+ if (!hadRegister) {
+ registerAudioDeviceAndroid()
+ }
+ if (flags > 0) {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "onStartCommand: 应用异常退出 Service重启")
+ }
+ mNotifyManager.startForegroundNotification()
+ return START_REDELIVER_INTENT
+ }
+
+
+ override fun onTaskRemoved(rootIntent: Intent?) {
+ val endCall = MeetingController.getInstance().call?.endCall()
+ // 防止用户在会议中杀进程,重进后申请主席失败
+ if (UIUtil.isService3()) {
+ EncryptedSPTool.remove(Constant.IS_VMR_3_ID)
+ } else {
+ EncryptedSPTool.remove(Constant.IS_VMR_2_ID)
+ EncryptedSPTool.remove(Constant.INITIATE_VMR)
+ }
+ LogUtil.zzz("TAG", "onTaskRemoved: $endCall")
+ super.onTaskRemoved(rootIntent)
+ }
+
+ override fun onDestroy() {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "CloudLinkService onDestroy: ")
+ mNotifyManager.stopForegroundNotification()
+ unRegisterAudioDeviceAndroid()
+ super.onDestroy()
+ }
+
+ override fun onBind(intent: Intent?): IBinder {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "CloudLinkService onBind: ")
+ return CloudLinkBinder()
+ }
+
+ override fun onUnbind(intent: Intent?): Boolean {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "CloudLinkService onUnbind: ")
+ return super.onUnbind(intent)
+ }
+
+
+ /**
+ * register audio device broadcast receiver
+ */
+ private fun registerAudioDeviceAndroid() {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "registerAudioDeviceAndroid: $packageName")
+ val intentFilter = IntentFilter()
+ //蓝牙SCO使用事件XX
+ intentFilter.addAction("android.media.SCO_AUDIO_STATE_CHANGED");
+ intentFilter.addAction("android.bluetooth.headset.action.STATE_CHANGED"); // OS2.0后支持
+ // 这个广播只是针对有线耳机,或者无线耳机的手机断开连接的事件,XX不到有线耳机和蓝牙耳机的接入
+ intentFilter.addAction(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
+ // 耳机插拔通知
+ intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
+ // 蓝牙打开关闭通知
+ intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ //OS3.0后支持 真正的蓝牙耳机连接和关闭通知,但是打开关闭蓝牙无通知 ok
+ intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+ intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
+ intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
+ intentFilter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)
+ registerReceiver(audioDeviceAndroid, intentFilter)
+ hadRegister = true
+ }
+
+ /**
+ * unregister audio device broadcast receiver
+ */
+ private fun unRegisterAudioDeviceAndroid() {
+ LogUtil.zzz(LogUtil.CLOUNDLINK, "unRegisterAudioDeviceAndroid: ")
+ unregisterReceiver(audioDeviceAndroid)
+ hadRegister = false
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/FloatingViewService.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/FloatingViewService.java
new file mode 100644
index 0000000..2d861b9
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/FloatingViewService.java
@@ -0,0 +1,200 @@
+package com.tengshisoft.chatmodule.hwclud.serivce;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Chronometer;
+import android.widget.FrameLayout;
+
+
+import com.hjq.toast.ToastUtils;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.hwclud.listener.FloatingViewListener;
+import com.tengshisoft.chatmodule.hwclud.manager.FloatingViewManager;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * @author zwl
+ * @describe 悬浮窗Service
+ * @date on 2018/11/26
+ */
+public class FloatingViewService extends Service implements FloatingViewListener {
+
+ private static final String TAG = "FloatingViewService";
+
+ private FloatingViewManager mFloatingViewManager;
+
+ private Chronometer ch;
+ private View floatView;
+ private RotationBroadcastReceive rotationBroadcastReceive;
+
+// private CameraView cameraView;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.e(TAG, "onCreate");
+ rotationBroadcastReceive = new RotationBroadcastReceive();
+ // 注册广播接收,注意:要监听这个系统广播就必须用这种方式来注册,不能再xml中注册,否则不能生效
+ IntentFilter filter = new IntentFilter();
+ filter.addAction("android.intent.action.CONFIGURATION_CHANGED");
+ registerReceiver(rotationBroadcastReceive, filter);
+ init();
+ }
+
+ private int init() {
+ if (this.mFloatingViewManager != null) {
+ return START_STICKY;
+ }
+
+ Log.e(TAG, "悬浮窗Service已启动");
+// View floatView = LayoutInflater.from(this).inflate(R.layout.call_float_view, null, false);
+ floatView = View.inflate(this, R.layout.call_float_view, null);
+ FrameLayout flVideo = floatView.findViewById(R.id.fl_video);
+// ch = (Chronometer) floatView.findViewById(R.id.call_time_ch);
+// cameraView = (CameraView) floatView.findViewById(R.id.camera);
+// cameraView.start();
+// ch.start();
+// ch.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
+// @Override
+// public void onChronometerTick(Chronometer chronometer) {
+// if (callback != null) {
+// callback.onDataChanged(chronometer.getText().toString());
+// }
+// }
+// });
+ floatView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ToastUtils.show("点击了悬浮窗");
+ //这样启动activity是解决启动延迟的问题
+ Intent intent = new Intent(FloatingViewService.this, SponsorMeetingActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
+ try {
+ pendingIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+
+ getScreenImageReader();
+
+ return START_REDELIVER_INTENT;
+ }
+ private void getScreenImageReader() {
+ DisplayMetrics dm = new DisplayMetrics();
+ WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ windowManager.getDefaultDisplay().getMetrics(dm);
+
+ this.mFloatingViewManager = new FloatingViewManager(this, this);
+
+ FloatingViewManager.Configs configs = new FloatingViewManager.Configs();
+ configs.floatingViewX = dm.widthPixels / 2;
+ configs.floatingViewY = dm.heightPixels / 4;
+ configs.overMargin = -(int) (8 * dm.density);
+ configs.animateInitialMove = false;
+ this.mFloatingViewManager.addFloatingView(floatView, configs);
+ }
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ Log.e(TAG, "onStartCommand");
+ return super.onStartCommand(intent, flags, startId);
+ }
+ private class RotationBroadcastReceive extends BroadcastReceiver {
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ @Override
+ public void onReceive(Context arg0, Intent arg1) {
+ LogUtil.i("FloatingButton_onReceive", "");
+ // 获取截屏的图像流
+ getScreenImageReader();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onDestroy() {
+ Log.e(TAG, "onDestroy");
+// try {
+// if (cameraView != null) {
+// cameraView.stop();
+// }
+// } catch (Exception e) {
+// e.printStackTrace();
+// }
+
+ destroyFloatingView();
+
+ super.onDestroy();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+
+ public class MyBinder extends Binder {
+ public FloatingViewService getService() {
+ return FloatingViewService.this;
+ }
+
+
+ public void setData(long data) {//写一个公共方法,用来对data数据赋值。
+ ch.setBase(data);
+ Log.e("123123", "setData=" + data);
+ }
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onFinishFloatingView() {
+ stopSelf();
+ }
+
+ /**
+ * 销毁悬浮窗
+ */
+ private void destroyFloatingView() {
+ if (this.mFloatingViewManager != null) {
+ this.mFloatingViewManager.removeAllFloatingView();
+ this.mFloatingViewManager = null;
+ }
+ Log.d(TAG, "悬浮窗已销毁");
+ }
+
+ private CallBack callback;
+
+ public void setCallback(CallBack callback) {
+ this.callback = callback;
+ }
+
+ public static interface CallBack {
+ void onDataChanged(String data);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/MinimizeService.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/MinimizeService.java
new file mode 100644
index 0000000..7886759
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/MinimizeService.java
@@ -0,0 +1,661 @@
+package com.tengshisoft.chatmodule.hwclud.serivce;
+
+
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.TextView;
+
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkWatchSvcAttendees;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.activity.InvitedPointCallActivity;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.beans.CallConstant;
+import com.tengshisoft.chatmodule.beans.ConferenceConstant;
+import com.tengshisoft.chatmodule.beans.SponsorMeetingConstant;
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingTitleBarController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.NotifyUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Platform;
+import com.tengshisoft.chatmodule.hwclud.utils.RomUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.TimerUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.util.List;
+
+import androidx.annotation.RequiresApi;
+
+import static com.tengshisoft.chatmodule.hwclud.utils.DateUtils.unitFormat;
+import static com.tengshisoft.chatmodule.hwclud.utils.ScreenUtil.dp2ps;
+import static com.tengshisoft.chatmodule.hwclud.utils.DeviceManager.isSilentState;
+
+/**
+ * 浮窗服务
+ * (语音、视频最小化浮窗)
+ *
+ * @author ts
+ */
+@RequiresApi(api = Build.VERSION_CODES.M)
+public class MinimizeService extends Service {
+ private boolean isConf = false;
+ /**
+ * 会议类型
+ */
+ private String type;
+ /**
+ * 带宽计算的分辨率
+ */
+ private int[] bandVideoSize;
+ boolean isVoiceToVideo = false;
+
+
+ public View floatWindow;
+ private FrameLayout flRemoteVideo;
+ private FrameLayout hideView;
+ private TextView tvTime;
+ private long time;
+ private WindowManager windowManager;
+ private WindowManager.LayoutParams layoutParams;
+ private TsdkCall tsdkCall;
+ private boolean isSVC = false;
+ private boolean isHasAux = false;
+ private Member temp_member;
+ private PowerManager.WakeLock wakeLock;
+ private String[] broadcastNames = new String[]{
+ BroadcastConstant.ACTION_CALL_END,
+ BroadcastConstant.ACTION_CALL_END_SERVICE,
+ BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE//是否有辅流通知
+ , BroadcastConstant.ACTION_CONF_STATE_UPDATE//会议状态刷新
+ , BroadcastConstant.ACTION_BACK_TO_DESK // 退至桌面
+ , BroadcastConstant.ACTION_BACK_TO_APP // 从桌面回来
+ , BroadcastConstant.ACTION_AUX_STOP
+ , BroadcastConstant.ACTION_MINIMIZE_SERVICE_VOICE_TO_VIDEO
+ , BroadcastConstant.ACTION_MINIMIZE_SERVICE_VIDEO_TO_VOICE
+ , BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI // 抗啸叫
+ , BroadcastConstant.ACTION_CALL_STATE_OFFHOOK
+ , BroadcastConstant.ACTION_CALL_STATE_IDLE
+ , BroadcastConstant.ACTION_CALL_STATE_RINGING
+ , BroadcastConstant.ACTION_MINIMIZE_SERVICE_OPEN_VIDEO
+ // 网络状况变化
+ , BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL
+ , BroadcastConstant.ACTION_AUX_FAILED
+ , BroadcastConstant.ACTION_BAND_CHANGED_WATCH
+ };
+ LocalBroadcastReceiver receiver = (broadcastName, obj) -> {
+ switch (broadcastName) {
+ case BroadcastConstant.ACTION_CALL_END:
+ case BroadcastConstant.ACTION_CALL_END_SERVICE:
+ removeView();
+ stopSelf();
+ break;
+ case BroadcastConstant.ACTION_DATE_CONFERENCE_AUX_DATA_STATE:
+ isHasAux = (boolean) obj;
+ Platform.runUi(() -> {
+
+ if (!isSVC) {
+ addSurfaceView(flRemoteVideo, VideoMgr.getInstance().getRemoteVideoView());
+ } else {
+ addSurfaceView(flRemoteVideo, VideoMgr.getInstance().getSvcBigView());
+ watchSvcAttendees(temp_member);
+ }
+ if (isHasAux) {
+ stopService(new Intent(this, AuxSendService.class));
+ addSurfaceView(flRemoteVideo, VideoMgr.getInstance().getAuxDataView());
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_CONF_STATE_UPDATE:
+ List memberList = MeetingMgrV2.getInstance().getCurrentConferenceMemberList();
+ for (Member member : memberList) {
+ if (member.isBroadcastSelf()) {
+ watchMember(member);
+ break;
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_BACK_TO_DESK:
+// if (MinimizeService.this.shareStopView != null) {
+// LogUtil.zzz("MinimizeService","隐藏最小化悬浮窗");
+// MinimizeService.this.shareStopView.setVisibility(View.GONE);
+// if (tsdkCall != null) {
+// VideoMgr.getInstance().closeCamera(tsdkCall);
+// }
+// }
+
+ break;
+ case BroadcastConstant.ACTION_BACK_TO_APP:
+ case BroadcastConstant.ACTION_AUX_STOP:
+ if (RomUtil.isMiui()) {
+ // 小米 ROM后台自动弹出界面权限检测
+ if (!RomUtil.canBackStartForXiaoMi(this)) {
+ ToastUtils.show("需要开启后台弹出权限");
+ break;
+ }
+ }
+ if (RomUtil.isVivo()) {
+ // vivo ROM后台自动弹出界面权限检测
+ if (!RomUtil.canBackStartForVivo(this)) {
+ ToastUtils.show("需要开启后台弹出权限");
+ break;
+ }
+ }
+ layoutClick();
+ break;
+ case BroadcastConstant.ACTION_MUTESUCCESS_AND_UPDATEUI:
+ UIUtil.runUI(() -> {
+ if (EncryptedSPTool.getBoolean(Constant.HOWL_AUTO_MUTE, true)) {
+ MeetingController.getInstance().isVoiceOpen = true;
+ CallFunc.getInstance().setMuteStatus(true);
+ ToastUtils.show(getString(R.string.cloudLink_mine_howlautomutetoast));
+ } else {
+ ToastUtils.show(getString(R.string.cloudLink_mine_howlautomutetoastna));
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_IDLE:
+ endMobileCall();
+ break;
+ case BroadcastConstant.ACTION_CALL_STATE_RINGING:
+ receivedMobileCall();
+ break;
+ //音频转视频
+ case BroadcastConstant.ACTION_MINIMIZE_SERVICE_VOICE_TO_VIDEO:
+ isVoiceToVideo = true;
+ UIUtil.runUI(this::layoutClick);
+ break;
+ //视频转音频
+ case BroadcastConstant.ACTION_MINIMIZE_SERVICE_VIDEO_TO_VOICE:
+ UIUtil.runUI(() -> {
+ removeView();
+ type = ConferenceConstant.VIDEO_VOICE_LAYOUT_CALL;
+ layoutParams.width = dp2ps(this, 90);
+ layoutParams.height = dp2ps(this, 160);
+ showFloatingWindow();
+ });
+ break;
+ //打开视频
+ case BroadcastConstant.ACTION_MINIMIZE_SERVICE_OPEN_VIDEO:
+ UIUtil.runUI(() -> {
+ TsdkCall tsdkCall = (TsdkCall) obj;
+ if (tsdkCall != null && tsdkCall.getCallInfo() != null && tsdkCall.getCallInfo().getIsFocus() == 0) {
+ removeView();
+ type = ConferenceConstant.VIDEO_CALL;
+ layoutParams.width = dp2ps(this, 160);
+ layoutParams.height = dp2ps(this, 90);
+ showFloatingWindow();
+ MeetingController.getInstance().isFloatWindowOpen = true;
+ MeetingController.getInstance().isVideoOpen = true;
+
+ CallMgrV2.getInstance().openCamera(tsdkCall.getCallInfo().getCallId(), 1);
+ VideoMgr.getInstance().setVideoOrient(tsdkCall.getCallInfo().getCallId(), CallConstant.FRONT_CAMERA);
+ }
+ });
+ break;
+ case BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL:
+ int netLevel = (int) obj;
+ LogUtil.zzz("小窗口网络状态 监听", netLevel);
+ MeetingTitleBarController.mCurrentNetLevel = netLevel;
+ break;
+ case BroadcastConstant.ACTION_BAND_CHANGED_WATCH:
+ int[] videoSize = MeetingController.getInstance().getVideoSize(1);
+ if (bandVideoSize != null && bandVideoSize[0] == videoSize[0]) {
+ LogUtil.zzz("BAND_WATCH", "the same resolution");
+ return;
+ }
+ bandVideoSize = videoSize;
+ watchSvcAttendees(temp_member);
+ break;
+ default:
+ break;
+ }
+ };
+
+ private void watchMember(Member member) {
+ temp_member = member;
+ if (isSVC) {
+ watchSvcAttendees(member);
+ }
+ }
+
+ private String ssrcTableStart;
+
+ long createTime = 0;
+
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ LogUtil.zzz("MinimizeTest", "onCreate");
+ createTime = System.currentTimeMillis();
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ MeetingController.getInstance().setMinimize(true);
+ windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
+ PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+ if (powerManager != null) {
+ wakeLock = powerManager.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.FULL_WAKE_LOCK,
+ MinimizeService.class.getSimpleName());
+ wakeLock.acquire();
+ }
+
+ layoutParams = new WindowManager.LayoutParams();
+ if (Build.VERSION.SDK_INT >= 21) {
+ layoutParams.type = 2038;
+ } else {
+ layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
+ }
+ layoutParams.format = PixelFormat.TRANSLUCENT;
+ layoutParams.gravity = Gravity.START | Gravity.BOTTOM;
+ layoutParams.flags =
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ int width = windowManager.getDefaultDisplay().getWidth();
+ int height = windowManager.getDefaultDisplay().getHeight();
+ if (width < height) {
+ layoutParams.x = width;
+ layoutParams.y = height - dp2ps(this, 20);
+ } else {
+ layoutParams.x = height;
+ layoutParams.y = width - dp2ps(this, 20);
+ }
+ }
+
+ /**
+ * 初始化显示流
+ */
+ private void initVideo() {
+ try {
+ if (!isSVC) {
+ addSurfaceView(flRemoteVideo, VideoMgr.getInstance().getRemoteVideoView());
+ } else {
+ addSurfaceView(flRemoteVideo, VideoMgr.getInstance().getSvcBigView());
+ watchSvcAttendees(temp_member);
+ }
+ if (isHasAux) {
+ addSurfaceView(flRemoteVideo, VideoMgr.getInstance().getAuxDataView());
+ }
+ addSurfaceView(hideView, VideoMgr.getInstance().getLocalHideView());
+ } catch (Exception e) {
+ LogUtil.zzz("Minimize", "最小化窗口获取tsdkCall失败");
+ }
+ }
+
+ private void addSurfaceView(ViewGroup container, SurfaceView child) {
+ if (child == null) {
+ return;
+ }
+ if (child.getParent() != null) {
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ vGroup.removeAllViews();
+ }
+ container.addView(child);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return new MyBinder();
+ }
+
+ Intent dataIntent;
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ dataIntent = intent;
+ VideoMgr.getInstance().removeAllSvcVideoWindow();
+ if (null != intent) {
+ isConf = intent.getBooleanExtra("isConf", false);
+ type = intent.getStringExtra("type");
+ isSVC = intent.getBooleanExtra("isSVC", false);
+ isHasAux = intent.getBooleanExtra("isHasAux", false);
+ temp_member = (Member) intent.getSerializableExtra("watchMember");
+ time = intent.getLongExtra("time", 0);
+ ssrcTableStart = intent.getStringExtra("ssrcTableStart");
+ }
+ if (ConferenceConstant.VOICE_CALL.equals(type) || ConferenceConstant.VIDEO_VOICE_LAYOUT_CALL.equals(type)) {
+ layoutParams.width = dp2ps(this, 90);
+ layoutParams.height = dp2ps(this, 160);
+ } else {
+ layoutParams.width = dp2ps(this, 160);
+ layoutParams.height = dp2ps(this, 90);
+ }
+ if (temp_member == null) {
+ temp_member = MeetingController.getInstance().getVoiceWatchMember();
+ } else {
+ MeetingController.getInstance().setVoiceWatchMember(null);
+ }
+ showFloatingWindow();
+
+ if (!MeetingController.getInstance().isAux()) {
+ initBeferService();
+ }
+ return START_REDELIVER_INTENT;
+ }
+
+ /**
+ * 绑定前台服务
+ */
+ private void initBeferService() {
+ Notification build = NotifyUtil.getNotificationBuilder(this, LogUtil.CLOUNDLINK, BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_confWaiting), "111").build();
+
+ startForeground(110, build);
+ }
+
+ public class MyBinder extends Binder {
+ public MinimizeService getService() {
+ return MinimizeService.this;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private void showFloatingWindow() {
+ if (Settings.canDrawOverlays(this)) {
+ removeView();
+ floatWindow = View.inflate(this, R.layout.minimize, null);
+ flRemoteVideo = floatWindow.findViewById(R.id.fl_remote_video);
+ hideView = floatWindow.findViewById(R.id.fl_hideview);
+ FrameLayout llVoice = floatWindow.findViewById(R.id.ll_voice);
+ tvTime = floatWindow.findViewById(R.id.tv_time);
+ windowManager.addView(floatWindow, this.layoutParams);
+ Constant.IS_BACK_MINIMIZE = true;
+ floatWindow.setOnTouchListener(new FloatingOnTouchListener());
+ updateTime(time);
+ TimerUtil.startTimer(1000, () -> updateTime(++time));
+ // 语音呼叫显示语音浮窗
+ if (ConferenceConstant.VOICE_CALL.equals(type) || ConferenceConstant.VIDEO_VOICE_LAYOUT_CALL.equals(type)) {
+ llVoice.setVisibility(View.VISIBLE);
+ } else {
+ llVoice.setVisibility(View.GONE);
+ floatWindow.setBackgroundColor(Color.BLACK);
+ flRemoteVideo.setVisibility(View.VISIBLE);
+ initVideo();
+ }
+ }
+ }
+
+ public void updateTime(long aLong) {
+ int hour;
+ int minute;
+ int second;
+ if (time < 0) {
+ tvTime.setText("00:00:00");
+ } else {
+ minute = (int) (aLong / 60);
+ if (minute < 60) {
+ second = (int) (aLong % 60);
+ tvTime.setText("00:" + unitFormat(minute) + ":" + unitFormat(second));
+ } else {
+ hour = minute / 60;
+ if (hour > 99) {
+ tvTime.setText("99:59:59");
+ }
+ minute = minute % 60;
+ second = (int) (aLong - hour * 3600 - minute * 60);
+ tvTime.setText(unitFormat(hour) + ":" + unitFormat(minute) + ":" + unitFormat(second));
+ }
+ }
+ }
+
+ private void watchSvcAttendees(Member member) {
+ TsdkWatchSvcAttendees watchSvcAttendees = new TsdkWatchSvcAttendees();
+ MeetingController.getInstance().setSvcWatchStatus(SvcWatchStatus.MINI_WATCH);
+ int[] videoSize = MeetingController.getInstance().getVideoSize(1);
+ watchSvcAttendees.setWidth(videoSize[0]);
+ watchSvcAttendees.setHeight(videoSize[1]);
+ watchSvcAttendees.setLableId(Integer.parseInt(ssrcTableStart));
+ int userId = MeetingMgrV2.getInstance().getWatchSingleUserId(member);
+ int result = MeetingMgrV2.getInstance().watchSVCMiniAttendee(watchSvcAttendees, userId);
+ if (0 != result) {
+ LogUtil.d("悬浮窗选看", "watchSvcAttendee fail result : " + result);
+ }
+ }
+
+ boolean isJump = false;
+
+ /**
+ * 悬浮窗点击事件
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private void layoutClick() {
+ if (isJump) {
+ return;
+ }
+ if (Settings.canDrawOverlays(this) || UIUtil.isForeground()) {
+ if (!UIUtil.isForeground()) {
+ if (RomUtil.isMiui()) {
+ // 小米 ROM后台自动弹出界面权限检测
+ if (!RomUtil.canBackStartForXiaoMi(this)) {
+ ToastUtils.show("需要开启后台弹出权限");
+ return;
+ }
+ }
+ if (RomUtil.isVivo()) {
+ // vivo ROM后台自动弹出界面权限检测
+ if (!RomUtil.canBackStartForVivo(this)) {
+ ToastUtils.show("需要开启后台弹出权限");
+ return;
+ }
+ }
+ }
+ isJump = true;
+ LogUtil.userAction("MinimizeTest 从小窗口返回 " + LogUtil.getInstance().getSimpleExtraInfo());
+ floatWindow.setOnTouchListener(null);
+ floatWindow.setOnClickListener(null);
+ Intent intent;
+ if (ConferenceConstant.VOICE_CALL.equals(type)) {
+ intent = new Intent(this, InvitedPointCallActivity.class);
+ intent.putExtra("isMiniback", true);
+ intent.putExtra("time", time);
+ intent.putExtra("tvConfId", dataIntent.getStringExtra("tvConfId"));
+ intent.putExtra("tvConfName", dataIntent.getStringExtra("tvConfName"));
+ } else {
+ intent = new Intent(this, SponsorMeetingActivity.class);
+ intent.putExtra("isMiniback", true);
+ intent.putExtra("isHasAux", isHasAux);
+ intent.putExtra("isConf", isConf);
+ intent.putExtra("time", time);
+ intent.putExtra("isConfConnect", dataIntent.getBooleanExtra("isConfConnect", false));
+ intent.putExtra("isSVC", dataIntent.getBooleanExtra("isSVC", false));
+ intent.putExtra("callInfo", dataIntent.getSerializableExtra("callInfo"));
+ intent.putExtra(Constant.MY_CONF_INFO, dataIntent.getSerializableExtra(Constant.MY_CONF_INFO));
+ intent.putExtra("type", type);
+ intent.putExtra(Constant.CALLED_NAME, dataIntent.getStringExtra(Constant.CALLED_NAME));
+ intent.putExtra("isMute", MeetingController.getInstance().isVoiceOpen);
+ intent.putExtra(SponsorMeetingConstant.CONF_ID, dataIntent.getStringExtra(SponsorMeetingConstant.CONF_ID));
+ intent.putExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME, dataIntent.getStringExtra(SponsorMeetingConstant.VOICE_DISPLAY_NAME));
+ intent.putExtra(SponsorMeetingConstant.VOICE_NUMBER, dataIntent.getStringExtra(SponsorMeetingConstant.VOICE_NUMBER));
+ intent.putExtra(SponsorMeetingConstant.IS_VOICE_TO_VIDEO_FORM_MINIMIZE, isVoiceToVideo);
+ }
+ intent.putExtra(Constant.MEETING_CAPTION_ENABLE, dataIntent.getBooleanExtra(Constant.MEETING_CAPTION_ENABLE, false));
+ intent.putExtra(Constant.MEETING_CONF_CAPTION_ENABLE, dataIntent.getBooleanExtra(Constant.MEETING_CONF_CAPTION_ENABLE, false));
+ intent.putExtra(Constant.MEETING_CONF_CAPTION_SHOWING, dataIntent.getBooleanExtra(Constant.MEETING_CONF_CAPTION_SHOWING, false));
+ intent.putExtra(Constant.MEETING_CONF_CONTROL_ENABLE, dataIntent.getBooleanExtra(Constant.MEETING_CONF_CONTROL_ENABLE, false));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ VideoMgr.getInstance().removeAllSvcVideoWindow();
+ startActivity(intent);
+
+ stopSelf();
+ } else {
+ ToastUtils.show("请开启悬浮窗权限");
+ }
+ }
+
+ private class FloatingOnTouchListener implements View.OnTouchListener {
+ private int x;
+ private int y;
+ private int downx;
+ private int downy;
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ x = (int) event.getRawX();
+ y = (int) event.getRawY();
+ downx = x;
+ downy = y;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ int nowX = (int) event.getRawX();
+ int nowY = (int) event.getRawY();
+ int movedX = nowX - x;
+ int movedY = nowY - y;
+ x = nowX;
+ y = nowY;
+ layoutParams.x = layoutParams.x + movedX;
+ layoutParams.y = layoutParams.y - movedY;
+ windowManager.updateViewLayout(view, layoutParams);
+ break;
+ case MotionEvent.ACTION_UP:
+ int upX = (int) event.getRawX();
+ int upY = (int) event.getRawY();
+ if (Math.abs(downx - upX) < 5 && Math.abs(downy - upY) < 5) {
+ layoutClick();
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ LogUtil.zzz("MinimizeTest", "onDestroy");
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ removeView();
+ stopForeground(true);
+// // 在后台运行时,如果打开的应用太多被系统回收后,执行离会操作,这样再次进入App可以正常发起会议或加入会议
+// if (!SystemUtil.isRunningForeground(this)) {
+//
+// int result = MeetingMgr.getInstance().leaveConf();
+// if (result == 0) {
+// LogUtil.zzz("MinimizeService", "onDestroy: 离开会议成功");
+// } else {
+// LogUtil.zzz("MinimizeService", "onDestroy: 离开会议失败");
+// }
+// }
+ if (wakeLock != null) {
+ wakeLock.release();
+ }
+ TimerUtil.stopTimer();
+ MeetingController.getInstance().setMinimize(false);
+ super.onDestroy();
+ }
+
+ private void removeView() {
+ if (windowManager != null && floatWindow != null && floatWindow.isShown()) {
+ windowManager.removeView(floatWindow);
+ }
+ }
+
+
+ public void receivedMobileCall() {
+ LogUtil.zzz("MinimizeService_receivedMobileCall");
+ if (isConf) {
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(false);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ isSilentState = self.isMute();
+ if (!isSilentState) {
+ MeetingMgrV2.getInstance().muteAttendee(self, !isSilentState);
+ MeetingController.getInstance().isVoiceOpen = !isSilentState;
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ }
+ } else {
+ TsdkCall sdkCall = CallMgrV2.getInstance().getTSdkCallByCallMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(false);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ CallFunc callFunc = CallFunc.getInstance();
+ isSilentState = callFunc.isMuteStatus();
+ if (!isSilentState) {
+ CallMgrV2.getInstance().muteMic(CallMgrV2.getInstance().getCallId(), !isSilentState);
+ callFunc.setMuteStatus(!isSilentState);
+ MeetingController.getInstance().isVoiceOpen = !isSilentState;
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ }
+ }
+ }
+
+ public void endMobileCall() {
+ LogUtil.zzz("MinimizeService_endMobileCall");
+ if (isConf) {
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(true);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ if (!isSilentState) {
+ MeetingMgrV2.getInstance().muteAttendee(self, isSilentState);
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ }
+ } else {
+ TsdkCall sdkCall = CallMgrV2.getInstance().getTSdkCallByCallMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(true);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ CallFunc callFunc = CallFunc.getInstance();
+ if (!isSilentState) {
+ CallMgrV2.getInstance().muteMic(CallMgrV2.getInstance().getCallId(), isSilentState);
+ callFunc.setMuteStatus(isSilentState);
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ }
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/ServiceManger.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/ServiceManger.java
new file mode 100644
index 0000000..147d3a1
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/ServiceManger.java
@@ -0,0 +1,265 @@
+package com.tengshisoft.chatmodule.hwclud.serivce;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Environment;
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.base.TsdkAppFilePathInfo;
+import com.huawei.ecterminalsdk.base.TsdkAppInfoParam;
+import com.huawei.ecterminalsdk.base.TsdkLogLevel;
+import com.huawei.ecterminalsdk.base.TsdkLogParam;
+import com.huawei.ecterminalsdk.base.TsdkMediaSrtpMode;
+import com.huawei.ecterminalsdk.base.TsdkNetworkInfoParam;
+import com.huawei.ecterminalsdk.base.TsdkServiceSecurityParam;
+import com.huawei.ecterminalsdk.base.TsdkTlsInParam;
+import com.huawei.ecterminalsdk.base.TsdkTlsParam;
+import com.huawei.ecterminalsdk.base.TsdkTlsSmParam;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LocContext;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.io.File;
+
+import static com.huawei.ecterminalsdk.base.TsdkClientType.TSDK_E_CLIENT_MOBILE;
+
+/**
+ * This class is about init and uninit business component classes.
+ * 初始化与去初始化业务组件类
+ */
+public class ServiceManger {
+
+ private static final String TAG = ServiceManger.class.getSimpleName();
+
+ /**
+ * Instance object of ServiceManger component.
+ * 新建一个ServiceMgr对象
+ */
+ private static final ServiceManger serviceMgr = new ServiceManger();
+
+ /**
+ * The context
+ * 上下文
+ */
+ private Context context;
+
+ /**
+ * The app path
+ * APP路径
+ */
+ private String appPath;
+
+
+ /**
+ *
+ */
+ private TsdkManager tsdkManager;
+
+ private static final String CONTACT_FILE_PATH = Environment.getExternalStorageDirectory() + File.separator + "tupcontact";
+
+ /* 应用程序根据自身业务支持情况进行设置 */
+ private int appType = 0;
+ private boolean isSupportAudioAndVideoCall = true;
+ private boolean isSupportAudioAndVideoConf = true;
+ private boolean isSupportDataConf = true;
+ private boolean isSupportCTD = false;
+ private boolean isSupportIM = false;
+ private boolean isSupportRichMediaMessage = false;
+ private boolean isSupportAddressbook = true;
+
+ /**
+ * This method is used to get instance object of ServiceManger.
+ * 获取ServiceMgr对象实例
+ *
+ * @return ImMgr Return instance object of ServiceManger
+ * 返回一个ServiceMgr对象实例
+ */
+ public static ServiceManger getServiceMgr() {
+ return serviceMgr;
+ }
+
+ /**
+ * This method is used to init service.
+ * 初始化业务组件
+ *
+ * @param context 上下文
+ * @param appPath 路径
+ * @param isCloseSecurityChannel 是否关闭安全通道 0:打开通道,1:关闭通道
+ * @return
+ */
+ public boolean startService(Context context, String appPath, int isCloseSecurityChannel) {
+ LogUtils.d(TAG, "startService()", "startService start");
+ int ret;
+
+ LocContext.init(context);
+// LogUtil.setLogPath("CloudLink");
+
+// LogUtil.i(TAG, "sdk init is begin.");
+
+
+ tsdkManager = TsdkManager.getInstance(context, appPath, ServiceNotifyV2.getInstance());
+
+ /* Step 1, set log param */
+ TsdkLogParam logParam = new TsdkLogParam();
+ logParam.setFileCount(13);
+ logParam.setLevel(TsdkLogLevel.TSDK_E_LOG_DEBUG);
+ logParam.setMaxSizeKb(1024 * 4);
+ logParam.setPath(BaseAppContext.getInstance().getFilesDir() + File.separator + "HWCloudLink" + "/");
+// logParam.setPath(Environment.getExternalStorageDirectory() + File.separator + "ECSDKDemo" + "/");
+
+ ret = tsdkManager.setConfigParam(logParam);
+ if (ret != 0) {
+ LogUtil.e(TAG, "set log failed." + ret);
+ return false;
+ }
+
+ //企业通讯录配置
+ TsdkAppFilePathInfo appFilePathInfo = new TsdkAppFilePathInfo();
+ File files = new File(CONTACT_FILE_PATH + File.separator + "dept" + File.separator);
+ if (!files.exists()) {
+ files.mkdirs();
+ }
+ appFilePathInfo.setDeptFilePath(CONTACT_FILE_PATH + File.separator + "dept" + File.separator);
+ File file = new File(CONTACT_FILE_PATH + File.separator + "icon" + File.separator);
+ if (!file.exists()) {
+ file.mkdirs();
+ }
+ appFilePathInfo.setIconFilePath(CONTACT_FILE_PATH + File.separator + "icon" + File.separator);
+ ret = tsdkManager.setConfigParam(appFilePathInfo);
+ if (ret != 0) {
+ LogUtil.e(TAG, "config file path failed." + ret);
+ return false;
+ }
+
+ /* Step 2, init sdk */
+ TsdkAppInfoParam appInfoParam = new TsdkAppInfoParam();
+ appInfoParam.setClientType(TSDK_E_CLIENT_MOBILE);
+ appInfoParam.setProductName("Huawei TE Mobile");
+ appInfoParam.setDeviceSn("123");
+ appInfoParam.setSupportAudioAndVideoCall(this.isSupportAudioAndVideoCall ? 1 : 0);
+ appInfoParam.setSupportAudioAndVideoConf(this.isSupportAudioAndVideoConf ? 1 : 0);
+ appInfoParam.setSupportDataConf(this.isSupportDataConf ? 1 : 0);
+ appInfoParam.setSupportLdapFrontstage(1);
+ appInfoParam.setUseUiPlugin(0);
+ appInfoParam.setIsCloseSecurityChannel(isCloseSecurityChannel);
+
+ ret = tsdkManager.init(appInfoParam);
+ if (ret != 0) {
+ LogUtil.e(TAG, "Terminal SDK init failed." + ret);
+ return false;
+ }
+
+ /*Step 3, config service param */
+ ret = configServiceParam();
+ if (ret != 0) {
+ LogUtil.e(TAG, "config service param failed, return " + ret);
+ return false;
+ }
+ LogUtil.zzz("SDK初始化", "成功");
+
+ return true;
+
+ }
+
+ /**
+ * This method is used to uninit service.
+ * 去初始化基础组件
+ */
+ public void stopService() {
+ tsdkManager.uninit();
+ Intent intent = new Intent(BaseAppContext.getInstance(), CloudLinkService.class);
+ BaseAppContext.getInstance().stopService(intent);
+ }
+
+ /**
+ * This method is used to config service param.
+ * 各种服务的配置
+ *
+ * @return
+ */
+ private int configServiceParam() {
+ //待实现,配置各种业务的基本配置
+
+ return 0;
+ }
+
+ /**
+ * This method is used to security configuration.
+ * 安全配置
+ *
+ * @param mSrtpMode SRTP模式
+ */
+ public void securityParam(int mSrtpMode, int bfcpTransportMode) {
+ //Set security param
+ if (isSupportAudioAndVideoCall) {
+ TsdkServiceSecurityParam serviceSecurityParam = new TsdkServiceSecurityParam();
+ serviceSecurityParam.setBfcpTransportMode(bfcpTransportMode);
+ TsdkMediaSrtpMode srtpMode = TsdkMediaSrtpMode.enumOf(mSrtpMode);
+ if (null != srtpMode) {
+ serviceSecurityParam.setMediaSrtpMode(srtpMode);
+ }
+ LogUtil.zzz("登录", "登录模式", LogUtil.toJson(serviceSecurityParam));
+ if (null != TsdkManager.getInstance()) {
+ TsdkManager.getInstance().setConfigParam(serviceSecurityParam);
+ }
+ }
+ }
+
+ /**
+ * This method is used to network param.
+ * 网络参数
+ */
+ public void networkParam(String port, String serverUrl, int utt) {
+ //Set network param
+ if (isSupportAudioAndVideoCall) {
+ TsdkNetworkInfoParam networkInfoParam = new TsdkNetworkInfoParam();
+ if (!TextUtils.isEmpty(port)) {
+ networkInfoParam.setServerAddr(serverUrl);
+ networkInfoParam.setSipTransportMode(utt);
+ networkInfoParam.setSipServerPort(Integer.parseInt(port));
+ String httpsport = EncryptedSPTool.getString(Constant.LOGIN_SERVER_HTTPSPORT);
+ if (!TextUtils.isEmpty(httpsport)) {
+ networkInfoParam.setHttpsServerPort(Integer.valueOf(httpsport));
+ }
+ String[] split = serverUrl.split("\\.");
+ if (split.length != 0) {
+ LogUtil.zzz("登录", "网络配置", " {\"httpPort\":" + 0 + ",\"serverAddr\":\"***.***.***." + split[split.length - 1] + "\",\"sipServerPort\":" + port + ",\"sipTransportMode\":" + utt + "}");
+ }
+ if (null != TsdkManager.getInstance()) {
+ TsdkManager.getInstance().setConfigParam(networkInfoParam);
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is used to tls configuration.
+ * TLS配置
+ */
+ public void tlsParam(int openSmVerfity, TsdkTlsInParam tlsInParam, TsdkTlsSmParam tlsSmParam) {
+ if (isSupportAudioAndVideoCall) {
+ TsdkTlsParam tlsParam = new TsdkTlsParam();
+ // 国密验证,0:关闭国密验证,1:开启国密验证
+ tlsParam.setOpenSmVerfity(openSmVerfity);
+ // 非国密参数
+ tlsParam.setInTlsParam(tlsInParam);
+ // 国密参数,64位so暂未支持
+ tlsParam.setSmTlsParam(tlsSmParam);
+ //TLS兼容模式
+ tlsParam.setTlsCompatible(EncryptedSPTool.getBoolean(Constant.LOGIN_SERVER_TLS_MODE_SWITCH_STATE) ? 1 : 0);
+ // Set tls param
+ if (null != TsdkManager.getInstance()) {
+ int code = TsdkManager.getInstance().setConfigParam(tlsParam);
+// LogUtil.zzz("国密参数",tlsParam);
+ LogUtil.zzz("登录", "国密配置结果", code);
+ }
+ }
+ }
+
+
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/ServiceNotifyV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/ServiceNotifyV2.java
new file mode 100644
index 0000000..51cfed0
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/serivce/ServiceNotifyV2.java
@@ -0,0 +1,1485 @@
+package com.tengshisoft.chatmodule.hwclud.serivce;
+
+
+import android.content.Intent;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkAttendee;
+import com.huawei.ecterminalsdk.base.TsdkAudioNetQuality;
+import com.huawei.ecterminalsdk.base.TsdkAuxTokenOwnerInd;
+import com.huawei.ecterminalsdk.base.TsdkCallInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfBaseInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfListInfo;
+import com.huawei.ecterminalsdk.base.TsdkConfOperationResult;
+import com.huawei.ecterminalsdk.base.TsdkConfParam;
+import com.huawei.ecterminalsdk.base.TsdkConfSpeakerInfo;
+import com.huawei.ecterminalsdk.base.TsdkDecodeInfo;
+import com.huawei.ecterminalsdk.base.TsdkFailedInfo;
+import com.huawei.ecterminalsdk.base.TsdkForceLogoutInfo;
+import com.huawei.ecterminalsdk.base.TsdkJoinConfIndInfo;
+import com.huawei.ecterminalsdk.base.TsdkLoginFailedInfo;
+import com.huawei.ecterminalsdk.base.TsdkLoginSuccessInfo;
+import com.huawei.ecterminalsdk.base.TsdkLogoutSuccessInfo;
+import com.huawei.ecterminalsdk.base.TsdkNotifyHandUpAttendee;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtAuthType;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtBroadcastInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtCancelConfResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtChcekConfpwdExistedResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtCheckInResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtConfBaseInfoInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtLockConfInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtLogUploadResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtLoginGetUserInfoResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtLoginStatus;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtRealTimeBandWidth;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtRequestAuditSiteSwitchResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtSearchLdapContactsResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtServiceDeviceInfo;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtStatisticLocalQos;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtSubtitleCapabilityInd;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtSubtitleContentInfo;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtVerifyConfPwdResult;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtVideoSwitchNotify;
+import com.huawei.ecterminalsdk.base.TsdkServiceAccountType;
+import com.huawei.ecterminalsdk.base.TsdkSessionCodec;
+import com.huawei.ecterminalsdk.base.TsdkSessionModified;
+import com.huawei.ecterminalsdk.base.TsdkSoftTerminalInfo;
+import com.huawei.ecterminalsdk.base.TsdkSvcWatchInfo;
+import com.huawei.ecterminalsdk.base.TsdkTerminalLocalName;
+import com.huawei.ecterminalsdk.base.TsdkTimeZoneInfoList;
+import com.huawei.ecterminalsdk.base.TsdkUserInfoParam;
+import com.huawei.ecterminalsdk.base.TsdkVideoNetQuality;
+import com.huawei.ecterminalsdk.base.TsdkVideoOrientation;
+import com.huawei.ecterminalsdk.base.TsdkVideoViewRefresh;
+import com.huawei.ecterminalsdk.base.TsdkVideoViewType;
+import com.huawei.ecterminalsdk.base.TsdkVmrInfo;
+import com.huawei.ecterminalsdk.base.TsdkVoipAccountInfo;
+import com.huawei.ecterminalsdk.base.TsdkonEvtAuditDir;
+import com.huawei.ecterminalsdk.base.TsdkonEvtReferNotify;
+import com.huawei.ecterminalsdk.models.TsdkCommonResult;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.TsdkNotify;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.huawei.ecterminalsdk.models.conference.TsdkConference;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.UserInfoBeanV2;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.AudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.ConfConvertUtil;
+import com.tengshisoft.chatmodule.hwclud.manager.LdapFrontstageMgr;
+import com.tengshisoft.chatmodule.hwclud.manager.LoginMangerV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.SaveRecentCalls;
+import com.tengshisoft.chatmodule.hwclud.manager.SwitchAudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.utils.ConfigAppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.MediaUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.NetUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.util.TimerTask;
+
+import androidx.annotation.RequiresApi;
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+/**
+ * @author : isoftstone
+ * @date : 2021/5/27
+ * @Description :监听所有的TsdkNotify事件
+ */
+public class ServiceNotifyV2 implements TsdkNotify {
+
+ private static final String TAG = "ServiceNotify";
+ private static volatile ServiceNotifyV2 instance;
+
+ /**
+ * 音频网络质量
+ */
+ private int audioNetQuality = 0;
+ /**
+ * 视频网络质量
+ */
+ private int videoNetQuality = 0;
+
+ private ServiceNotifyV2() {
+ }
+
+ public static ServiceNotifyV2 getInstance() {
+ if (instance == null) {
+ synchronized (ServiceNotifyV2.class) {
+ if (instance == null) {
+ instance = new ServiceNotifyV2();
+ }
+ }
+ }
+ return instance;
+ }
+
+ public int getAudioNetQuality() {
+ return audioNetQuality;
+ }
+
+ public int getVideoNetQuality() {
+ return videoNetQuality;
+ }
+
+ public void setAudioNetQuality(int audioNetQuality) {
+ this.audioNetQuality = audioNetQuality;
+ }
+
+ public void setVideoNetQuality(int videoNetQuality) {
+ this.videoNetQuality = videoNetQuality;
+ }
+
+ /**
+ * 登陆成功
+ *
+ * @param userId 用户Id
+ * @param serviceAccountType 服务帐号类型
+ * @param loginSuccessInfo 登陆成功信息
+ */
+ @Override
+ public void onEvtLoginSuccess(int userId, TsdkServiceAccountType serviceAccountType, TsdkLoginSuccessInfo loginSuccessInfo) {
+ //loginServerType 4 组网环境:3.0 不等于4 组网环境:2.0
+ LogUtils.d("onEvtLoginSuccess loginServerType is" + loginSuccessInfo.getLoginServerType());
+ LoginMangerV2.getInstance().handleLoginSuccess(userId, serviceAccountType, loginSuccessInfo);
+ }
+
+ /**
+ * 登陆失败
+ *
+ * @param userId 用户Id
+ * @param serviceAccountType 服务帐号类型
+ * @param loginFailedInfo 登陆失败信息
+ */
+ @Override
+ public void onEvtLoginFailed(int userId, TsdkServiceAccountType serviceAccountType, TsdkLoginFailedInfo loginFailedInfo) {
+ LogUtil.zzz(TAG, "onEvtLoginFailed loginFailedInfo : " + loginFailedInfo.toString());
+ LoginMangerV2.getInstance().handleLoginFailed(loginFailedInfo);
+ }
+
+
+ /**
+ * VoIP帐号状态信息
+ *
+ * @param userId 用户Id
+ * @param voipAccountInfo VoIP帐号信息
+ */
+ @Override
+ public void onEvtVoipAccountStatus(int userId, TsdkVoipAccountInfo voipAccountInfo) {
+ LogUtil.zzz(TAG, "onEvtVoipAccountStatus");
+ // 设置帐号
+ UserInfoBeanV2 userInfoBean = new UserInfoBeanV2();
+ userInfoBean.setAccount(voipAccountInfo.getAccount());
+ // 设置终端号
+ userInfoBean.setNumber(voipAccountInfo.getNumber());
+ TsdkVoipAccountInfo newObject = LogUtil.getNewObject(voipAccountInfo, TsdkVoipAccountInfo.class);
+ if (newObject != null) {
+ newObject.setAccount(LogUtil.commonDisplay(newObject.getAccount()));
+ newObject.setNumber(LogUtil.commonDisplay(newObject.getNumber()));
+ newObject.setTerminal(LogUtil.commonDisplay(newObject.getTerminal()));
+ }
+ LogUtil.zzz(TAG, "onEvtVoipAccountStatus voipAccountInfo ", newObject);
+ LoginMangerV2.getInstance().handleVoipAccountStatus(voipAccountInfo);
+ }
+
+ /**
+ * 登出成功
+ *
+ * @param userId 用户Id
+ * @param serviceAccountType 服务帐号类型
+ * @param logoutSuccessInfo 异常登出的原因信息
+ */
+ @Override
+ public void onEvtLogoutSuccess(int userId, TsdkServiceAccountType serviceAccountType, TsdkLogoutSuccessInfo logoutSuccessInfo) {
+ LogUtil.zzz(TAG, "onEvtLogoutSuccess logoutSuccessInfo ", logoutSuccessInfo);
+ if (NetUtil.logoutCallBack != null) {
+ // 登出成功回调
+ NetUtil.logoutCallBack.success();
+ }
+ // 设置有网络
+ MeetingController.getInstance().setHaveNet(true);
+ LoginMangerV2.getInstance().handleLogoutSuccess(serviceAccountType, logoutSuccessInfo);
+ }
+
+ /**
+ * 登出失败
+ *
+ * @param userId 用户Id
+ * @param result 结果
+ */
+ @Override
+ public void onEvtLogoutFailed(int userId, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtLogoutFailed result ", result);
+ }
+
+ /**
+ * SMC3.0提示用户首次修改密码通知
+ *
+ * @param userId 用户Id
+ * @param result 结果
+ */
+ @Override
+ public void onEvtChangeFirstPasswordNotify(int userId, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtChangeFirstPasswordNotify result ", result);
+ LoginMangerV2.getInstance().handleLogoutFirstChangePwd(result);
+ }
+
+ /**
+ * 强制登出
+ *
+ * @param userId 用户Id
+ * @param serviceAccountType 服务帐号类型
+ * @param forceLogoutInfo 强制登出信息
+ */
+ @Override
+ public void onEvtForceLogout(int userId, TsdkServiceAccountType serviceAccountType, TsdkForceLogoutInfo forceLogoutInfo) {
+ LogUtil.zzz(TAG, "onEvtForceLogout serviceAccountType ", serviceAccountType);
+ LoginMangerV2.getInstance().handleForceLogout(forceLogoutInfo);
+ }
+
+
+ /**
+ * 获取临时帐号结果事件
+ *
+ * @param userId 用户Id
+ * @param result 操作结果
+ */
+ @Override
+ public void onEvtGetTempUserResult(int userId, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtGetTempUserResult result ", result);
+ MeetingMgrV2.getInstance().handleGetTempUserResult(userId, result);
+ }
+
+ /**
+ * 获取链接会议参数事件通知
+ *
+ * @param userId 用户Id
+ * @param confParam 会议信息
+ */
+ @Override
+ public void onEvtGetConfParamInd(int userId, TsdkConfParam confParam) {
+ LogUtil.zzz(TAG, "onEvtGetConfParamInd confParam ", confParam);
+ MeetingMgrV2.getInstance().onEvtGetConfParamInd(confParam);
+ }
+
+ /**
+ * 密码修改结果的通知
+ *
+ * @param failedInfo 错误信息,修改成功不返回,修改失败时返回
+ * @param result 结果
+ */
+ @Override
+ public void onEvtPasswordChangedResult(TsdkFailedInfo failedInfo, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtPasswordChangedResult failedInfo ", failedInfo);
+ LoginMangerV2.getInstance().handleLogoutFirstChangePwdResult(failedInfo, result);
+ }
+
+ /**
+ * 呼叫结果
+ *
+ * @param call 呼叫信息
+ * @param result 结果
+ */
+ @Override
+ public void onEvtCallStartResult(TsdkCall call, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtCallStartResult result ", result);
+ if (null != NetUtil.response) {
+ // 设置呼叫结果
+ NetUtil.response.onEvtCallStartResult(call);
+ }
+ }
+
+ /**
+ * 来电事件
+ *
+ * @param call 呼叫信息
+ * @param maybeVideoCall 是否是视频,true:视频通话,false:音频通话
+ */
+ @Override
+ public void onEvtCallIncoming(TsdkCall call, boolean maybeVideoCall) {
+ LogUtil.zzz(TAG, "onEvtCallIncoming maybeVideoCall ", maybeVideoCall);
+ showCallLog("onEvtCallIncoming", BaseAppContext.getInstance().getResources()
+ .getString(R.string.cloudLink_meting_incoming_call_information), call);
+ // 来电时关闭MinimizeService MCU倒换
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_END_SERVICE, null);
+ // 设置来电事件状态
+ MeetingController.getInstance().setCallComing(true);
+ // 设置来电状态、响铃时间
+ SaveRecentCalls.Companion.getInstance().onEvtCallIncoming();
+ if (call != null && call.getCallInfo() != null) {
+ // 初始化窗口
+ VideoMgr.getInstance().initVideoWindow(call.getCallInfo().getCallId());
+ }
+ // 处理来电事件
+ CallMgrV2.getInstance().handleCallComing(call, maybeVideoCall);
+
+ }
+
+ /**
+ * 呼出事件
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCallOutgoing(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtCallOutgoing");
+ showCallLog("onEvtCallOutgoing", BaseAppContext.getInstance().getResources()
+ .getString(R.string.cloudLink_meting_call_information), call);
+ // 设置呼出状态、时间
+ SaveRecentCalls.Companion.getInstance().onEvtCallOutgoing();
+ // 设置呼出事件状态
+ MeetingController.getInstance().setCallOutGoing(true);
+ if (call != null && call.getCallInfo() != null) {
+ // 初始化窗口
+ VideoMgr.getInstance().initVideoWindow(call.getCallInfo().getCallId());
+ // 设置对端号码
+ MeetingController.getInstance().setTerminal(call.getCallInfo().getPeerNumber());
+ // 处理呼出事件
+ CallMgrV2.getInstance().handleCallGoing(call);
+ }
+ }
+
+ /**
+ * 回铃音事件
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCallRingback(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtCallRingback");
+ // 处理响铃音事件
+ CallMgrV2.getInstance().handleCallRingback(call);
+ }
+
+ /**
+ * RTP创建事件
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCallRtpCreated(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtCallRtpCreated");
+ // 处理RTP创建事件
+ CallMgrV2.getInstance().handleCallRtpCreated(call);
+ }
+
+ /**
+ * 通话建立事件
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCallConnected(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtCallConnected");
+ // 请求音频焦点
+ MediaUtil.getInstance().requestFocus();
+ // 设置呼叫对象
+ MeetingController.getInstance().setCall(call);
+ // 开启CloudLinkService
+ Intent intent = new Intent(BaseAppContext.getInstance(), CloudLinkService.class);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ BaseAppContext.getInstance().startForegroundService(intent);
+ } else {
+ BaseAppContext.getInstance().startService(intent);
+ }
+ // 入会前,把上一次的可能未关闭的无码流dialog提示框关闭掉
+ Intent it = new Intent(Constant.NO_DURATION_DIALOG_CLOSE);
+ LocalBroadcastManager.getInstance(BaseAppContext.getInstance()).sendBroadcast(it);
+ // 设置会议总带宽
+ MeetingController.getInstance().setBandWidth(call.getCallInfo().getBandWidth(), call.getCallInfo().getSvcType() == 2);
+ // 设置是否是视频
+ SwitchAudioRouteManager.Companion.getInstance().setIsvideo(call.getCallInfo().getIsVideoCall() == 1);
+ // 构建音频路由管理类
+ new AudioRouteManager.Builder()
+ .setHasSetting(false)
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setIsVideo(call.getCallInfo().getIsVideoCall() == 1)
+ .build();
+ // 设置连接时间
+ SaveRecentCalls.Companion.getInstance().onEvtCallConnected();
+ if (NetUtil.response != null) {
+ LogUtil.zzz(TAG, "NetUtil.response onEvtCallConnected");
+ // 设置视频流连接
+ NetUtil.response.onEvtCallConnected(call);
+ }
+ // 处理通话建立事件
+ CallMgrV2.getInstance().handleCallConnected(call);
+ if (!MeetingController.getInstance().isCallComing() || !MeetingController.getInstance().isCallOutGoing()) {
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_SC, "");
+ }
+ }
+
+ /**
+ * 加入会议结果
+ *
+ * @param conference 会议信息
+ * @param result 操作结果
+ * @param info 会议接通信息
+ */
+ @Override
+ public void onEvtJoinConfResult(TsdkConference conference, TsdkCommonResult result, TsdkJoinConfIndInfo info) {
+ LogUtil.zzz(TAG, "onEvtJoinConfResult result ", result);
+ // 设置是否收到会控结果回调
+ MeetingController.getInstance().conferenceRight = true;
+ // 设置SvcBigConf
+ MeetingController.getInstance().setSvcBigConf(false);
+ // 加入会议结果
+ MeetingMgrV2.getInstance().handleJoinConfResult(conference, result, info);
+ }
+
+ /**
+ * 通话结束
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCallEnded(TsdkCall call) {
+ LogUtils.e("onCallEnded");
+ // 放弃音频焦点
+ MediaUtil.getInstance().abandonAudioFocus();
+ // 设置finishsp状态
+ MeetingController.getInstance().finishSp = true;
+ // 修改存储主席密码为空
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, "");
+ if (call == null || call.getCallInfo() == null) {
+ LogUtil.zzz(TAG, "onEvtCallEnded call or callInfo is null");
+ // 发送通话结束广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_CALL_END, new CallInfo());
+ return;
+ }
+ // 设置用户点击
+ SwitchAudioRouteManager.Companion.getInstance().setSettingAudioRoute(false);
+ // 通过call对象保存通话记录
+ SaveRecentCalls.Companion.getInstance().saveRecentCalls(call, MeetingMgrV2.getInstance().getSubject());
+ // 处理通话结束
+ CallMgrV2.getInstance().handleCallEnded(call);
+ if (NetUtil.response != null) {
+ // 通话结束
+ NetUtil.response.onEvtCallEnded(call);
+ }
+ // 设置是否是单向直播会议
+ MeetingMgrV2.getInstance().setFlag(false);
+ // 设置会议主题
+ MeetingMgrV2.getInstance().setSubject("");
+ // 设置当前正在召开的会议为空
+ MeetingMgrV2.getInstance().resetCurrentConference();
+ // 清除此状态
+ CallMgrV2.getInstance().setReferCall(false);
+ // 重置会议状态
+ MeetingController.getInstance().reset();
+ // 清除方向监听
+ VideoMgr.getInstance().cleanOrientationListener();
+ // 释放视频资源
+ CallMgrV2.getInstance().videoDestroy();
+ //
+ MeetingController.getInstance().isMeetingDuration = false;
+ }
+
+ /**
+ * 呼叫销毁事件
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCallDestroy(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtCallDestroy");
+ if (call == null || call.getCallInfo() == null) {
+ LogUtil.zzz(TAG, "onEvtCallDestroy call or callInfo is null");
+ return;
+ }
+ // 处理呼叫销毁事件
+ CallMgrV2.getInstance().handleCallDestroy(call);
+ }
+
+ /**
+ * 远端请求打开视频(音频通话升级为视频通话)
+ *
+ * @param call 呼叫信息
+ * @param orientation 视频横竖屏状态
+ */
+ @Override
+ public void onEvtOpenVideoReq(TsdkCall call, TsdkVideoOrientation orientation) {
+ LogUtil.zzz(TAG, "onEvtOpenVideoReq");
+ // 会议是否最小化
+ if (MeetingController.getInstance().isMinimize()) {
+ // 发送音频转视频广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_MINIMIZE_SERVICE_VOICE_TO_VIDEO, call);
+ } else {
+ CallMgrV2.getInstance().handleOpenVideoReq(call, orientation);
+ }
+ }
+
+ /**
+ * 远端拒绝音频转视频
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtRefuseOpenVideoInd(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtRefuseOpenVideoInd");
+ // 处理远端拒绝音频转视频
+ CallMgrV2.getInstance().handleRefuseOpenVideoInd(call);
+ // 构建音频路由管理类
+ new AudioRouteManager.Builder()
+ .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .build();
+ }
+
+ /**
+ * 关闭视频(视频转音频)通知
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtCloseVideoInd(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtCloseVideoInd");
+ if (MeetingController.getInstance().isMinimize()) {
+ // 发送视频转音频广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_MINIMIZE_SERVICE_VIDEO_TO_VOICE, call);
+ } else {
+ // 处理视频转音频结果
+ CallMgrV2.getInstance().handleCloseVideoInd(call);
+ }
+ // 设置是否显示请求音频转视频界面
+ MeetingController.getInstance().setShowRequestAddVideo(false);
+ // 设置是否是视频
+ SwitchAudioRouteManager.Companion.getInstance().setIsvideo(false);
+ // 构建音频路由管理类
+ new AudioRouteManager.Builder()
+ .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .build();
+ }
+
+ /**
+ * 打开视频(音频转视频)通知
+ *
+ * @param call 呼叫信息
+ */
+ @Override
+ public void onEvtOpenVideoInd(TsdkCall call) {
+ LogUtil.zzz(TAG, "onEvtOpenVideoInd");
+ // 设置是否是视频
+ SwitchAudioRouteManager.Companion.getInstance().setIsvideo(true);
+ // 构建音频路由管理类
+ new AudioRouteManager.Builder()
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .build();
+ if (MeetingController.getInstance().isMinimize()) {
+ // 发送打开视频(音频转视频)广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_MINIMIZE_SERVICE_OPEN_VIDEO, call);
+ } else {
+ // 处理音频转视频结果
+ CallMgrV2.getInstance().handleOpenVideoInd(call);
+ }
+ }
+
+ /**
+ * 刷新窗口信息通知
+ *
+ * @param call 呼叫信息
+ * @param refreshInfo 视频刷新事件信息
+ */
+ @Override
+ public void onEvtRefreshViewInd(TsdkCall call, TsdkVideoViewRefresh refreshInfo) {
+ LogUtil.zzz(TAG, "onEvtRefreshViewInd refreshInfo ", refreshInfo);
+ // 刷新窗口信息
+ CallMgrV2.getInstance().handleRefreshViewInd(call, refreshInfo);
+ }
+
+ /**
+ * 呼叫路由变更通知
+ *
+ * @param call 呼叫信息
+ * @param route 音频路由下标,1:扬声器,2:蓝牙,3:听筒,4:耳机
+ */
+ @Override
+ public void onEvtCallRouteChange(TsdkCall call, int route) {
+ LogUtil.zzz(TAG, "onEvtCallRouteChange route " + route);
+ // 处理呼叫路由变更
+ CallMgrV2.getInstance().onEvtCallRouteChange(route);
+ }
+
+ /**
+ * 音频文件播放结束通知
+ *
+ * @param handle 文件句柄
+ */
+ @Override
+ public void onEvtPlayMediaEnd(int handle) {
+ LogUtil.zzz(TAG, "onEvtPlayMediaEnd handle " + handle);
+ }
+
+ /**
+ * 会话修改完成通知
+ *
+ * @param call 呼叫信息
+ * @param sessionModified 会话修改完成结果信息
+ */
+ @Override
+ public void onEvtSessionModified(TsdkCall call, TsdkSessionModified sessionModified) {
+ LogUtil.zzz(TAG, "onEvtSessionModified sessionModified ", sessionModified);
+ // 设置会议总带宽
+ MeetingController.getInstance().setBandWidth(sessionModified.getBandWidth(), sessionModified.getSvcType() == 2);
+ // svc倒换替换
+ MeetingController.getInstance().setSvcLableSsrc(sessionModified.getSvcLableSsrc());
+ // 会话类型变更后 需刷新下选看
+ MeetingController.getInstance().setMeetingSessionModified(true);
+ }
+
+ /**
+ * 会话正在使用的编解码器信息
+ *
+ * @param call 呼叫信息
+ * @param sessionCodec 会话正在使用的编解码器信息
+ */
+ @Override
+ public void onEvtSessionCodec(TsdkCall call, TsdkSessionCodec sessionCodec) {
+ LogUtil.zzz(TAG, "onEvtSessionCodec sessionCodec ", sessionCodec);
+ }
+
+ /**
+ * 结束呼叫失败事件
+ *
+ * @param call 呼叫信息
+ * @param result 结果
+ */
+ @Override
+ public void onEvtEndcallFailed(TsdkCall call, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtEndcallFailed result ", result);
+ }
+
+
+ /**
+ * 预约会议结果
+ *
+ * @param result 操作结果
+ * @param confBaseInfo 会议基础信息,仅3.0返回
+ */
+ @Override
+ public void onEvtBookConfResult(TsdkCommonResult result, TsdkConfBaseInfo confBaseInfo) {
+ LogUtil.zzz(TAG, "onEvtBookConfResult result ", result);
+ // 创建会议结果
+ MeetingMgrV2.getInstance().handleBookConfResult(result, confBaseInfo);
+ }
+
+ /**
+ * 查询会议列表结果
+ *
+ * @param result 操作结果
+ * @param confList 会议列表信息
+ */
+ @Override
+ public void onEvtQueryConfListResult(TsdkCommonResult result, TsdkConfListInfo confList) {
+ LogUtil.zzz(TAG, "onEvtQueryConfListResult result ", result);
+ // 查询会议列表
+ MeetingMgrV2.getInstance().handleQueryConfListResult(result, confList);
+ }
+
+ /**
+ * 请求vmr会议信息列表结果,绑定了VMR的帐号登陆后默认会收到此通知
+ *
+ * @param result 结果
+ * @param vmrInfo 虚拟会议详情
+ */
+ @Override
+ public void onEvtGetVmrListResult(TsdkCommonResult result, TsdkVmrInfo vmrInfo) {
+ LogUtil.zzz(TAG, "onEvtGetVmrListResult result ", result);
+ // 请求vmr列表结果
+ MeetingMgrV2.getInstance().handleGetVmrListResult(result, vmrInfo);
+ }
+
+ /**
+ * 鉴权刷新失败
+ *
+ * @param userId 用户Id
+ * @param result 结果
+ */
+ @Override
+ public void onEvtAuthRefreshFailed(int userId, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtAuthRefreshFailed result ", result);
+ LoginMangerV2.getInstance().handleAuthRefreshFailed(result);
+ }
+
+ /**
+ * 会控操作结果
+ *
+ * @param conference 会议信息
+ * @param result 会控操作结果信息
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public void onEvtConfctrlOperationResult(TsdkConference conference, TsdkConfOperationResult result) {
+ LogUtil.zzz(TAG, "onEvtConfctrlOperationResult result ", result);
+ // 会控操作结果
+ MeetingMgrV2.getInstance().handleConfctrlOperationResult(conference, result);
+ }
+
+ /**
+ * 会议信息及状态更新
+ *
+ * @param conference 会议信息
+ */
+ @Override
+ public void onEvtInfoAndStatusUpdate(TsdkConference conference) {
+ LogUtil.zzz(TAG, "onEvtInfoAndStatusUpdate conference " + conference);
+ if (conference.getAttendeeList().size() == 1) {
+ LogUtil.zzz(TAG, "onEvtInfoAndStatusUpdate isMute " + conference.getAttendeeList().get(0).getStatusInfo().getIsMute());
+ }
+ if (conference.getAttendeeList().size() > 1) {
+ // 设置是否有与会者
+ MeetingController.getInstance().isHasMember = true;
+ }
+ if (conference.isSvcForceWatchInd()) {
+ // SC转换后画面卡住
+ MeetingMgrV2.getInstance().scChangeRefreshView();
+ }
+ // 与会者状态信息和状态更新处理
+ MeetingMgrV2.getInstance().handleInfoAndStatusUpdate(conference);
+ }
+
+ /**
+ * 发言方通知
+ *
+ * @param conference 会议信息
+ * @param speakerList 发言方信息
+ */
+ @Override
+ public void onEvtSpeakerInd(TsdkConference conference, TsdkConfSpeakerInfo speakerList) {
+ LogUtil.zzz(TAG, "onEvtSpeakerInd speakerNum ", speakerList.getSpeakerNum());
+ // 发言人处理
+ MeetingMgrV2.getInstance().handleSpeakerInd(speakerList);
+ }
+
+ /**
+ * 申请会控权限失败
+ *
+ * @param conference 会议信息
+ * @param result 操作结果
+ */
+ @Override
+ public void onEvtRequestConfRightFailed(TsdkConference conference, TsdkCommonResult result) {
+ LogUtil.zzz(TAG, "onEvtRequestConfRightFailed result ", result);
+ }
+
+ /**
+ * 会议来电通知
+ *
+ * @param conference 会议信息
+ */
+ @Override
+ public void onEvtConfIncomingInd(TsdkConference conference) {
+ LogUtil.zzz(TAG, "onEvtConfIncomingInd");
+ // 会议来电处理
+ MeetingMgrV2.getInstance().handleConfIncomingInd(conference);
+ }
+
+ /**
+ * 查询企业地址本的联系人信息结果
+ *
+ * @param commonResult 查询结果
+ * @param param 查询到的联系人结果信息
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ public void onEvtSearchLdapContactsResult(TsdkCommonResult commonResult, TsdkOnEvtSearchLdapContactsResult.Param param) {
+ LogUtil.zzz(TAG, "onEvtSearchLdapContactsResult commonResult ", commonResult);
+ // 查询联系人信息返回结果
+ LdapFrontstageMgr.getInstance().handleSearchLdapContactResult(commonResult, param);
+ if (null != NetUtil.searchUsernameFormId && null != commonResult) {
+ UIUtil.runUI(new TimerTask() {
+ @Override
+ public void run() {
+ if (commonResult.result == 1) {
+ if (null != param && null != param.searchResultData && null != param.searchResultData.getLdapContactList()) {
+ NetUtil.searchUsernameFormId.success(param);
+ } else {
+ //查詢成功結果爲空
+ NetUtil.searchUsernameFormId.fail(commonResult.result, commonResult.reasonDescription);
+ }
+ } else {
+ NetUtil.searchUsernameFormId.fail(commonResult.result, commonResult.reasonDescription);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 企业地址本服务启动结果通知
+ *
+ * @param commonResult 查询结果 0 LDAP服务启动成功 1 LDAP服务启动失败 3 LDAP服务配置失败
+ */
+ @Override
+ public void onEvtLdapStartServiceResult(TsdkCommonResult commonResult) {
+ LogUtil.zzz(TAG, "onEvtLdapStartServiceResult commonResult ", commonResult);
+ }
+
+ /**
+ * 通话质量通知
+ *
+ * @param evtStatisticLocalQos 通话质量结果上报
+ */
+ @Override
+ public void onEvtStatisticLocalQos(TsdkOnEvtStatisticLocalQos evtStatisticLocalQos) {
+ LogUtil.zzz(TAG, "onEvtStatisticLocalQos");
+ }
+
+ /**
+ * 当前会议基础信息通知
+ *
+ * @param evtConfBaseInfoInd 会议基础信息
+ */
+ @Override
+ public void onEvtConfBaseInfoInd(TsdkOnEvtConfBaseInfoInd evtConfBaseInfoInd) {
+ LogUtil.zzz(TAG, "onEvtConfBaseInfoInd");
+ // 加入会议结果
+ MeetingMgrV2.getInstance().onEvtConfBaseInfoInd(evtConfBaseInfoInd);
+
+ }
+
+
+ /**
+ * 视频辅流发送成功的通知
+ *
+ * @param callId 呼叫Id
+ */
+ @Override
+ public void onEvtAuxSending(int callId) {
+ LogUtil.zzz(TAG, "onEvtAuxSending");
+ // 辅流发送成功通知
+ MeetingMgrV2.getInstance().onEvtAuxSending(callId);
+ }
+
+ /**
+ * 视频辅流接收成功的通知
+ *
+ * @param callId 呼叫Id
+ */
+ @Override
+ public void onEvtAuxDataRecving(int callId) {
+ LogUtil.zzz(TAG, "onEvtAuxDataRecving");
+ }
+
+ /**
+ * 视频辅流停止通知
+ *
+ * @param callId 呼叫Id
+ */
+ @Override
+ public void onEvtAuxDataStopped(int callId) {
+ LogUtil.zzz(TAG, "onEvtAuxDataStopped");
+ // 解决先收到开启服务后收到停止辅流关闭服务的问题
+ ConfigAppUtil.setIsAuxServiceStopped(false);
+ // 辅流状态改变通知
+ MeetingMgrV2.getInstance().handleAuxDataStateChange(false);
+ }
+
+ /**
+ * 视频辅流共享失败通知
+ *
+ * @param callId 呼叫Id
+ * @param failCode 0:启动成功
+ * 1:服务端拒绝令牌授予
+ * 2:UDP网络异常
+ * 3:TCP网络异常
+ * 4:本端发送报文后,没有收到对方的响应
+ * 5:令牌请求失败,目前由于重协商角色变换导致
+ * 6:作为客户端,抢发辅流失败
+ * 7:TLS链路异常,如认证失败,断网等
+ * 8:辅流开启失败
+ */
+ @Override
+ public void onEvtAuxShareFailed(int callId, int failCode) {
+ LogUtil.zzz(TAG, "onEvtAuxShareFailed", " callId" + callId + ",failCode " + failCode);
+ // 辅流发送失败通知
+ MeetingMgrV2.getInstance().onEvtAuxShareFailed(failCode);
+ }
+
+ /**
+ * 视频辅流解码成功通知
+ *
+ * @param decodeInfo 视频辅流解码信息类
+ */
+ @Override
+ public void onEvtDecodeSuccess(TsdkDecodeInfo decodeInfo) {
+ int mediaType = decodeInfo.getMediatype();
+ int index = TsdkVideoViewType.TSDK_E_VIEW_AUX_DATA_VIEW.getIndex();
+ LogUtil.zzz(TAG, "onEvtDecodeSuccess mediaType ", mediaType);
+ if (mediaType == index) {
+ // 辅流状态改变通知
+ MeetingMgrV2.getInstance().handleAuxDataStateChange(true);
+ }
+ }
+
+ /**
+ * 观看SVC与会者接口调用后收到的通知
+ *
+ * @param handle 操作句柄
+ * @param svcWatchInfo SVC观看者信息
+ */
+ @Override
+ public void onEvtSvcWatchInd(int handle, TsdkSvcWatchInfo svcWatchInfo) {
+ LogUtil.zzz(TAG, "onEvtSvcWatchInd handle ", handle);
+ MeetingMgrV2.getInstance().onEvtSvcWatchInd(svcWatchInfo);
+ }
+
+ /**
+ * 通知上层无码流持续时间
+ *
+ * @param duration 无码流时长
+ */
+ @Override
+ public void onEvtNoStreamDuration(int duration) {
+ LogUtil.zzz(TAG, "onEvtNoStreamDuration duration ", duration);
+ // 发送无码流通知
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_NO_STREAM_DURATION, duration);
+ }
+
+ /**
+ * 呼叫转移通知
+ *
+ * @param result 操作结果
+ * @param notify 通知信息
+ */
+ @Override
+ public void onEvtReferNotify(TsdkCommonResult result, TsdkonEvtReferNotify notify) {
+ LogUtil.zzz(TAG, "onEvtReferNotify result ", result);
+ // 设置呼叫状态
+ CallMgrV2.getInstance().setReferCall(true);
+ }
+
+ /**
+ * type=0;单向
+ * type=1;点对点
+ */
+ @Override
+ public void onEvtAuditDir(TsdkCommonResult tsdkCommonResult, TsdkonEvtAuditDir tsdkonEvtAuditDir) {
+ switch (tsdkonEvtAuditDir.param.directionType) {
+ case 0:
+ LogUtil.zzz(TAG, "onEvtAuditDir type=0, tsdkCommonResult ", tsdkCommonResult);
+ // 设置是否是单向直播会议
+ MeetingMgrV2.getInstance().setFlag(true);
+ MeetingMgrV2.getInstance().handleUnidirectLiveBroadcastConfResult(tsdkCommonResult, tsdkonEvtAuditDir);
+ break;
+ case 1:
+ LogUtil.zzz(TAG, "onEvtAuditDir type=1, tsdkCommonResult ", tsdkCommonResult);
+ if (MeetingMgrV2.getInstance().isFlag()) {
+ // 单向多转换
+ MeetingMgrV2.getInstance().handleUnidirectLiveBroadcastConfResult(tsdkCommonResult, tsdkonEvtAuditDir);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * 上报用户认证类型,是否外部认证的通知
+ *
+ * @param result 结果
+ * @param notify 鉴权类型信息结构体
+ */
+ @Override
+ public void onEvtAuthTypeNotify(TsdkCommonResult result, TsdkOnEvtAuthType notify) {
+ LogUtil.zzz(TAG, "onEvtAuthTypeNotify notify", notify);
+ // 存储认证方式 0 未认证 1 本地认证 2 外部认证 3 无效值
+ EncryptedSPTool.putInt(Constant.AUTH_TYPE, notify.param.authType);
+ }
+
+ @Override
+ public void onEvtRequestAuditSiteSwitchResult(TsdkCommonResult tsdkCommonResult, TsdkOnEvtRequestAuditSiteSwitchResult tsdkOnEvtRequestAuditSiteSwitchResult) {
+ LogUtil.zzz(TAG, "onEvtRequestAuditSiteSwitchResult tsdkCommonResult ", tsdkCommonResult);
+ }
+
+ /**
+ * 查询时区列表结果通知
+ *
+ * @param result 结果
+ * @param timeZoneInfoList 查询到的时区信息
+ */
+ @Override
+ public void onEvtTimeZoneListResult(TsdkCommonResult result, TsdkTimeZoneInfoList timeZoneInfoList) {
+ LogUtil.zzz(TAG, "onEvtTimeZoneListResult result ", result);
+ // 处理获取到的时区列表结果
+ MeetingMgrV2.getInstance().handleTimeZoneListResult(result, timeZoneInfoList);
+ }
+
+ /**
+ * SMC上报会议签到结果通知
+ *
+ * @param commonResult 操作结果
+ * @param evtCheckInResult 会议签到结果信息
+ */
+ @Override
+ public void onEvtCheckInResult(TsdkCommonResult commonResult, TsdkOnEvtCheckInResult evtCheckInResult) {
+ LogUtil.zzz(TAG, "onEvtCheckInResult commonResult ", commonResult);
+ // SMC上报会议签到结果(3.0及以后版本)通知处理
+ MeetingMgrV2.getInstance().handleCheckInResult(commonResult);
+ }
+
+
+ /**
+ * 取消预约会议结果
+ *
+ * @param commonResult 操作结果
+ * @param cancelConfResult 取消会议结果信息
+ */
+ @Override
+ public void onEvtCancelConfResult(TsdkCommonResult commonResult, TsdkOnEvtCancelConfResult cancelConfResult) {
+ LogUtil.zzz(TAG, "onEvtCancelConfResult commonResult ", commonResult);
+ if (null != NetUtil.cancelConfResultCallBack && null != commonResult) {
+ UIUtil.runUI(new TimerTask() {
+ @Override
+ public void run() {
+ if (commonResult.result == 0) {
+ NetUtil.cancelConfResultCallBack.success();
+ } else {
+ NetUtil.cancelConfResultCallBack.fail(commonResult.result, commonResult.reasonDescription);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 日志上传结果通知
+ *
+ * @param commonResult 操作结果
+ * @param evtLogUploadResult 上传日志结果信息
+ */
+ @Override
+ public void onEvtLogUploadResult(TsdkCommonResult commonResult, TsdkOnEvtLogUploadResult evtLogUploadResult) {
+ LogUtil.zzz(TAG, "onEvtLogUploadResult commonResult ", commonResult);
+ if (null != commonResult) {
+ // 发送日志上传广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_LOG_UPDATE, commonResult.result == 0);
+ }
+ }
+
+ /**
+ * 音频输入输出设备变更
+ *
+ * @param route 0 蓝牙耳机断开
+ * 1 蓝牙耳机连接
+ * 2 有线耳机断开
+ * 3 有线耳机连接
+ */
+ @Override
+ public void onEvtCallDeviceStatusChange(int route) {
+ Log.d(TAG, "onEvtCallDeviceStatusChange route " + route);
+ switch (route) {
+ case 0:
+ // 设置蓝牙耳机连接状态
+ SwitchAudioRouteManager.Companion.getInstance().setBluetoothHeadset(false);
+ break;
+ case 2:
+ // 设置有线耳机连接状态
+ SwitchAudioRouteManager.Companion.getInstance().setWiredHeadset(false);
+ break;
+ case 1:
+ // 设置蓝牙耳机连接状态
+ SwitchAudioRouteManager.Companion.getInstance().setBluetoothHeadset(true);
+ break;
+ case 3:
+ // 设置有线耳机连接状态
+ SwitchAudioRouteManager.Companion.getInstance().setWiredHeadset(true);
+ break;
+ default:
+ break;
+ }
+ // 设置耳机是否接入
+ SwitchAudioRouteManager.Companion.getInstance().setHeadsetPlug(
+ SwitchAudioRouteManager.Companion.getInstance().getBluetoothHeadset()
+ | SwitchAudioRouteManager.Companion.getInstance().getWiredHeadset());
+ if (SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug()) {
+ // 构建音频路由管理类
+ new AudioRouteManager.Builder()
+ .setHasSetting(false)
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+ .build();
+ } else {
+ // 构建音频路由管理类
+ new AudioRouteManager.Builder()
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .setIsVideo(SwitchAudioRouteManager.Companion.getInstance().getIsvideo())
+ .build();
+ }
+
+ LogUtil.zzz(TAG, "onEvtCallDeviceStatusChange isSettingAudioRoute " + SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute()
+ , " headsetPlug " + SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug()
+ , " isVideo " + SwitchAudioRouteManager.Companion.getInstance().getIsvideo());
+ CallMgrV2.getInstance().handleAudioDeviceChanged(route);
+ }
+
+ /**
+ * 获取软终端版本信息结果通知(SMC3.0版本)
+ *
+ * @param commonResult 操作结果
+ * @param softTerminalInfo 软终端版本信息
+ */
+ @Override
+ public void onEvtGetSTerminalInfoResult(TsdkCommonResult commonResult, TsdkSoftTerminalInfo softTerminalInfo) {
+ LogUtil.zzz(TAG, "onEvtGetSTerminalInfoResult commonResult ", commonResult);
+ if (NetUtil.checkVersionCallBack != null) {
+ UIUtil.runUI(new TimerTask() {
+ @Override
+ public void run() {
+ if (commonResult.getResult() == 0) {
+ LogUtil.zzz(TAG, "onEvtGetSTerminalInfoResult ucAndroidVersionNum " + softTerminalInfo.getUcAndroidVersionNum());
+ NetUtil.checkVersionCallBack.success(softTerminalInfo.getUcAndroidVersionNum(), softTerminalInfo.getUcAndroidLink());
+ } else {
+ NetUtil.checkVersionCallBack.fail(commonResult.getResult(), commonResult.getResult() + "");
+ }
+ }
+ });
+ }
+
+ }
+
+ /**
+ * 升级APP,返回下载链接
+ */
+ @Override
+ public void onEvtGetSoftTerminalDownloadPathResult(TsdkCommonResult tsdkCommonResult, String s) {
+ LogUtil.zzz(TAG, "onEvtGetSoftTerminalDownloadPathResult tsdkCommonResult ", tsdkCommonResult, TextUtils.isEmpty(s));
+ }
+
+ /**
+ * 上报用户相关信息
+ *
+ * @param userInfoResult 用户信息
+ */
+ @Override
+ public void onEvtGetUserInfoResult(TsdkOnEvtLoginGetUserInfoResult userInfoResult) {
+ LogUtil.zzz(TAG, "onEvtGetUserInfoResult userInfoParam ", userInfoResult.param.userInfoParam);
+ TsdkUserInfoParam userInfoParam = userInfoResult.param.userInfoParam;
+ if (userInfoParam != null) {
+ LoginMangerV2.getInstance().userInfoParam = userInfoParam;
+ if (userInfoResult.param.reasonCode != 0) {
+ LogUtil.zzz(TAG, "onEvtGetUserInfoResult reasonCode " + userInfoResult.param.reasonCode);
+ return;
+ }
+ // 设置用户名
+ LoginMangerV2.getInstance().setDisplayName(userInfoParam.getUserName());
+ EncryptedSPTool.putString(Constant.MINE_USER_NAME, userInfoParam.getUserName());
+ // 设置终端绑定号码
+ LoginMangerV2.getInstance().setTerminal(userInfoParam.getTerminalMiddleuri());
+ // 设置组织名称
+ LoginMangerV2.getInstance().setDeptName(userInfoParam.getAccountOrgName().equals("root") ? "VC" : userInfoParam.getAccountOrgName());
+ // 设置终端类型
+ LoginMangerV2.getInstance().setTerminalType(userInfoParam.getTerminalType());
+ // 发送获取用户信息结果广播
+ Intent it = new Intent(BroadcastConstant.ACTION_GET_USER_INFO_RESULT);
+ it.putExtra("GetUserInfo_DisplayName", userInfoParam.getUserName());
+ it.putExtra("GetUserInfo_Terminal", userInfoParam.getTerminalMiddleuri());
+ LocalBroadcastManager.getInstance(BaseAppContext.getInstance()).sendBroadcast(it);
+ // 会议中终端别名参数
+ TsdkTerminalLocalName terminalLocalName = new TsdkTerminalLocalName();
+ if (UIUtil.getStringCharCount(userInfoParam.getUserName()) > Constant.NAME_CHARACTER_LENGTH_192) {
+ String userName = UIUtil.subStringForCharCount(Constant.NAME_CHARACTER_LENGTH_192, userInfoParam.getUserName());
+ // 会议中终端别名
+ terminalLocalName.setLocalName(userName);
+ } else {
+ // 会议中终端别名
+ terminalLocalName.setLocalName(userInfoParam.getUserName());
+ }
+ // 设置会议中终端别名参数
+ TsdkManager.getInstance().setConfigParam(terminalLocalName);
+ LogUtil.zzz("onEvtGetUserInfoResult uUserName " + LogUtil.commonDisplay(userInfoParam.getUserName()) + ";terminalMiddleUri " + LogUtil.commonDisplay(userInfoParam.getTerminalMiddleuri()));
+ } else {
+ LogUtil.zzz(TAG, "onEvtGetUserInfoResult userInfo is null");
+ }
+ }
+
+ /**
+ * 登陆状态通知
+ *
+ * @param param 断网信息
+ */
+ @Override
+ public void onEvtLoginStatus(TsdkOnEvtLoginStatus.Param param) {
+ LogUtil.zzz(TAG, "onEvtLoginStatus param ", param);
+ boolean isLineStatus = param.onlineStatus == 0;
+ UIUtil.runUI(() -> {
+ LogUtil.zzz(TAG, "isLineStatus", isLineStatus);
+ // 设置是否有网络
+ MeetingController.getInstance().setHaveNet(isLineStatus);
+ // 发送是否有网络广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_NET_WORK_IS_CONNECT, isLineStatus);
+ });
+ }
+
+ /**
+ * 字幕能力上报
+ *
+ * @param param 上报信息
+ */
+ @Override
+ public void onEvtServiceDeviceInfo(TsdkOnEvtServiceDeviceInfo.Param param) {
+ LogUtil.zzz(TAG, "onEvtServiceDeviceInfo TsdkOnEvtServiceDeviceInfo.param ", param.serviceDeviceInfo.toString());
+ int isSupportSubtitle = param.serviceDeviceInfo.getHasAiServer();
+ EncryptedSPTool.putInt(ConstantsV2.IS_SUPPORT_SUBTITLE, isSupportSubtitle);
+ }
+
+ @Override
+ public void onEvtAudioNetQualityResult(TsdkAudioNetQuality tsdkAudioNetQuality) {
+ if (tsdkAudioNetQuality != null && tsdkAudioNetQuality.param != null) {
+ LogUtil.zzz(TAG, "onEvtAudioNetQualityResult audioNetQuality.param ", tsdkAudioNetQuality.param);
+ // 下行网络级别
+ int netLevel = tsdkAudioNetQuality.param.netLevel.getDownNetLevel();
+ // 发送音频会话中质量信息广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL, netLevel);
+ // 上行网络级别
+ int audLevel = tsdkAudioNetQuality.param.netLevel.getUpNetLevel();
+ LogUtil.zzz(TAG, "onEvtAudioNetQualityResult audLevel: " + audLevel + " ,oldLevel " + tsdkAudioNetQuality);
+ if (audLevel <= 3 && audLevel != audioNetQuality) {
+ // 网络状况差弹窗
+ ToastUtils.show(BaseAppContext.getInstance().getResources().getString(R.string.cloudLink_net_work_weak));
+ }
+ // 音频网络质量
+ audioNetQuality = audLevel;
+ }
+ }
+
+ /**
+ * 返回视频会话中质量信息
+ *
+ * @param tsdkVideoNetQuality 视频质量
+ */
+ @Override
+ public void onEvtVideoNetQualityResult(TsdkVideoNetQuality tsdkVideoNetQuality) {
+ if (tsdkVideoNetQuality != null && tsdkVideoNetQuality.param != null) {
+ LogUtil.zzz(TAG, "onEvtVideoNetQualityResult videoNetQuality.param ", tsdkVideoNetQuality.param);
+ // 下行网络级别
+ int netLevel = tsdkVideoNetQuality.param.netLevel.getDownNetLevel();
+ // 发送音频会话中质量信息广播
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_UPDATE_SERVER_NET_LEVEL, netLevel);
+ // 上行网络级别
+ int videoLevel = tsdkVideoNetQuality.param.netLevel.getUpNetLevel();
+ LogUtil.zzz(TAG, "onEvtVideoNetQualityResult videoLevel:" + videoLevel + " ,oldLevel " + videoNetQuality);
+ if (videoLevel <= 3 && videoLevel != videoNetQuality) {
+ // 网络状况差弹窗
+ ToastUtils.show(BaseAppContext.getInstance().getResources().getString(R.string.cloudLink_net_work_weak));
+ }
+ // 音频网络质量
+ videoNetQuality = videoLevel;
+ }
+
+ }
+
+ /**
+ * 大会议模式事件通知
+ *
+ * @param notify 1表示可以任意选看,2表示只允许主持人选看
+ */
+ @Override
+ public void onEvtSvcWatchPolicyInd(int notify) {
+ LogUtil.zzz(TAG, "onEvtSvcWatchPolicyInd notify ", notify);
+ // 设置SvcBigConf
+ MeetingController.getInstance().setSvcBigConf(notify == 2);
+ // 弹窗提示 进入大会模式,当前不支持自由选看
+ ToastUtils.show(BaseAppContext.getInstance().getResources().getString(R.string.cloudLink_svc_big_conf_hint));
+ }
+
+ /**
+ * 更新VMR信息结果通知
+ *
+ * @param commonResult 结果
+ */
+ @Override
+ public void onEvtUpdateVmrInfoResult(TsdkCommonResult commonResult) {
+ MeetingMgrV2.getInstance().onEvtUpdateVmrInfoSucceed(commonResult);
+ LogUtil.zzz(TAG, "onEvtUpdateVmrInfoResult result ", commonResult.result);
+ }
+
+ /**
+ * 检测到啸叫通知
+ *
+ * @param howlIsMuted true 表示已关闭麦克风或被静音 false 表示检测到啸叫,但是功能开关设置为false的情况,
+ * 没有自动关闭麦克风或自动静音
+ */
+ @Override
+ public void onEvtHowlAutoMute(boolean howlIsMuted) {
+ LogUtil.zzz(TAG, "onEvtHowlAutoMute howlIsMuted ", howlIsMuted);
+ MeetingMgrV2.getInstance().onEvtHowlAutoMute(howlIsMuted);
+ }
+
+ /**
+ * 实时带宽信息变化通知
+ *
+ * @param param 实时带宽信息
+ */
+ @Override
+ public void onEvtRealBandwidthChanged(TsdkOnEvtRealTimeBandWidth.Param param) {
+ LogUtil.zzz(TAG, "onEvtRealBandwidthChanged param ", param);
+ // 设置会议总带宽
+ MeetingController.getInstance().setBandWidth(param.getRealTimeBandwidth());
+ MeetingMgrV2.getInstance().handleBandChanged();
+ }
+
+ /**
+ * 启停摄像头或辅流通知
+ *
+ * @param param 主辅流信息,通知启停摄像头或辅流
+ */
+ @Override
+ public void onEvtVideoSwitchNotify(TsdkOnEvtVideoSwitchNotify.Param param) {
+ LogUtil.zzz(TAG, "onEvtVideoSwitchNotify msgType ", param.getMsgType());
+ }
+
+ /**
+ * 设备状态通知
+ *
+ * @param param 设备状态通知信息
+ */
+ @Override
+ public void onEvtDeviceStateNotify(TsdkOnEvtDeviceStateNotify.Param param) {
+ LogUtil.zzz(TAG, "onEvtDeviceStateNotify deviceState ", param.getDeviceState());
+ // 设置呼叫Id
+ MeetingController.getInstance().setCallId(param.getCallId());
+ // 设置设备静音状态
+ MeetingController.getInstance().needUpdateMuteState = true;
+ MeetingMgrV2.getInstance().onEvtDeviceStateNotify(param);
+ MeetingController.getInstance().setScChange(true);
+ BaseAppContext.getInstance().stopService(new Intent(BaseAppContext.getInstance(), MinimizeService.class));
+ }
+
+ /**
+ * 与会者举手或者取消举手的通知
+ *
+ * @param attendee 与会者基础信息及状态
+ */
+ @Override
+ public void onEvtHandUpInd(TsdkNotifyHandUpAttendee attendee) {
+ LogUtil.zzz(TAG, "onEvtHandUpInd statusInfo ", attendee.getStatusInfo());
+ MeetingMgrV2.getInstance().onEvtHandUpInd(attendee);
+ }
+
+ /**
+ * 辅流令牌指示通知
+ *
+ * @param auxTokenOwnerInd 辅流令牌指示信息
+ */
+ @Override
+ public void onEvtUpdateAuxShareOwnerInd(TsdkAuxTokenOwnerInd auxTokenOwnerInd) {
+ LogUtil.zzz(TAG, "onEvtUpdateAuxShareOwnerInd statusInfo ", auxTokenOwnerInd.getStatusInfo());
+ }
+
+ /**
+ * 广播状态通知
+ *
+ * @param param
+ */
+ @Override
+ public void onEvtBroadcastInd(TsdkOnEvtBroadcastInd.Param param) {
+ LogUtil.zzz(TAG, "onEvtBroadcastInd isBroadcast ", param.getIsBroadcast());
+ MeetingMgrV2.getInstance().onEvtBroadcastInd(param);
+ }
+
+ /**
+ * AVC会议当前画面选看通知
+ *
+ * @param tsdkAttendee 与会者信息
+ */
+ @Override
+ public void onEvtAvcWatchingAttendeeInd(TsdkAttendee tsdkAttendee) {
+ LogUtil.zzz(TAG, "onEvtAvcWatchingAttendeeInd");
+ if (tsdkAttendee != null && tsdkAttendee.getBaseInfo() != null && !TextUtils.isEmpty(tsdkAttendee.getBaseInfo().getNumber())) {
+ Member member = ConfConvertUtil.convertAttendeeInfo(tsdkAttendee);
+ MeetingController.getInstance().setIsPassiveWatchingMember(member);
+ }
+ }
+
+ /**
+ * 入会查询是否需要会议密码通知
+ *
+ * @param param 入会密码查询信息
+ */
+ @Override
+ public void onEvtCheckConfPwdExistedResult(TsdkOnEvtChcekConfpwdExistedResult.Param param) {
+ LogUtil.zzz(TAG, "onEvtQueryConfPwdResult", "param = " + param.toString());
+ MeetingMgrV2.getInstance().onEvtCheckConfPwdExistedResult(param);
+ }
+
+ /**
+ * 入会验证会议密码通知
+ *
+ * @param param 入会密码验证信息
+ */
+ @Override
+ public void onEvtVerifyConfPwdResult(TsdkOnEvtVerifyConfPwdResult.Param param) {
+ LogUtil.zzz(TAG, "onEvtVerifyConfPwdResult ", "param = " + param.toString());
+ MeetingMgrV2.getInstance().onEvtVerifyConfPwdResult(param);
+ }
+
+ /**
+ * 会议锁定事件通知
+ *
+ * @param param
+ */
+ @Override
+ public void onEvtLockConfInd(TsdkOnEvtLockConfInd.Param param) {
+ LogUtil.zzz(TAG, "onEvtLockConfInd isLock ", param.isLock);
+ MeetingController.getInstance().setMeetingIsLock(param.isLock == 1);
+ LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_EVT_MEETING_LOCK, param.isLock == 1);
+ //入会后不提示会议解锁信息
+ //会议解锁事件会存在推送两次情况
+
+ }
+
+ @Override
+ public void onEvtSubtitleCapabilityInd(TsdkOnEvtSubtitleCapabilityInd.Param param) {
+ LogUtil.zzz(TAG, "onEvtSubtitleCapabilityInd param ", param.isConfSupportSubtitle);
+ MeetingMgrV2.getInstance().handleEvtSubtitleCapabilityInd(param.isConfSupportSubtitle);
+ }
+
+ @Override
+ public void onEvtSubtitleContentInfo(TsdkOnEvtSubtitleContentInfo.Param param) {
+ LogUtil.zzz(TAG, "onEvtSubtitleContentInfo param ", param.subtitleContentInfo.toString());
+ MeetingMgrV2.getInstance().handleEvtSubtitleContentInfo(param.subtitleContentInfo);
+ }
+
+ /**
+ * 打印TsdkCallInfo信息
+ */
+ private void showCallLog(String mName, String s, TsdkCall call) {
+ if (call != null && call.getCallInfo() != null) {
+ TsdkCallInfo callInfo = call.getCallInfo();
+ StringBuilder sb = new StringBuilder();
+ sb.append(LogUtil.StringInterval(callInfo.getIsCaller() == 0 ? "被叫" : "主叫"));
+ sb.append(LogUtil.StringInterval(callInfo.getIsVideoCall() == 0 ? "音频" : "视频"));
+ sb.append(LogUtil.StringInterval(callInfo.getIsFocus() == 0 ? "点呼" : "会议"));
+ if (callInfo.getIsFocus() == 1) {
+ sb.append(LogUtil.StringInterval(callInfo.getIsSvcCall() == 1 ? "SVC" : "AVC"));
+ }
+ sb.append(LogUtil.StringInterval("PeerNumber=" + LogUtil.commonDisplay(callInfo.getPeerNumber())));
+ sb.append(LogUtil.StringInterval("PeerDisplayName=" + LogUtil.commonDisplay(callInfo.getPeerDisplayName())));
+ sb.append(LogUtil.StringInterval("CallId=" + LogUtil.commonDisplay(callInfo.getCallId() + "")));
+ LogUtil.zzz(mName, s, sb.toString());
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragment.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragment.java
new file mode 100644
index 0000000..1ef0c3a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragment.java
@@ -0,0 +1,215 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.FrameLayout;
+
+import com.hjq.toast.ToastUtils;
+import com.huawei.ecterminalsdk.base.TsdkWatchSvcAttendees;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.R2;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RequiresApi;
+import butterknife.BindView;
+
+/**
+ * @author Jelly
+ */
+public class AuxMeetingFragment extends LazyloadFragment implements AuxMeetingFragmentContract.AuxMeetingFragmentView {
+
+ public static final String EXTRA_CALL_INFO = "call_info";
+
+ @BindView(R2.id.aux_video)
+ FrameLayout auxVideo;
+ @BindView(R2.id.zoom_layout)
+ ZoomLayout zoomLayout;
+
+ private AuxMeetingFragmentPresenter presenter;
+ private MyTsdkCallInfo myTsdkCallInfo;
+ private Handler handler = new Handler(Looper.getMainLooper());
+ private static final String TAG = "AuxMeetingFragment";
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_ADD_LOCAL_VIEW};
+
+ private LocalBroadcastReceiver receiver = new LocalBroadcastReceiver() {
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+ switch (broadcastName) {
+ case BroadcastConstant.ACTION_ADD_LOCAL_VIEW:
+ LogUtil.zzz(TAG, "onReceive: ADD_LOCAL_VIEW");
+ handler.post(() -> presenter.setDataVideoContainer(getActivity(), null, auxVideo));
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ public static AuxMeetingFragment getInstance(MyTsdkCallInfo callInfo) {
+ AuxMeetingFragment fragment = new AuxMeetingFragment();
+ Bundle bundle = new Bundle();
+ bundle.putSerializable(EXTRA_CALL_INFO, callInfo);
+ fragment.setArguments(bundle);
+ return fragment;
+ }
+
+ @Override
+ protected int setContentView() {
+ return R.layout.fragment_aux_meeting;
+ }
+
+ @Override
+ protected void getData() {
+ Bundle bundle = getArguments();
+ if (bundle != null) {
+ myTsdkCallInfo = (MyTsdkCallInfo) bundle.getSerializable(EXTRA_CALL_INFO);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (zoomLayout != null) {
+ zoomLayout.resetView();
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(@NonNull Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (zoomLayout != null) {
+ zoomLayout.changeWindow(newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+ }
+
+ }
+
+ @Override
+ protected void initView() {
+ if (MeetingController.getInstance().isShowAuxZoomTips()) {
+ ToastUtils.show(getString(R.string.cloudLink_aux_zoom_tips));
+ MeetingController.getInstance().setShowAuxZoomTips(false);
+ }
+
+ }
+
+ @Override
+ protected void initListener() {
+
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ protected void lazyLoad() {
+ if (getActivity() != null && getActivity() instanceof SponsorMeetingActivity) {
+ ((SponsorMeetingActivity) getActivity()).setRemote(false);
+ }
+
+ if (presenter == null) {
+ presenter = new AuxMeetingFragmentPresenter((SponsorMeetingActivity) getActivity(), getContext());
+ Configuration configuration = this.getResources().getConfiguration();
+ int mOrientation = configuration.orientation;
+ presenter.setDataVideoContainer(getActivity(), getFloatWindow(), auxVideo);
+ presenter.setAutoRotation(auxVideo, false, mOrientation);
+ auxVideo.setOnClickListener(view -> {
+ try {
+ ((SponsorMeetingActivity) getActivity()).showLabel();
+ } catch (NullPointerException ignored) {
+
+ LogUtil.zzz("AuxMeetingFragment", "" + ignored);
+ }
+ });
+ } else {
+ Configuration configuration = this.getResources().getConfiguration();
+ int mOrientation = configuration.orientation;
+ presenter.setDataVideoContainer(getActivity(), getFloatWindow(), auxVideo);
+ presenter.setAutoRotation(auxVideo, false, mOrientation);
+ auxVideo.setOnClickListener(view -> {
+ try {
+ ((SponsorMeetingActivity) getActivity()).showLabel();
+ } catch (NullPointerException ignored) {
+
+ }
+ });
+ }
+ if (myTsdkCallInfo.isSVC()) {
+ watchSvcAttendees();
+ }
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ if (isVisibleToUser) {
+ Log.d(TAG, "AuxMeetingFragment setUserVisibleHint: 注册广播");
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ } else {
+ Log.d(TAG, "AuxMeetingFragment setUserVisibleHint: 反注册广播");
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ }
+ }
+
+ private void watchSvcAttendees() {
+ LogUtil.zzz("AuxMeetingFragment 辅流选看最小分辨率");
+ int userId = MeetingMgrV2.getInstance().getWatchSingleUserId(null);
+ if (userId == 0) {
+ LogUtil.zzz("AuxMeetingFragment 辅流选看最小分辨率失败 number = " + userId);
+ return;
+ }
+ TsdkWatchSvcAttendees watchSvcAttendees = new TsdkWatchSvcAttendees();
+ MeetingController.getInstance().setSvcWatchStatus(SvcWatchStatus.AUX_DATA);
+ int[] videoSize = MeetingController.getInstance().getVideoSize(1);
+ watchSvcAttendees.setWidth(videoSize[0]);
+ watchSvcAttendees.setHeight(videoSize[1]);
+ watchSvcAttendees.setLableId(MeetingController.getInstance().getSSrc(myTsdkCallInfo));
+ int result = MeetingMgrV2.getInstance().watchAuxSingleSvcAttendee(watchSvcAttendees, userId);
+ if (0 != result) {
+ LogUtil.d(TAG, "AuxMeetingFragment watchSvcAttendee fail result : " + result);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ }
+
+ @Override
+ public void showNetworkError() {
+
+ }
+
+ @Override
+ public void showLoading() {
+
+ }
+
+ @Override
+ public void showLoading(String tip) {
+
+ }
+
+ @Override
+ public void hideLoading() {
+
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private FrameLayout getFloatWindow() {
+// return ((SponsorMeetingActivity) Objects.requireNonNull(getActivity())).getFloatWindow();
+ return ((SponsorMeetingActivity) requireActivity()).getFloatWindow();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragmentContract.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragmentContract.java
new file mode 100644
index 0000000..3269278
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragmentContract.java
@@ -0,0 +1,17 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+
+public interface AuxMeetingFragmentContract {
+ interface AuxMeetingFragmentView extends BaseContract.BaseView{
+
+ }
+ interface AuxMeetingFragmentPresenter extends BaseContract.BasePresenter {
+
+ void setDataVideoContainer(Context context, ViewGroup floatWindow, ViewGroup auxVideo);
+
+ void setAutoRotation(Object object, boolean b, int mOrientation);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragmentPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragmentPresenter.java
new file mode 100644
index 0000000..e0df2e6
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/AuxMeetingFragmentPresenter.java
@@ -0,0 +1,70 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+
+import android.content.Context;
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+
+
+public class AuxMeetingFragmentPresenter extends BaseConfPresenter implements AuxMeetingFragmentContract.AuxMeetingFragmentPresenter {
+ AuxMeetingFragmentContract.AuxMeetingFragmentView mView;
+
+ public AuxMeetingFragmentPresenter(BaseConfContract.BaseConfView view, Context context) {
+ super(view, context);
+ }
+
+ @Override
+ public void detachView() {
+ super.detachView();
+ }
+
+ @Override
+ public void setDataVideoContainer(Context context, ViewGroup floatWindow, ViewGroup auxVideo) {
+ if (floatWindow != null) {
+ addCameraSurfaceView(floatWindow, getLocalVideoView());
+ }
+
+ if (auxVideo != null) {
+ addSurfaceView(auxVideo, getAuxDataVideoView());
+ }
+ }
+
+ @Override
+ public void setAutoRotation(Object object, boolean b, int mOrientation) {
+ VideoMgr.getInstance().setAutoRotation(object, b, mOrientation);
+ }
+
+ public SurfaceView getLocalVideoView() {
+ return VideoMgr.getInstance().getLocalVideoView();
+ }
+
+ public SurfaceView getAuxDataVideoView() {
+ return VideoMgr.getInstance().getAuxDataView();
+ }
+
+ private void addCameraSurfaceView(ViewGroup container, SurfaceView child) {
+ if (child == null) {
+ return;
+ }
+ if (child.getParent() != null) {
+ return;
+ }
+ container.addView(child);
+ }
+
+ private void addSurfaceView(ViewGroup container, SurfaceView child) {
+ if (child == null) {
+ return;
+ }
+ if (child.getParent() != null) {
+ if (container == child.getParent()) {
+ return;
+ }
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ vGroup.removeAllViews();
+ }
+ container.addView(child);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseConfContract.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseConfContract.java
new file mode 100644
index 0000000..6de00a3
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseConfContract.java
@@ -0,0 +1,102 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+
+import android.widget.LinearLayout;
+
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tenlionsoft.baselib.core.beans.Member;
+
+import java.util.List;
+
+/**
+ * 语音点对点呼叫和语音会议
+ */
+public interface BaseConfContract {
+
+ int LAYOUT_POINT_VOICE = 1;
+ int LAYOUT_POINT_VIDEO = 2;
+ int LAYOUT_CONF_VOICE = 3;
+ int LAYOUT_CONF_VIDEO = 4;
+
+ interface BaseConfView extends BaseContract.BaseView {
+
+ void showCustomToast(int resID);
+
+ void showCustomToast(String str);
+
+ void leaveConfSuccess();
+
+ void updateView(int what, boolean isShow, Object obj);
+
+ void refreshMemberList(List memberList);
+
+ void confEnd();
+
+ void toBackStartService();
+
+ void showStreamDialog(boolean isAudio);
+
+ void watchText(String allText);
+
+
+ /**
+ * 扬声器和听筒切换 视图界面更新
+ *
+ * @param audioRoute DEFAULT(0) 听筒
+ * LOUDSPEAKER(1) 扬声器
+ * BLUETOOTH(2)
+ * EARPIECE(3)
+ * HEADSET(4)
+ */
+ void updateTsDkMobileAudioRoute(TsdkMobileAuidoRoute audioRoute);
+
+ /**
+ * 延长会议
+ */
+ void postponeConf();
+
+ /**
+ * 申请主席回调
+ * @param type
+ * @param name
+ * @param result
+ */
+ void handleChairmanResult(Constant.ParticipantEvent type, String name, int result);
+
+ void auxFailed(int code);
+
+ /**
+ * 更新控件可编辑状态
+ */
+ void updateViewEnable();
+ }
+
+ interface InvitedPresenter extends BaseContract.BasePresenter {
+ /**
+ * 退出会议
+ */
+ void leaveConf();
+
+ /**
+ * 是否为主席
+ *
+ * @return
+ */
+ boolean isChairMan();
+
+ void endConf();
+
+ boolean muteSelf();
+
+ void receivedMobileCall(boolean isConf, boolean isInit);
+
+ void endMobileCall(boolean isConf);
+
+ TsdkMobileAuidoRoute switchAudioRoute();
+
+ void muteConf(boolean isMute);
+
+ void showDialogKeyboard(LinearLayout layout);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseConfPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseConfPresenter.java
new file mode 100644
index 0000000..7341f63
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseConfPresenter.java
@@ -0,0 +1,669 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.allen.library.SuperTextView;
+import com.google.android.material.bottomsheet.BottomSheetDialog;
+import com.huawei.ecterminalsdk.base.TsdkConfOperationResult;
+import com.huawei.ecterminalsdk.base.TsdkConfRole;
+import com.huawei.ecterminalsdk.base.TsdkMobileAuidoRoute;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.ControlOperationsResults;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.serivce.ServiceNotifyV2;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import androidx.localbroadcastmanager.content.LocalBroadcastManager;
+
+import static com.tengshisoft.chatmodule.hwclud.utils.Constant.CALL_UPDATE_VOICE;
+import static com.tengshisoft.chatmodule.hwclud.utils.DeviceManager.isSilentState;
+
+/**
+ * 会议控制的BaseConfPresenter
+ */
+public abstract class BaseConfPresenter implements BaseConfContract.InvitedPresenter {
+
+ public BaseConfContract.BaseConfView mView;
+
+ protected Context context;
+
+ private static final String TAG = "BaseConfPresenter";
+ private BottomSheetDialog bsDialog;
+
+ private String confID;
+
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_UN_MUTE_CONF_RESULT,
+ BroadcastConstant.ACTION_MUTE_CONF_RESULT, BroadcastConstant.ACTION_CONF_STATE_UPDATE,
+ BroadcastConstant.ACTION_CONF_CALL_CONNECTED, BroadcastConstant.ACTION_CALL_CONNECTED,
+ BroadcastConstant.ACTION_CALL_END, BroadcastConstant.ACTION_POSTPONE_CONF_RESULT,
+ BroadcastConstant.ACTION_CALL_GOING,/* BroadcastConstant.ACTION_ADD_LOCAL_VIEW,*/
+ BroadcastConstant.ACTION_CONF_AUX_DATA_SEND, BroadcastConstant.ACTION_CONF_AUX_DATA_FAILED,
+ BroadcastConstant.ACTION_CONF_AUDIT_DIR,
+ BroadcastConstant.ACTION_VIDEO_TO_AUDIO, BroadcastConstant.ACTION_CALL_UPGRADE_ACTION,
+ BroadcastConstant.ACTION_OPEN_VIDEO, BroadcastConstant.ACTION_REFUSE_OPEN_VIDEO,
+ BroadcastConstant.ACTION_WATCH_ATTENDEE_CONF_RESULT, BroadcastConstant.ACTION_EVT_MEETING_LOCK,
+ BroadcastConstant.ACTION_RELEASE_CHAIRMAN_RESULT, BroadcastConstant.ACTION_CONF_OPTION_SUCCESS};
+
+ private LocalBroadcastReceiver receiver = new LocalBroadcastReceiver() {
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+
+ LogUtil.d(LogUtil.CLOUNDLINK + "BaseConfPresenter", "broadcastName:" + broadcastName);
+ switch (broadcastName) {
+ // 取消静音会议结果
+ case BroadcastConstant.ACTION_UN_MUTE_CONF_RESULT:
+ if ((int) obj != 0) {
+ mView.showCustomToast(R.string.cloudLink_meeting_unMuteConfFail);
+ } else {
+ mView.showCustomToast(R.string.cloudLink_meeting_cancelAllConf);
+ // 刷新与会者界面
+ List memberList = MeetingMgrV2.getInstance().getCurrentConferenceMemberList();
+ if (memberList == null) {
+ break;
+ } else {
+ for (Member member : memberList) {
+ member.setMute(false);
+ }
+ LogUtil.zzz(TAG, "BroadcastConstant.ACTION_UN_MUTE_CONF_RESULT", memberList.size());
+ mView.refreshMemberList(memberList);
+ }
+ }
+ break;
+ // 静音会议结果
+ case BroadcastConstant.ACTION_MUTE_CONF_RESULT:
+ if ((int) obj != 0) {
+ mView.showCustomToast(R.string.cloudLink_meeting_muteConfFail);
+ } else {
+ mView.showCustomToast(R.string.cloudLink_meeting_muteAllConf);
+ // 刷新与会者界面
+ List memberList = MeetingMgrV2.getInstance().getCurrentConferenceMemberList();
+ if (memberList == null) {
+ break;
+ } else {
+ for (Member member : memberList) {
+ member.setMute(true);
+ }
+ LogUtil.zzz(TAG, "BroadcastConstant.ACTION_UN_MUTE_CONF_RESULT", memberList.size());
+ mView.refreshMemberList(memberList);
+ }
+ }
+ break;
+ /**
+ * 收到会议状态返回的监听
+ */
+ case BroadcastConstant.ACTION_CONF_STATE_UPDATE:
+ List memberList = MeetingMgrV2.getInstance().getCurrentConferenceMemberList();
+ LogUtil.zzz(TAG, "BroadcastConstant.ACTION_CONF_STATE_UPDATE", memberList.size());
+ mView.refreshMemberList(memberList);
+ break;
+ case BroadcastConstant.ACTION_CALL_END: // 点呼callend走这里
+ // -----------Yason 让加在这里 begin-----------
+ if (MeetingMgrV2.getInstance().getMemberList() != null) {
+ MeetingMgrV2.getInstance().getMemberList().clear();
+ }
+ mView.confEnd();
+ break;
+ // 选看结果
+ case BroadcastConstant.ACTION_WATCH_ATTENDEE_CONF_RESULT:
+ if ((int) obj != 0) {
+ // 三端对齐 均不提示选看失败
+ // mView.showCustomToast(R.string.cloudLink_meeting_chooseToSeeFail);
+ MeetingController.getInstance().setWatchingMember(null);
+ MeetingController.getInstance().setWatchMember(false);
+ BaseAppContext.getInstance().setMember(null);
+ } else {
+ LogUtil.zzz(TAG, "ACTION_WATCH_ATTENDEE_CONF_RESULT == 0");
+ }
+ break;
+ case BroadcastConstant.ACTION_CONF_CALL_CONNECTED:
+ mView.updateViewEnable();
+ mView.updateView(Constant.UPDATE_CONF_CONNECT, true, obj);
+ break;
+ case BroadcastConstant.ACTION_CONF_OPTION_SUCCESS:
+ mView.updateViewEnable();
+ break;
+ case BroadcastConstant.ACTION_CALL_CONNECTED:
+ mView.updateView(Constant.UPDATE_CALL_CONNECT, true, obj);
+ break;
+ // 接收callId
+ case BroadcastConstant.ACTION_CALL_GOING:
+ CallInfo callInfo = (CallInfo) obj;
+ if (!callInfo.isFocus()) {
+ if (callInfo.isVideoCall()) {
+ mView.updateView(Constant.CALL_VIDEO_GOING, true, callInfo);
+ } else {
+ mView.updateView(Constant.CALL_VOICE_GOING, true, callInfo);
+ }
+ }
+
+ break;
+ case BroadcastConstant.ACTION_CONF_AUX_DATA_SEND:
+ //VideoMgr.getInstance().clearCallVideo();
+ LogUtil.zzz(TAG, "开始共享...");
+ mView.toBackStartService();
+ break;
+ case BroadcastConstant.ACTION_CONF_AUX_DATA_FAILED:
+ LogUtil.zzz(TAG, "Aux Failed");
+ MeetingController.getInstance().setAux(false);
+ auxFail(obj);
+// TimerUtil.delay(2000,()->{
+// LocalBroadcast.getInstance().sendBroadcast(BroadcastConstant.ACTION_AUX_FAILED,obj);
+// });
+
+ break;
+ case BroadcastConstant.ACTION_CONF_AUDIT_DIR:
+ LogUtil.zzz("单向 直播");
+ mView.updateView(Constant.AUDIT_DIR, true, obj);
+ break;
+ case BroadcastConstant.ACTION_POSTPONE_CONF_RESULT:
+ if (null != obj) {
+ TsdkConfOperationResult result = (TsdkConfOperationResult) obj;
+ if (result.getReasonCode() != 0) {
+ Log.d(TAG, "onReceive: 延长会议失败result = " + result.getReasonCode());
+ mView.showCustomToast(R.string.cloudLink_meeting_postPoneFail);
+ MeetingController.getInstance().duration = 0;
+ break;
+ } else {
+ mView.showCustomToast(context.getResources().getString(R.string.cloudLink_meeting_postPoneSuccess) + " " + MeetingController.getInstance().duration + " " + context.getResources().getString(R.string.cloudLink_min));
+ MeetingController.getInstance().duration = 0;
+ }
+ }
+ break;
+ case BroadcastConstant.ACTION_VIDEO_TO_AUDIO:
+ if (null != obj) {
+ mView.updateView(Constant.ACTION_VIDEO_TO_AUDIO, false, obj);
+ }
+ break;
+ case BroadcastConstant.ACTION_CALL_UPGRADE_ACTION:
+ if (null != obj) {
+ mView.updateView(Constant.CALL_UPGRADE_ACTION, false, obj);
+ }
+ break;
+ case BroadcastConstant.ACTION_OPEN_VIDEO:
+ if (null != obj) {
+ mView.updateView(Constant.OPEN_VIDEO, false, obj);
+ }
+ break;
+ case BroadcastConstant.ACTION_REFUSE_OPEN_VIDEO:
+ if (null != obj) {
+ mView.updateView(Constant.REFUSE_OPEN_VIDEO, false, obj);
+ }
+ break;
+ //会议锁定
+ case BroadcastConstant.ACTION_EVT_MEETING_LOCK:
+ if (obj != null) {
+ boolean isLock = (boolean) obj;
+ if (isLock) {
+ mView.showCustomToast(R.string.cloudLink_meeting_lock_success);
+ MeetingController.getInstance().setSilentUnlock(false);
+ } else {
+ //释放主持人不提示锁定事件
+ if (!MeetingController.getInstance().isSilentUnlock()) {
+ mView.showCustomToast(R.string.cloudLink_meeting_unlocked);
+ } else {
+ MeetingController.getInstance().setSilentUnlock(false);
+ }
+ }
+
+ }
+ break;
+ //释放主持人
+ case BroadcastConstant.ACTION_RELEASE_CHAIRMAN_RESULT:
+ ControlOperationsResults.ChairmanResult releasechairmanResult = (ControlOperationsResults.ChairmanResult) obj;
+ if (releasechairmanResult.getResult() != 0) {
+ mView.handleChairmanResult(Constant.ParticipantEvent.RELEASE_CHAIRMAN_FAILED, releasechairmanResult.getName(), releasechairmanResult.getResult());
+ return;
+ }
+ mView.handleChairmanResult(Constant.ParticipantEvent.RELEASE_CHAIRMAN_SUCCESS, releasechairmanResult.getName(), releasechairmanResult.getResult());
+ MeetingController.getInstance().setChairman(false);
+
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ /**
+ * 共享失败类型
+ */
+ private void auxFail(Object obj) {
+ int errorType = (int) obj;
+ mView.auxFailed(errorType);
+
+ }
+
+ private Dialog dialog2;
+
+ public BaseConfPresenter(BaseConfContract.BaseConfView mView, Context context) {
+ this.mView = mView;
+ this.context = context;
+ }
+
+ public void registerBroadcast() {
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ }
+
+ @Override
+ public void leaveConf() {
+ int callID = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ CallMgrV2.getInstance().endCall(callID);
+ int result = MeetingMgrV2.getInstance().leaveConf();
+ // 清空上行数据
+ ServiceNotifyV2.getInstance().setVideoNetQuality(0);
+ ServiceNotifyV2.getInstance().setAudioNetQuality(0);
+ if (result == 0) {
+ MeetingController.getInstance().setChairman(false);
+ mView.leaveConfSuccess();
+ } else {
+ mView.leaveConfSuccess();//弱网情况下,离开会议,失败也会销毁界面。有问题。
+ }
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ }
+
+
+ @Override
+ public boolean isChairMan() {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+
+ if (null == self) {
+ return false;
+ }
+ return (self.getRole() == TsdkConfRole.TSDK_E_CONF_ROLE_CHAIRMAN);
+ }
+
+
+ @Override
+ public void endConf() {
+ // 结束会议刷新会议列表
+ Intent it = new Intent();
+ it.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ it.setAction(BroadcastConstant.ACTION_REFRESH_MEETINGS);
+ LocalBroadcastManager.getInstance(context).sendBroadcast(it);
+ int result = MeetingMgrV2.getInstance().endConf();
+ if (result == 0) {
+// if (MeetingController.getInstance().isHaveNet()) {
+// mView.showCustomToast(R.string.cloudLink_meeting_endConfSuccess);
+// }
+ mView.leaveConfSuccess();
+ } else {
+ mView.showCustomToast(R.string.cloudLink_parmerError);
+ return;
+ }
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ }
+
+ @Override
+ public boolean muteSelf() {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ LogUtil.zzz("静音失败 self = null");
+ return false;
+ }
+ LogUtil.zzz("是否静音" + !self.isMute());
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, !self.isMute());
+ if (result == 0) {
+ mView.updateView(CALL_UPDATE_VOICE, !self.isMute(), "");
+ }
+ return result == 0;
+ }
+
+ @Override
+ public TsdkMobileAuidoRoute switchAudioRoute() {
+ return CallMgrV2.getInstance().switchAudioRoute();
+ }
+
+ @Override
+ public void detachView() {
+ if (bsDialog != null) {
+ bsDialog.dismiss();
+ bsDialog = null;
+ }
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ }
+
+ public void showLandLeaveConfBottomSheetDialog() {
+ showBottomDialog();
+// showLeaveConfDialog();
+ }
+
+ EndConfDialog endConfDialog;
+
+ private void showLeaveConfDialog() {
+ LogUtil.zzz("是否是主席:" + MeetingController.getInstance().isChairman());
+ if (MeetingController.getInstance().isChairman()) {
+ AtomicBoolean isChecked = new AtomicBoolean(false);
+ endConfDialog = new EndConfDialog(context);
+ endConfDialog.setTitle(context.getString(R.string.cloudLink_meeting_leaveExit));
+ endConfDialog.setCheckBoxString(context.getString(R.string.cloudLink_meeting_leaveandendConf), (buttonView, isChecked1) -> {
+ if (isChecked1) {
+ isChecked.set(true);
+ } else {
+ isChecked.set(false);
+ }
+ });
+ endConfDialog.setNo(context.getString(R.string.cloudLink_cancel), () -> {
+ endConfDialog.dismiss();
+ endConfDialog = null;
+ });
+ endConfDialog.setYes(context.getString(R.string.cloudLink_sure), () -> {
+ if (isChecked.get()) {
+ endConfDialog.dismiss();
+ endConfDialog = null;
+ endConf();
+ } else {
+ endConfDialog.dismiss();
+ endConfDialog = null;
+ leaveConf();
+ }
+ });
+ if (!endConfDialog.isShowing()) {
+ endConfDialog.show();
+ }
+ } else {
+ endConfDialog = new EndConfDialog(context);
+ endConfDialog.setTitle(context.getString(R.string.cloudLink_meeting_leaveExit));
+ endConfDialog.setNo(context.getString(R.string.cloudLink_cancel), () -> endConfDialog.dismiss());
+ endConfDialog.setYes(context.getString(R.string.cloudLink_sure), () -> {
+ endConfDialog.dismiss();
+ endConfDialog = null;
+ leaveConf();
+ });
+ if (!endConfDialog.isShowing()) {
+ endConfDialog.show();
+ }
+ }
+
+ }
+
+ private void showBottomDialog() {
+ //1、使用Dialog、设置style
+ if (dialog2 != null && dialog2.isShowing()) {
+ dialog2.dismiss();
+ dialog2 = null;
+ }
+ //1、使用Dialog、设置style
+ dialog2 = new Dialog(context, R.style.CloudDialog);
+ //2、设置布局
+ View view = View.inflate(context, R.layout.item_conf_leave_bottom_layout, null);
+ dialog2.setContentView(view);
+
+ Window window = dialog2.getWindow();
+ //设置弹出位置
+ window.setGravity(Gravity.BOTTOM);
+ //设置弹出动画
+ window.setWindowAnimations(R.style.main_menu_animStyle);
+ //设置对话框大小
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ dialog2.show();
+ SuperTextView leaveConf = dialog2.findViewById(R.id.tv_leave_conf);
+ leaveConf.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ dialog2.dismiss();
+ leaveConf();
+ }
+ });
+ SuperTextView endConf = dialog2.findViewById(R.id.tv_end_conf);
+ if (isChairMan()) {
+ leaveConf.getCenterBottomTextView().setVisibility(View.VISIBLE);
+ endConf.setOnSuperTextViewClickListener(superTextView -> {
+ dialog2.dismiss();
+ endConf();
+ });
+ } else {
+ leaveConf.getCenterTextView().setTextColor(context.getResources().getColor(R.color.red_button));
+ leaveConf.getCenterBottomTextView().setVisibility(View.GONE);
+ endConf.setVisibility(View.GONE);
+ }
+
+ dialog2.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ dialog2.dismiss();
+ }
+ });
+ if (isChairMan()) {
+ ((TextView) dialog2.findViewById(R.id.tv_name)).setText(context.getString(R.string.cloudLink_meeting_endOrExit));
+ endConf.setOnSuperTextViewClickListener(superTextView -> {
+ dialog2.dismiss();
+ endConf();
+ });
+ } else {
+ endConf.setVisibility(View.GONE);
+ ((TextView) dialog2.findViewById(R.id.tv_name)).setText(context.getString(R.string.cloudLink_meeting_leaveExit));
+ }
+ }
+
+ public void setConfID(String confID) {
+ this.confID = confID;
+ }
+
+ public String getConfID() {
+ return confID;
+ }
+
+ // private DialPlateControl mDialPlateControl;//虚拟键盘
+ private boolean isKeyboardShow = false;
+
+ /**
+ * 二次拨号盘
+ */
+ @Override
+ public void showDialogKeyboard(LinearLayout layout) {
+ SecondDialPlateControl mPlateControl = new SecondDialPlateControl(layout, MeetingMgrV2.getInstance().getCurrentConferenceCallID());
+ if (!isKeyboardShow) {
+ mPlateControl.showDialPlate();
+ isKeyboardShow = true;
+
+ } else {
+ mPlateControl.hideDialPlate();
+ isKeyboardShow = false;
+ }
+ }
+
+ Dialog dialdialog;
+
+ public void showDialogKeyboard() {
+ if (dialdialog != null && dialdialog.isShowing()) {
+ return;
+ }
+ dialdialog = new Dialog(context, R.style.CloudDialog);
+ //2、设置布局
+ LinearLayout view = (LinearLayout) View.inflate(context, R.layout.item_conf_keyboard, null);
+ dialdialog.setContentView(view);
+
+ Window window = dialdialog.getWindow();
+ //设置弹出位置
+ window.setGravity(Gravity.BOTTOM);
+
+ //设置弹出动画
+ window.setWindowAnimations(R.style.main_menu_animStyle);
+ //设置对话框大小
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ try {
+ dialdialog.show();
+ } catch (Exception e) {
+ LogUtil.zzz("异常了");
+ }
+ SecondDialPlateControl mPlateControl = new SecondDialPlateControl(view, MeetingMgrV2.getInstance().getCurrentConferenceCallID());
+ mPlateControl.showDialPlate();
+ }
+
+ public void dismissDialogKeyboard() {
+ if (dialdialog != null && dialdialog.isShowing()) {
+ dialdialog.dismiss();
+ }
+ }
+
+ /**
+ * 全体静音
+ *
+ * @param isMute
+ */
+ @Override
+ public void muteConf(boolean isMute) {
+ int result = MeetingMgrV2.getInstance().muteConf(isMute);
+ if (result != 0) {
+ if (isMute) {
+ mView.showCustomToast(R.string.cloudLink_meeting_muteAllConfFail);
+ } else {
+ mView.showCustomToast(R.string.cloudLink_meeting_unMuteConfFail);
+ }
+ } /*else {
+ if(isMute){
+ mView.showCustomToast(R.string.mute_all_conf);
+ }else{
+ mView.showCustomToast(R.string.cancel_all_conf);
+ }
+ }*/
+ }
+
+ /**
+ * 点对点静音
+ */
+ public void muteCall(int mCallID) {
+ CallFunc callFunc = CallFunc.getInstance();
+ boolean currentMuteStatus = callFunc.isMuteStatus();
+ if (CallMgrV2.getInstance().muteMic(mCallID, !currentMuteStatus)) {
+ callFunc.setMuteStatus(!currentMuteStatus);
+ mView.updateView(CALL_UPDATE_VOICE, !currentMuteStatus, "");
+ }
+ }
+
+ public void postponeConf() {
+ mView.postponeConf();
+ }
+
+ public void meetingLock() {
+ int result = MeetingMgrV2.getInstance().setMeetingLock(!MeetingController.getInstance().isMeetingIsLock());
+ if (result != 0) {
+ mView.showCustomToast(MeetingController.getInstance().isMeetingIsLock() ?
+ R.string.cloudLink_meeting_unlock_failed : R.string.cloudLink_meeting_lock_failed);
+
+ }
+ }
+
+ @Override
+ public void receivedMobileCall(boolean isConf, boolean isInit) {
+ LogUtil.zzz("receivedMobileCall");
+ if (isConf) {
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(false);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ if (isInit) {
+ isSilentState = !EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + "_AUDIO");
+ } else {
+ isSilentState = self.isMute();
+ }
+ if (!isSilentState) {
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, !isSilentState);
+ if (result == 0) {
+ MeetingController.getInstance().isVoiceOpen = !isSilentState;
+ mView.updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ }
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ mView.updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ } else {
+ TsdkCall sdkCall = CallMgrV2.getInstance().getTSdkCallByCallMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(false);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ CallFunc callFunc = CallFunc.getInstance();
+ if (isInit) {
+ isSilentState = false;
+ } else {
+ isSilentState = callFunc.isMuteStatus();
+ }
+ if (!isSilentState) {
+ CallMgrV2.getInstance().muteMic(CallMgrV2.getInstance().getCallId(), !isSilentState);
+ callFunc.setMuteStatus(!isSilentState);
+ MeetingController.getInstance().isVoiceOpen = !isSilentState;
+ mView.updateView(Constant.RECEIVED_MOBILE_CALL, !isSilentState, "");
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ mView.updateView(Constant.RECEIVED_MOBILE_CALL, isSilentState, "");
+ }
+ }
+ }
+
+ @Override
+ public void endMobileCall(boolean isConf) {
+ LogUtil.zzz("endMobileCall");
+ if (isConf) {
+ TsdkCall sdkCall = MeetingMgrV2.getInstance().getTSdkCallByMeetingMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(true);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ if (!isSilentState) {
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, isSilentState);
+ if (result == 0) {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ mView.updateView(Constant.END_MOBILE_CALL, isSilentState, "");
+ }
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ mView.updateView(Constant.END_MOBILE_CALL, isSilentState, "");
+ }
+ } else {
+ TsdkCall sdkCall = CallMgrV2.getInstance().getTSdkCallByCallMgr();
+ if (sdkCall != null) {
+ sdkCall.muteSpeaker(true);
+ } else {
+ LogUtil.zzz("sdk call is null");
+ }
+ CallFunc callFunc = CallFunc.getInstance();
+ if (!isSilentState) {
+ CallMgrV2.getInstance().muteMic(CallMgrV2.getInstance().getCallId(), isSilentState);
+ callFunc.setMuteStatus(isSilentState);
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ mView.updateView(Constant.END_MOBILE_CALL, isSilentState, "");
+ } else {
+ MeetingController.getInstance().isVoiceOpen = isSilentState;
+ mView.updateView(Constant.END_MOBILE_CALL, isSilentState, "");
+ }
+ }
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseContract.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseContract.java
new file mode 100644
index 0000000..40ccc3c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseContract.java
@@ -0,0 +1,39 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+
+/**
+ * BaseContract
+ */
+public interface BaseContract {
+
+ interface BasePresenter {
+
+ /**
+ * View解绑
+ */
+ void detachView();
+ }
+
+ interface BaseView {
+
+ /**
+ * 网络异常
+ */
+ void showNetworkError();
+
+
+ /**
+ * 显示加载框
+ */
+ void showLoading();
+
+ void showLoading(String tip);
+
+ /**
+ * 隐藏加载框
+ */
+ void hideLoading();
+ }
+
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseDialPlateControl.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseDialPlateControl.java
new file mode 100644
index 0000000..52b0f2f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseDialPlateControl.java
@@ -0,0 +1,79 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.tengshisoft.chatmodule.R;
+
+
+/**
+ * This abstract class is about base dial plate control.
+ */
+public abstract class BaseDialPlateControl implements View.OnClickListener, View.OnLongClickListener {
+ protected static final String[] CODE_ARRAY = {"0", "1",
+ "2", "3", "4", "5", "6", "7", "8", "9", "*", "#"};
+
+ protected static final int[] NUM_ID_ARRAY;
+
+ protected View dialPlateView;
+ protected EditText mNumInputEt;
+ protected ImageView iv_pwd;
+ private boolean isDisplayPasswordFlag = false;
+
+ static {
+ NUM_ID_ARRAY = new int[12];
+ NUM_ID_ARRAY[0] = R.id.call_zero;
+ NUM_ID_ARRAY[1] = R.id.callOne;
+ NUM_ID_ARRAY[2] = R.id.callTwo;
+ NUM_ID_ARRAY[3] = R.id.callThree;
+ NUM_ID_ARRAY[4] = R.id.callFour;
+ NUM_ID_ARRAY[5] = R.id.callFive;
+ NUM_ID_ARRAY[6] = R.id.callSix;
+ NUM_ID_ARRAY[7] = R.id.callSeven;
+ NUM_ID_ARRAY[8] = R.id.callEight;
+ NUM_ID_ARRAY[9] = R.id.callNine;
+ NUM_ID_ARRAY[10] = R.id.callX;
+ NUM_ID_ARRAY[11] = R.id.callJ;
+ }
+
+ public BaseDialPlateControl(View plate) {
+ dialPlateView = plate;
+ mNumInputEt = plate.findViewById(R.id.callNumber);
+ TextView[] layouts = new TextView[NUM_ID_ARRAY.length];
+ for (int i = 0; i < NUM_ID_ARRAY.length; i++) {
+ layouts[i] = plate.findViewById(NUM_ID_ARRAY[i]);
+ layouts[i].setOnClickListener(this);
+ layouts[i].setOnLongClickListener(this);
+ layouts[i].setTag(i);
+ }
+ }
+
+
+
+ public void showDialPlate() {
+ dialPlateView.setVisibility(View.VISIBLE);
+ }
+
+ public void hideDialPlate() {
+ mNumInputEt.setText("");
+ mNumInputEt.clearFocus();
+ dialPlateView.setVisibility(View.GONE);
+ }
+
+ @Override
+ public void onClick(View v) {
+ handleOnClick(v);
+ }
+
+ protected abstract void handleOnClick(View v);
+
+ @Override
+ public boolean onLongClick(View v) {
+ handleOnLongClick(v);
+ return true;
+ }
+
+ protected abstract void handleOnLongClick(View v);
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseMvpActivityV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseMvpActivityV2.java
new file mode 100644
index 0000000..476a5c7
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BaseMvpActivityV2.java
@@ -0,0 +1,650 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+import com.hjq.toast.ToastUtils;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.activity.ChatActivity;
+import com.tengshisoft.chatmodule.activity.InvitedPointCallActivity;
+import com.tengshisoft.chatmodule.beans.CallInfo;
+import com.tengshisoft.chatmodule.beans.IntentConstant;
+import com.tengshisoft.chatmodule.beans.TSDKErrorConstant;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tengshisoft.chatmodule.hwclud.utils.ActivityUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.AppUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LocContext;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.NotifyUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.core.widget.views.LoadingDialog;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatDelegate;
+import androidx.appcompat.widget.Toolbar;
+import butterknife.ButterKnife;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+
+/**
+ * @Time: 2021/5/27
+ * @Author: isoftstone
+ * @Description: Activity公共基础类
+ */
+public abstract class BaseMvpActivityV2 extends BasePermissionActivityV2 implements IView {
+ public static String TAG = BaseMvpActivityV2.class.getSimpleName();
+ protected P mPresenter;
+ /**
+ * 加载框
+ */
+ private LoadingDialog dialog;
+ /**
+ * 底部拨号Dialog
+ */
+ Dialog mDialDialog;
+
+ private CloudLinkDialog tipDialog;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ LogUtil.zzz(TAG, this.getClass().getSimpleName(), "onCreate");
+ initIntent();
+ initPresenter();
+ setContentView(getLayoutId());
+ ButterKnife.bind(this);
+ prepareData(savedInstanceState);
+ disableAutoFill();
+ initView();
+ initData();
+ }
+
+
+ /**
+ * 初始化数据
+ */
+ protected void prepareData(Bundle savedInstanceState) {
+ // 页面数据准备
+ }
+
+
+ private void initPresenter() {
+ mPresenter = createPresenter();
+ // 完成Presenter和view的绑定
+ if (mPresenter != null) {
+ mPresenter.attachView(this, this);
+ }
+ }
+
+ @Override
+ public void showLoading() {
+ // 这里实现自己的加载弹框
+ showLoading(getString(R.string.cloudLink_login_loadinig));
+ }
+
+ @Override
+ public void dismissLoading() {
+ // 取消弹框
+ hideLoading();
+ }
+
+ @Override
+ public void onFail(Throwable ex, String code, String msg) {
+ // 基础的网络请求失败处理
+ hideLoading();
+ }
+
+ @Override
+ public void onNetError() {
+ // 网络错误处理
+ hideLoading();
+ }
+
+ /**
+ * 获取当前activity的id
+ *
+ * @return 当前xml的布局res ID
+ */
+ protected abstract int getLayoutId();
+
+ /**
+ * Intent数据初始化,页面相关设置
+ */
+ protected abstract void initIntent();
+
+
+ /**
+ * 初始化view控件
+ */
+ protected abstract void initView();
+
+ /**
+ * 页面加载数据
+ */
+ protected abstract void initData();
+
+ /**
+ * 创建Presenter
+ *
+ * @return Presenter
+ */
+ protected abstract P createPresenter();
+
+ /**
+ * 设置页面标题栏
+ *
+ * @param toolbar Toolbar标题栏
+ * @param homeAsUpEnabled 是否添加返回的图标
+ * @param title 标题
+ */
+ protected void initToolBar(Toolbar toolbar, boolean homeAsUpEnabled, String title) {
+ toolbar.setTitle(title);
+ toolbar.setNavigationIcon(R.drawable.ic_toolbar_back);
+ setSupportActionBar(toolbar);
+
+ // 给左上角图标的左边加上一个返回的图标
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(homeAsUpEnabled);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ onBackPressed();
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // Fragment 逐个出栈
+ int count = getSupportFragmentManager().getBackStackEntryCount();
+ if (count == 0) {
+ super.onBackPressed();
+ } else {
+ getSupportFragmentManager().popBackStack();
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ protected void onResume() {
+ checkHacCall();
+ super.onResume();
+ }
+
+ @Override
+ public AppCompatDelegate getDelegate() {
+ return super.getDelegate();
+ }
+
+
+ /**
+ * 更新View状态
+ *
+ * @param v 需要置灰的控件
+ */
+ protected void setGrayView(View v) {
+ if (v != null) {
+ v.setEnabled(MeetingController.getInstance().isHaveNet());
+ if (MeetingController.getInstance().isHaveNet()) {
+ v.setBackgroundResource(R.drawable.shape_button);
+ } else {
+ v.setBackgroundResource(R.drawable.shape_button_gray);
+ }
+ }
+ }
+
+ /**
+ * @param tip 提示语
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void logoutByRes(int tip) {
+ exitAppToClear(true);
+ if (tipDialog != null && tipDialog.isShowing()) {
+ return;
+ }
+ tipDialog = new CloudLinkDialog(this);
+ tipDialog.setStr_message(getString(tip), null);
+ tipDialog.setOnlyYes(true);
+ tipDialog.setCancelable(false);
+ tipDialog.setYes(getString(R.string.cloudLink_sure), null, () -> {
+ tipDialog.dismiss();
+ exitApp();
+ });
+ tipDialog.show();
+ }
+
+ /**
+ * 但是目前的场景,及时自动登录也会失败。
+ *
+ * @param isLogout true表示注销登录或断网 false表示token过期之类
+ */
+ @SuppressWarnings("SameParameterValue")
+ protected void exitAppToClear(boolean isLogout) {
+ EncryptedSPTool.putBoolean(Constant.IS_LOGOUT, isLogout);
+ EncryptedSPTool.remove(Constant.IS_VMR);
+ EncryptedSPTool.remove(Constant.VMR_ACCESSNUMBER);
+ EncryptedSPTool.remove(Constant.VMR_CONF_ID);
+ EncryptedSPTool.remove(Constant.IS_VMR_2_ID);
+ EncryptedSPTool.remove(Constant.MEETING_CHAIRMAN);
+ EncryptedSPTool.remove(Constant.FEEDBACK_UPDATE_3);
+ }
+
+ public void hideLoading() {
+ runOnUiThread(() -> {
+ if (dialog != null) {
+ dialog.disDialog();
+ }
+ });
+ }
+
+ @Override
+ public void showLoading(String loading) {
+ if (dialog != null) {
+ dialog.disDialog();
+ }
+ dialog = new LoadingDialog(this).setLoadingText(loading);
+ dialog.show();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ protected void exitApp() {
+// // 规避LoginActivity多次启动
+// ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
+// List tasks = activityManager.getAppTasks();
+// if (ListTools.empty(tasks)) {
+// return;
+// }
+// String topActivityName = tasks.get(0).getTaskInfo().topActivity.getClassName();
+// App.isLogout = 1;
+// if (topActivityName.equals(LoginActivityV2.class.getName())) {
+// return;
+// }
+// hideLoading();
+// Intent intent1 = new Intent(this, LoginActivityV2.class);
+// intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+// startActivity(intent1);
+ }
+
+
+ /**
+ * 显示底部拨号键盘Dialog
+ */
+ public void showDialogKeyboard() {
+// if (mDialDialog != null && mDialDialog.isShowing()) {
+// return;
+// }
+// mDialDialog = new Dialog(this, R.style.CloudDialog);
+// //2、设置布局
+// LinearLayout view = (LinearLayout) View.inflate(this, R.layout.item_conf_keyboard, null);
+// mDialDialog.setContentView(view);
+//
+// Window window = mDialDialog.getWindow();
+// //设置弹出位置
+// window.setGravity(Gravity.BOTTOM);
+//
+// //设置弹出动画
+// window.setWindowAnimations(R.style.main_menu_animStyle);
+// //设置对话框大小
+// window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+// mDialDialog.show();
+//
+// LogUtil.zzz(TAG, "showDialogKeyboard()");
+// SecondDialPlateControl mPlateControl = new SecondDialPlateControl(view,
+// MeetingMgrV2.getInstance().getCurrentConferenceCallID());
+// mPlateControl.showDialPlate();
+ }
+
+ /**
+ * 隐藏底部拨号键盘Dialog
+ */
+ public void dismissDialogKeyboard() {
+ if (mDialDialog != null && mDialDialog.isShowing()) {
+ mDialDialog.dismiss();
+ }
+ }
+
+ /**
+ * SDK上报当前网络状态
+ *
+ * @param isHaveNet 是否有网
+ */
+ protected void netWorkIsConnect(boolean isHaveNet) {
+ MeetingController.getInstance().setHaveNet(isHaveNet);
+ }
+
+ /**
+ * 会议结束广播处理
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void actionCallEnd(Object obj) {
+ dismissLoading();
+ if (obj instanceof CallInfo) {
+ runOnUiThread(() -> {
+ boolean isNoStream = EncryptedSPTool.getBoolean(Constant.IS_NO_STREAM_DURATION);
+ if (isNoStream) {
+ dealNoDuration();
+ return;
+ }
+ CallInfo callEnd = (CallInfo) obj;
+ if (callEnd != null) {
+ LogUtil.zzz(TAG, "CallEnd()",
+ "reasonCode = " + callEnd.getReasonCode() + ";isFocus = " + callEnd.isFocus());
+ // 是否是会议
+ if (callEnd.isFocus()) {
+ if (isContactCall()) {
+ dealContactCallMeeting(callEnd.getReasonCode());
+ } else {
+ dealCallMeeting(callEnd.getReasonCode());
+ }
+ } else {
+ if (isContactCall()) {
+ dealContactCallNotMeeting(callEnd.getReasonCode());
+ } else {
+ dealCallNotMeeting(callEnd.getReasonCode());
+ }
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * 是否呼叫相关页面
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private boolean isContactCall() {
+// else if (AppUtil.isActivityTop(ContactDetailActivity.class, this)) {
+// LogUtil.zzz(TAG, "isContactCall", ContactDetailActivity.class.getSimpleName());
+// return true;
+// } else if (AppUtil.isActivityTop(RecentCallsActivity.class, this)) {
+// LogUtil.zzz(TAG, "isContactCall", RecentCallsActivity.class.getSimpleName());
+// return true;
+// }
+ if (AppUtil.isActivityTop(ChatActivity.class, this)) {
+ return true;
+ } else {
+ LogUtil.zzz(TAG, "isContactCall", this.getClass().getSimpleName());
+ return false;
+ }
+ }
+
+ /**
+ * 通话相关非会议结束处理
+ *
+ * @param reasonCode 返回码
+ */
+ private void dealContactCallNotMeeting(int reasonCode) {
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331801:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endCall));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331781:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callHangUp));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_userOffline));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_call_failed));
+ break;
+ default:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callEnd));
+ break;
+ }
+ }
+
+ /**
+ * 非会议相关通知响应
+ *
+ * @param reasonCode 响应码
+ */
+ public void dealCallNotMeeting(int reasonCode) {
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331648:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_confNotExit));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331781:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callHangUp));
+ break;
+ case TSDKErrorConstant.TSDK_NET_ERROR_CODE:
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331801:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endCall));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331817:
+ // 如果断网异常情况,不弹出当前网络异常,通话中断,请重新呼叫,
+ // 后续断网重连加上后,放开此限制
+ if (!MeetingMgrV2.getInstance().isDisconnection()) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_call_tls_error));
+ } else {
+ MeetingMgrV2.getInstance().setDisconnection(false);
+ }
+ break;
+ case TSDKErrorConstant.TSDK_E_CALL_ERR_50331770:
+ if (MeetingController.getInstance().isLogin()) {
+ // 匿名链接入会非登录状态不显示该toast
+ ToastUtils.show(getString(R.string.cloudLink_call_failed));
+ }
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331776:
+ if (EncryptedSPTool.getBoolean(Constant.JOIN_MEETING_NOLOGIN)) {
+ // 匿名链接入会
+ ToastUtils.show(getString(R.string.cloudLink_joinconf_err_749));
+ } else {
+ ToastUtils.show(getString(R.string.cloudLink_joinconf_error));
+ }
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331754:
+ if (EncryptedSPTool.getBoolean(Constant.JOIN_MEETING_NOLOGIN)) {
+ ToastUtils.show(getString(R.string.cloudLink_joinconf_err_749));
+ }
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331769:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callEnd));
+ break;
+ default:
+ if (MeetingController.getInstance().isNotHaveNet()) {
+ showNoNetDialog(getString(R.string.cloudLink_meeting_call_tls_error));
+ } else {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_callEnd));
+ }
+ break;
+ }
+ }
+
+ /**
+ * 通话相关会议相关通知响应
+ *
+ * @param reasonCode 响应码
+ */
+ public void dealContactCallMeeting(int reasonCode) {
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_userOffline));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_failed));
+ break;
+ default:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_endConfSuccess));
+ break;
+ }
+ }
+
+ /**
+ * 会议相关通知响应
+ *
+ * @param reasonCode 响应码
+ */
+ public void dealCallMeeting(int reasonCode) {
+ switch (reasonCode) {
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331750:
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331648:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_confNotExit));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331749:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_join_503));
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331776:
+ if (EncryptedSPTool.getBoolean(Constant.JOIN_MEETING_NOLOGIN)) {
+ // 匿名链接入会
+ ToastUtils.show(getString(R.string.cloudLink_joinconf_err_749));
+ } else {
+ ToastUtils.show(getString(R.string.cloudLink_joinconf_error));
+ }
+ break;
+ case TSDKErrorConstant.CALL_ERROR_CODE_50331817:
+ // 如果断网异常情况,不弹出 会议已结束提示
+ if (!MeetingMgrV2.getInstance().isDisconnection()) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_tls_error));
+ } else {
+ MeetingMgrV2.getInstance().setDisconnection(false);
+ }
+ break;
+
+ case TSDKErrorConstant.TSDK_E_CALL_ERR_50331770:
+ ToastUtils.show(getString(R.string.cloudLink_meeting_failed));
+ break;
+ default:
+ if (reasonCode != TSDKErrorConstant.TSDK_E_CALL_NET_ERROR) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_confEnd));
+ }
+ break;
+ }
+
+ }
+
+ @TargetApi(Build.VERSION_CODES.O)
+ private void disableAutoFill() {
+ getWindow().getDecorView().setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS);
+ }
+
+ @SuppressLint("CheckResult")
+ public void dealNoDuration() {
+ //TODO
+ Observable.timer(ConstantsV2.DELAY_MILLIS_600, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(aLong -> {
+// Intent it = new Intent(BaseMvpActivityV2.this, NoDurationActivityV2.class);
+// it.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+// startActivity(it);
+ EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, false);
+ });
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ protected void checkHacCall() {
+ if (MeetingController.getInstance().getTempCallInfo() != null) {
+ NotifyUtil.cancelNotify(this);
+ String mFilePath = Environment.getExternalStorageDirectory() + File.separator + CallFunc.RINGING_FILE;
+ CallMgrV2.getInstance().startPlayRingingTone(mFilePath);
+ if (MeetingController.getInstance().getTempCallInfo().isFocus()) {
+ if (MeetingController.getInstance().getTempCallInfo().isVideoCall()) {
+ Intent intent = new Intent(this, InvitedPointCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.putExtra(Constant.CALL_INFO, MeetingController.getInstance().getTempCallInfo());
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.CONF_VIDEO_COMING);
+ ActivityUtil.startActivity(LocContext.getContext(), intent);
+ } else {
+ Intent intent = new Intent(this, InvitedPointCallActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.addCategory(IntentConstant.DEFAULT_CATEGORY);
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.VOICE_CONF_COMING);
+ intent.putExtra(Constant.CALL_INFO, MeetingController.getInstance().getTempCallInfo());
+ ActivityUtil.startActivity(LocContext.getContext(), intent);
+ }
+ } else {
+ // 非会议情况
+ Intent intent = new Intent(this, InvitedPointCallActivity.class);
+ intent.putExtra(Constant.CALL_INFO, MeetingController.getInstance().getTempCallInfo());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ intent.addCategory(IntentConstant.DEFAULT_CATEGORY);
+ if (MeetingController.getInstance().getTempCallInfo().isVideoCall()) {
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.POINT_VIDEOED);
+ } else {
+ intent.putExtra(Constant.VOICE_JOIN_TYPE, Constant.POINT_CALLED);
+ }
+ ActivityUtil.startActivity(BaseAppContext.getInstance(), intent);
+ }
+ }
+ }
+
+ /**
+ * 会议中断网弹出无码流弹框提示
+ *
+ * @param info 提示文本
+ */
+ protected void showNoNetDialog(String info) {
+// TODO
+// TimerUtil.delay(ConstantsV2.DELAY_MILLIS_600, () -> {
+// Intent it = new Intent(this, NoDurationActivityV2.class);
+// it.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+// it.putExtra(Constant.SHOW_NO_NET_DIALOG_INFO, info);
+// startActivity(it);
+// EncryptedSPTool.putBoolean(Constant.IS_NO_STREAM_DURATION, false);
+// });
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+ View v = getCurrentFocus();
+ if (isShouldHideKeyboard(v, ev)) {
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(v.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ private boolean isShouldHideKeyboard(View v, MotionEvent event) {
+ if ((v instanceof EditText)) {
+ int[] l = {0, 0};
+ v.getLocationInWindow(l);
+ int left = l[0], top = l[1], bottom = top + v.getHeight(), right = left + v.getWidth();
+ return !(event.getX() > left && event.getX() < right && event.getY() > top && event.getY() < bottom);
+ }
+ return false;
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ LogUtil.zzz(TAG, this.getClass().getSimpleName(), "onDestroy");
+ // 将Presenter和view解绑
+ if (mPresenter != null) {
+ mPresenter.detachView();
+ mPresenter = null;
+ }
+ if (dialog != null) {
+ dialog.disDialog();
+ }
+ }
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BasePermissionActivityV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BasePermissionActivityV2.java
new file mode 100644
index 0000000..91186e8
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BasePermissionActivityV2.java
@@ -0,0 +1,150 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Settings;
+import android.view.Gravity;
+
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.api.IAgree;
+import com.tengshisoft.chatmodule.hwclud.api.IRefuse;
+import com.tengshisoft.chatmodule.hwclud.utils.CloudLinkPermission;
+import com.tengshisoft.chatmodule.hwclud.utils.NotifyUtil;
+
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.content.ContextCompat;
+
+/**
+ * @Time: 2021/5/31
+ * @Author: isoftstone
+ * @Description:
+ */
+public abstract class BasePermissionActivityV2 extends AppCompatActivity {
+ protected CloudLinkPermission cloudLinkPermission = new CloudLinkPermission();
+ /**
+ * 禁止后询问对话框
+ */
+ private CloudLinkDialog reqPermissionDialog;
+ /**
+ * 是否强制授权
+ */
+ protected boolean isForce;
+
+ protected void checkPermission(IAgree iAgree, IRefuse iRefuse, Class>... clz) {
+ if (cloudLinkPermission.checkPermission(this, iAgree, iRefuse, clz)) {
+ iAgree.agree();
+ }
+ }
+
+ protected void checkPermission(IAgree iAgree, Class>... clz) {
+ checkPermission(iAgree, null, clz);
+ }
+
+ protected void permissionReconfirm(List refusePermissions, boolean notAgainAsk) {
+ if (refusePermissions == null || refusePermissions.size() == 0) {
+ return;
+ }
+ StringBuilder stringBuilder = new StringBuilder();
+ for (String s : refusePermissions) {
+ stringBuilder.append(s);
+ stringBuilder.append("、");
+ }
+ if (stringBuilder.length() > 0) {
+ stringBuilder.deleteCharAt(stringBuilder.length() - 1);
+ }
+ String permissions = stringBuilder.toString();
+ String appName = getString(R.string.app_name);
+ String message = permissions;
+ if (notAgainAsk) {
+ message = message + getString(R.string.cloudLink_permission_settingHint, appName);
+ }
+ if (reqPermissionDialog != null && reqPermissionDialog.isShowing()) {
+ reqPermissionDialog.dismiss();
+ }
+ reqPermissionDialog = new CloudLinkDialog(this);
+ reqPermissionDialog.setStr_message(getString(R.string.cloudLink_permission_need) + message, Gravity.CENTER_VERTICAL);
+ reqPermissionDialog.setYes(notAgainAsk ? getString(R.string.cloudLink_go_setting) : getString(R.string.cloudLink_apply), null, () -> {
+ if (notAgainAsk) {
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getApplicationContext().getPackageName(), null);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(uri);
+ startActivity(intent);
+ } else {
+ cloudLinkPermission.reCheck();
+ }
+ reqPermissionDialog.dismiss();
+ });
+ reqPermissionDialog.setNo(isForce ? getString(R.string.cloudLink_mine_cancelAndExit) : getString(R.string.cloudLink_cancel), null, () -> {
+ reqPermissionDialog.dismiss();
+ if (isForce) {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ });
+ reqPermissionDialog.setCancelable(!isForce);
+ reqPermissionDialog.show();
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (cloudLinkPermission != null) {
+ cloudLinkPermission.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ protected void showOpenPermissionDialog() {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(this);
+ cloudLinkDialog.setStr_message(getResources().getString(R.string.cloudLink_meeting_getFloatPer), null);
+ cloudLinkDialog.setYes(getString(R.string.cloudLink_toopen), ContextCompat.getColorStateList(this, R.color.dialog_button_yes), () -> {
+ cloudLinkDialog.dismiss();
+ Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
+ startActivityForResult(intent, 101);
+ });
+ cloudLinkDialog.setNo(isForce ? getString(R.string.cloudLink_mine_cancelAndExit) : getString(R.string.cloudLink_cancel), null, () -> {
+ cloudLinkDialog.dismiss();
+ if (isForce) {
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ });
+ cloudLinkDialog.setCancelable(!isForce);
+ cloudLinkDialog.show();
+ }
+
+ protected void showBgStartActivityPermissionDialog() {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(this);
+ cloudLinkDialog.setStr_message(getString(R.string.cloudLink_openPermission), 1);
+ cloudLinkDialog.setYes(getString(R.string.cloudLink_sure), ContextCompat.getColorStateList(this, R.color.dialog_button_yes), () -> {
+ cloudLinkDialog.dismiss();
+ NotifyUtil.requestSetting(BasePermissionActivityV2.this);
+ });
+ cloudLinkDialog.setNo(getString(R.string.cloudLink_cancel), null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.show();
+ }
+
+ protected void goChannelSetting() {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(this);
+ cloudLinkDialog.setStr_message(getString(R.string.cloudLink_openChannel), 1);
+ cloudLinkDialog.setYes(getString(R.string.cloudLink_sure), ContextCompat.getColorStateList(this, R.color.dialog_button_yes), () -> {
+ cloudLinkDialog.dismiss();
+ NotifyUtil.requestChannel(BasePermissionActivityV2.this);
+ });
+ cloudLinkDialog.setNo(getString(R.string.cloudLink_cancel), null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.show();
+ }
+
+ protected void goNotifySetting() {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(this);
+ cloudLinkDialog.setStr_message(getString(R.string.cloudLink_openNotification), 1);
+ cloudLinkDialog.setYes(getString(R.string.cloudLink_sure), ContextCompat.getColorStateList(this, R.color.dialog_button_yes), () -> {
+ cloudLinkDialog.dismiss();
+ NotifyUtil.requestNotify(BasePermissionActivityV2.this);
+ });
+ cloudLinkDialog.setNo(getString(R.string.cloudLink_cancel), null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.show();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BasePresenterV2.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BasePresenterV2.java
new file mode 100644
index 0000000..bb29cf6
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/BasePresenterV2.java
@@ -0,0 +1,64 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+
+import com.tengshisoft.chatmodule.hwclud.utils.NetUtil;
+
+/**
+ * @Time: 2021/5/31
+ * @Author: isoftstone
+ * @Description:
+ */
+public abstract class BasePresenterV2 implements IPresenter {
+
+ protected V mRootView;
+ protected Context mContext;
+
+
+ @Override
+ public void attachView(V view, Context context) {
+ this.mRootView = view;
+ this.mContext = context;
+ }
+
+ @Override
+ public void detachView() {
+ this.mRootView = null;
+ this.mContext = null;
+ NetUtil.searchNameFormID(null, null);
+ }
+
+
+ protected V getView() {
+ return mRootView;
+ }
+
+ protected void showLoading() {
+ if (null != mRootView) {
+ getView().showLoading();
+ }
+ }
+
+ protected void onFail(Throwable ex, String code, String msg) {
+ if (null != mRootView) {
+ getView().onFail(ex, code, msg);
+ }
+ }
+
+ protected void onNetError() {
+ if (null != mRootView) {
+ getView().onNetError();
+ }
+ }
+
+ protected void dismissLoading() {
+ if (null != mRootView) {
+ getView().dismissLoading();
+ }
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/CloudLinkDialog.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/CloudLinkDialog.java
new file mode 100644
index 0000000..fe2cc61
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/CloudLinkDialog.java
@@ -0,0 +1,336 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.os.Bundle;
+import android.os.Handler;
+import android.text.Editable;
+import android.text.InputFilter;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.Gravity;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+
+import com.tengshisoft.chatmodule.R;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+
+/**
+ * @Time: 2021/7/9
+ * @Author: isoftstone
+ * @Description: 常用提示、确认、编辑框
+ */
+public class CloudLinkDialog extends Dialog {
+
+ private LinearLayout llEt;
+ private LinearLayout llCheck;
+ // 标题
+ private TextView title;
+ // 消息
+ private TextView message;
+ // 提示语
+ private TextView tvTip;
+ // 确定按钮
+ private TextView yes;
+ // 取消按钮
+ private TextView no;
+ private View line;
+ // 文本框
+ private NullMenuEditText editText_message;
+
+ private CheckBox checkBox;
+ // 标题文本
+ private String strTitle = null;
+ // 复选框文本
+ private String strCheckBox = null;
+ // 接收信息的文本
+ private String strMessage;
+ // 信息gravity属性
+ private Integer messageGravity = Gravity.CENTER;
+ // 确定按钮的文本
+ private String strYesBtn;
+ // 取消按钮的文本
+ private String strNoBtn;
+ // 确认字体颜色
+ private ColorStateList yesColor;
+ // 取消字体颜色
+ private ColorStateList noColor;
+ // 内容为空时显示的文本
+ private String textHint;
+ private boolean onlyYes = false;
+ private boolean isEdit = false;
+ /**
+ * 输入框是否文本信息
+ */
+ private boolean isTxt = false;
+ private int noVisibility;
+ // 点击事件
+ private OnYesOnclickListener yesOnclickListener;
+ // 点击事件
+ private OnNoOnclickListener noOnclickListener;
+ // checkBox监听
+ private CompoundButton.OnCheckedChangeListener checkedChangeListener;
+ private Context ctx;
+
+ public CloudLinkDialog(@NonNull Context context) {
+ super(context, R.style.CloudDialog);
+ ctx = context;
+ }
+
+ /**
+ * 设置确定按钮和取消被点击的接口
+ */
+ public interface OnYesOnclickListener {
+ /**
+ * 确定事件
+ */
+ void onYesClick();
+ }
+
+ public interface OnNoOnclickListener {
+ /**
+ * 取消事件
+ */
+ void onNoClick();
+ }
+
+ /**
+ * 设置标题显示
+ *
+ * @param str_title 标题内容
+ */
+ public void showTitle(String str_title) {
+ this.strTitle = str_title;
+ }
+
+ /**
+ * 设置标题显示
+ *
+ * @param str_checkBox 内容
+ */
+ public void setCheckBoxString(String str_checkBox, CompoundButton.OnCheckedChangeListener checkedChangeListener) {
+ this.checkedChangeListener = checkedChangeListener;
+ this.strCheckBox = str_checkBox;
+ }
+
+ public void setYes(String str_yes_btn, ColorStateList yes_color, OnYesOnclickListener yesOnclickListener) {
+ if (null != str_yes_btn) {
+ this.strYesBtn = str_yes_btn;
+ }
+ if (null != yes_color) {
+ this.yesColor = yes_color;
+ }
+ this.yesOnclickListener = yesOnclickListener;
+ }
+
+ public void setNo(String str_no_btn, ColorStateList no_color, OnNoOnclickListener noOnclickListener) {
+ if (null != str_no_btn) {
+ this.strNoBtn = str_no_btn;
+ }
+ if (null != no_color) {
+ this.noColor = no_color;
+
+ }
+ this.noOnclickListener = noOnclickListener;
+ }
+
+ public void setStr_message(String str_message, Integer message_gravity) {
+ this.strMessage = str_message;
+ if (null != message_gravity) {
+ this.messageGravity = message_gravity;
+ }
+ }
+
+ public void noVisibility(int visibility) {
+ noVisibility = visibility;
+ }
+
+ /**
+ * 设置编辑框
+ *
+ * @param edit
+ * @param hint
+ */
+ public void editText_message(boolean edit, String hint) {
+ this.isEdit = edit;
+ this.textHint = hint;
+ }
+
+ public void editText_message(boolean edit, String hint, boolean inputtype) {
+ this.isEdit = edit;
+ this.textHint = hint;
+ this.isTxt = inputtype;
+ }
+
+
+ /**
+ * 中有yes按钮
+ *
+ * @param onlyYes
+ */
+ public void setOnlyYes(boolean onlyYes) {
+ this.onlyYes = onlyYes;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.view_dialog);
+ setCanceledOnTouchOutside(false);
+ initView();
+ initData();
+ initEvent();
+ }
+
+ public void initView() {
+ yes = findViewById(R.id.yes);
+ no = findViewById(R.id.no);
+ no.setVisibility(noVisibility);
+ message = findViewById(R.id.message);
+ title = findViewById(R.id.title);
+ line = findViewById(R.id.line);
+ llEt = findViewById(R.id.ll_et);
+ llCheck = findViewById(R.id.ll_check);
+ checkBox = findViewById(R.id.checkbox);
+ editText_message = findViewById(R.id.text_message);
+ tvTip = findViewById(R.id.tv_tip_message);
+
+ }
+
+ public void initData() {
+ if (null != strMessage) {
+ message.setText(strMessage);
+ message.setGravity(messageGravity);
+ }
+ if (null != strYesBtn) {
+ yes.setText(strYesBtn);
+ }
+ if (null != strNoBtn) {
+ no.setText(strNoBtn);
+ }
+ if (null != yesColor) {
+ yes.setTextColor(yesColor);
+ }
+ if (null != noColor) {
+ no.setTextColor(noColor);
+ }
+ if (null != strTitle) {
+ title.setText(strTitle);
+ title.setVisibility(View.VISIBLE);
+ }
+ if (null != strCheckBox) {
+ checkBox.setText(strCheckBox);
+ checkBox.setOnCheckedChangeListener(checkedChangeListener);
+ llCheck.setVisibility(View.VISIBLE);
+ }
+ if (isEdit) {
+ llEt.setVisibility(View.VISIBLE);
+ editText_message.setVisibility(View.VISIBLE);
+ editText_message.setHint(textHint);
+ if (isTxt) {
+ editText_message.setInputType(InputType.TYPE_CLASS_TEXT);
+ // 最大输入长度
+ editText_message.setFilters(new InputFilter[]{new InputFilter.LengthFilter(16)});
+ } else {
+ editText_message.setIsDisplayPasswordImageView(false);
+ }
+ yes.setEnabled(false);
+ yes.setTextColor(ContextCompat.getColor(BaseAppContext.getInstance(), R.color.hideColor));
+ editText_message.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ tvTip.setText("");
+ tvTip.setVisibility(View.GONE);
+ if (TextUtils.isEmpty(s)) {
+ yes.setEnabled(false);
+ yes.setTextColor(ContextCompat.getColor(BaseAppContext.getInstance(), R.color.hideColor));
+ } else {
+ yes.setEnabled(true);
+ yes.setTextColor(ContextCompat.getColor(BaseAppContext.getInstance(), R.color.dialog_button_yes));
+ }
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+ }
+ if (onlyYes) {
+ no.setVisibility(View.GONE);
+ line.setVisibility(View.GONE);
+ }
+ }
+
+ private void initEvent() {
+ yes.setOnClickListener(v -> {
+ if (null != yesOnclickListener) {
+ yesOnclickListener.onYesClick();
+ }
+ });
+
+ no.setOnClickListener(v -> {
+ if (null != noOnclickListener) {
+ noOnclickListener.onNoClick();
+ }
+ });
+ }
+
+ /**
+ * 获取输入框内容
+ *
+ * @return
+ */
+ public String getEditTextMessage() {
+ if (isEdit) {
+ return editText_message.getText().toString();
+ }
+ return null;
+ }
+
+ /**
+ * 获取输入框view
+ *
+ * @return
+ */
+ public EditText getEditText() {
+ return editText_message;
+ }
+
+ /**
+ * 设置提示语
+ *
+ * @param tipMsg
+ */
+ public void setTipMsg(String tipMsg) {
+ if (!TextUtils.isEmpty(tipMsg)) {
+ tvTip.setText(tipMsg);
+ tvTip.setVisibility(View.VISIBLE);
+ }
+ }
+
+ public void hideSoftKeyBoard() {
+ new Handler().postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ InputMethodManager imm = (InputMethodManager) ctx.getSystemService(Context.INPUT_METHOD_SERVICE);
+ imm.hideSoftInputFromWindow(editText_message.getWindowToken(), 0);
+ }
+ }, 200);
+ }
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/CustomViewPager.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/CustomViewPager.java
new file mode 100644
index 0000000..e3e6e82
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/CustomViewPager.java
@@ -0,0 +1,71 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+
+
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.viewpager.widget.ViewPager;
+
+public class CustomViewPager extends ViewPager {
+
+ private float startX;
+ private float beforeX;
+ private CanRightScrollListener canRightScrollListener;
+
+ public void setCanRightScrollListener(CanRightScrollListener canRightScrollListener) {
+ this.canRightScrollListener = canRightScrollListener;
+ }
+
+ public interface CanRightScrollListener {
+ boolean noRightScroll(float dx);
+ }
+
+ public CustomViewPager(@NonNull Context context) {
+ super(context);
+ }
+
+ public CustomViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ /**
+ *因为Adapter里使用了PagerAdapter.POSITION_UNCHANGED,所以后面的页面没有移除,这里做限制,禁止滑动
+ */
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (!MeetingController.getInstance().isCanScroll){
+ return false;
+ }
+ startX = (int) event.getX();
+ beforeX = event.getX();
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (canRightScrollListener != null && canRightScrollListener.noRightScroll(event.getX() - beforeX)) {
+ if (event.getX() - beforeX < 0) {
+ return false;
+ }
+ beforeX = event.getX();
+ }
+ if (startX - event.getX() > 0 && getAdapter() != null && (getCurrentItem() == getAdapter().getCount() - 1)) {
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public void scrollTo(int x, int y) {
+ super.scrollTo(x, y);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/DialogUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/DialogUtil.java
new file mode 100644
index 0000000..4006fa8
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/DialogUtil.java
@@ -0,0 +1,226 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.MeetingTitleInfoBean;
+
+public class DialogUtil {
+ private static MeetingTitlePopWindow meetingTitlePopWindow = null;
+
+ /**
+ * @param context 上下文
+ * @param info 内容
+ * @param yes 右边按钮的的回调
+ */
+ public static void simpleAskDialog(Context context, String info, View.OnClickListener yes) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.setStr_message(info, Gravity.CENTER);
+ cloudLinkDialog.setYes(context.getString(R.string.cloudLink_sure), null, () -> {
+ cloudLinkDialog.dismiss();
+ yes.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.setNo(context.getString(R.string.cloudLink_cancel), null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.show();
+ }
+
+ /**
+ * @param context 上下文
+ * @param info 内容
+ * @param yesText 右边按钮的的文本
+ * @param noText 左边按钮的的文本
+ * @param yes 右边按钮的的回调
+ */
+ public static void simpleAskDialog(Context context, String info, String yesText, String noText, View.OnClickListener yes) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.setStr_message(info, Gravity.CENTER);
+ cloudLinkDialog.setYes(yesText, null, () -> {
+ cloudLinkDialog.dismiss();
+ yes.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.setNo(noText, null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.show();
+ }
+
+ /**
+ * @param context 上下文
+ * @param title 标题
+ * @param info 内容
+ * @param yesText 右边按钮的的文本
+ * @param noText 左边按钮的的文本
+ * @param yes 右边按钮的的回调
+ */
+ public static void simpleAskDialog(Context context, String title, String info, String yesText, String noText, View.OnClickListener yes) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.showTitle(title);
+ cloudLinkDialog.setStr_message(info, Gravity.CENTER);
+ cloudLinkDialog.setYes(yesText, null, () -> {
+ cloudLinkDialog.dismiss();
+ yes.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.setNo(noText, null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.show();
+ }
+
+ /**
+ * @param context 上下文
+ * @param info 内容
+ * @param yes 右边按钮的的回调
+ * @param no 左边按钮的的回调
+ */
+ public static void simpleAskDialog(Context context, String info, View.OnClickListener yes, View.OnClickListener no) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.setStr_message(info, Gravity.CENTER);
+ cloudLinkDialog.setYes(context.getString(R.string.cloudLink_sure), null, () -> {
+ cloudLinkDialog.dismiss();
+ yes.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.setNo(context.getString(R.string.cloudLink_cancel), null, () -> {
+ cloudLinkDialog.dismiss();
+ no.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.setOnCancelListener(dialog -> no.onClick(cloudLinkDialog.getCurrentFocus()));
+ cloudLinkDialog.show();
+ }
+
+ static FullScreenDialog fullScreenDialog = null;
+
+ /**
+ * @param context 上下文
+ * @param info 内容
+ * @param yes 右边按钮的的回调
+ * @param no 左边按钮的的回调
+ */
+ public static void requestAddVideo(Context context, String info, View.OnClickListener yes, View.OnClickListener no) {
+ View view = View.inflate(context, R.layout.activity_point_call, null);
+ fullScreenDialog = new FullScreenDialog(context);
+ view.findViewById(R.id.call_tool).setVisibility(View.GONE);
+ view.findViewById(R.id.iv_left_red_called).setVisibility(View.VISIBLE);
+ view.findViewById(R.id.iv_left_red_called).setOnClickListener(v -> {
+ fullScreenDialog.cancel();
+ no.onClick(v);
+ });
+ view.findViewById(R.id.iv_right_green_called).setVisibility(View.VISIBLE);
+ view.findViewById(R.id.iv_right_green_called).setOnClickListener(yes::onClick);
+ TextView textView = view.findViewById(R.id.tv_method);
+ textView.setText(info);
+ fullScreenDialog.setContentView(view);
+ fullScreenDialog.show();
+ }
+
+ public static void startCallOnDialog(Context context, String tName, String info, View.OnClickListener endCall) {
+ View view = View.inflate(context, R.layout.start_call, null);
+ view.findViewById(R.id.iv_center_red_call).setVisibility(View.VISIBLE);
+ view.findViewById(R.id.tv_name).setVisibility(View.VISIBLE);
+ ImageView bg = view.findViewById(R.id.bg_icon);
+// int index = FirstLetterUtil.getFirstLetter(tName).toLowerCase().charAt(0) * 10;
+ int index = 0;
+ bg.setImageResource(R.drawable.profile_image_bg);
+ bg.setImageLevel(index);
+ TextView name = view.findViewById(R.id.tv_name);
+ name.setText(tName);
+ TextView textView = view.findViewById(R.id.tv_method);
+ textView.setText(info);
+ fullScreenDialog = new FullScreenDialog(context);
+ fullScreenDialog.setCancelable(false);
+ view.findViewById(R.id.iv_center_red_call).setOnClickListener(v -> {
+ fullScreenDialog.cancel();
+ endCall.onClick(v);
+ });
+ fullScreenDialog.setContentView(view);
+ fullScreenDialog.show();
+ }
+
+ /**
+ * @param context 上下文
+ * @param title 标题
+ * @param info 内容
+ * @param checkBox CheckBox的文本
+ * @param checkedChangeListener checkB监听事件
+ * @param yes 右边按钮的的回调
+ * @param no 左边按钮的的回调
+ */
+ public static void simpleCheckDialog(Context context, String title, String info, String checkBox,
+ CompoundButton.OnCheckedChangeListener checkedChangeListener
+ , View.OnClickListener yes, View.OnClickListener no) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.showTitle(title);
+ cloudLinkDialog.setStr_message(info, Gravity.START);
+ cloudLinkDialog.setCheckBoxString(checkBox, checkedChangeListener);
+ cloudLinkDialog.setYes(context.getString(R.string.cloudLink_sure), null, () -> {
+ cloudLinkDialog.dismiss();
+ yes.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.setNo(context.getString(R.string.cloudLink_cancel), null, () -> {
+ cloudLinkDialog.dismiss();
+ no.onClick(cloudLinkDialog.getCurrentFocus());
+ });
+ cloudLinkDialog.show();
+ }
+
+ /**
+ * @param context 上下文
+ * @param info 内容
+ * @param buttonInfo 按钮内容
+ */
+ public static void tipsDialog(Context context, String info, String buttonInfo) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.setStr_message(info, Gravity.CENTER);
+ cloudLinkDialog.setYes(buttonInfo, null, cloudLinkDialog::dismiss);
+ cloudLinkDialog.noVisibility(View.GONE);
+ cloudLinkDialog.show();
+ }
+
+ /**
+ * @param context 上下文
+ * @param info 内容
+ * @param buttonInfo 按钮内容
+ */
+ public static void tipsDialog(Context context, String info, String buttonInfo, CloudLinkDialog.OnYesOnclickListener onYesOnclickListener) {
+ CloudLinkDialog cloudLinkDialog = new CloudLinkDialog(context);
+ cloudLinkDialog.setStr_message(info, Gravity.CENTER);
+ cloudLinkDialog.setYes(buttonInfo, null, () -> {
+ cloudLinkDialog.dismiss();
+ onYesOnclickListener.onYesClick();
+ });
+ cloudLinkDialog.noVisibility(View.GONE);
+ cloudLinkDialog.show();
+ }
+
+ public static void cancelRequestAddVideo() {
+ try {
+ if (fullScreenDialog != null) {
+ fullScreenDialog.cancel();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * @param context 上下文
+ * @param layoutType 布局类型
+ */
+ public static void meetingTitleInfoDialog(Context context, int layoutType, boolean isHorizontal, MeetingTitleInfoBean bean, View.OnClickListener onClickListener) {
+ if (meetingTitlePopWindow != null) {
+ meetingTitlePopWindow.dismiss();
+ }
+ meetingTitlePopWindow = new MeetingTitlePopWindow(context, layoutType, isHorizontal, bean, onClickListener);
+ meetingTitlePopWindow.showPopupWindow();
+ }
+
+ public static void updateNetworkQuality(int netLevel) {
+ if (meetingTitlePopWindow != null) {
+ meetingTitlePopWindow.setStreamImg(netLevel);
+ }
+ }
+
+ public static boolean isShow() {
+ return meetingTitlePopWindow != null && meetingTitlePopWindow.isShowing();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/EndConfDialog.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/EndConfDialog.java
new file mode 100644
index 0000000..fd5863b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/EndConfDialog.java
@@ -0,0 +1,140 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+
+import com.tengshisoft.chatmodule.R;
+
+import androidx.annotation.NonNull;
+
+/**
+ * @author Jelly
+ * @version 1.0
+ * @e-mail denggd0519@thundersoft.com
+ * @date 2020/12/9 14:09
+ * @desc
+ */
+public class EndConfDialog extends Dialog {
+ private String str_title = null;//标题文本
+ private String str_checkBox = null;//复选框文本
+ private onYesOnclickListener yesOnclickListener;//点击事件
+ private onNoOnclickListener noOnclickListener;//点击事件
+ private CompoundButton.OnCheckedChangeListener checkedChangeListener;//checkBox监听
+
+ private TextView title;//标题
+ private TextView yes;//确定按钮
+ private TextView no;//取消按钮
+ private String str_yes_btn;//确定按钮的文本
+ private String str_no_btn;//取消按钮的文本
+ private View line;
+ private CheckBox checkBox;
+ private LinearLayout ll_check;
+ private TextView tv_cbdes;//复选框后面文本
+ public EndConfDialog(@NonNull Context context) {
+ super(context, R.style.CloudDialog);
+ }
+ /**
+ * 设置确定按钮和取消被点击的接口
+ */
+ public interface onYesOnclickListener {
+ public void onYesClick();
+ }
+
+ public interface onNoOnclickListener {
+ public void onNoClick();
+ }
+ /**
+ * 设置标题显示
+ *
+ * @param str_title 标题内容
+ */
+ public void setTitle(String str_title) {
+ this.str_title = str_title;
+ }
+
+ /**
+ * 设置标题显示
+ *
+ * @param str_checkBox 内容
+ */
+ public void setCheckBoxString(String str_checkBox, CompoundButton.OnCheckedChangeListener checkedChangeListener) {
+ this.checkedChangeListener = checkedChangeListener;
+ this.str_checkBox = str_checkBox;
+ }
+ public void setYes(String str_yes_btn, onYesOnclickListener yesOnclickListener) {
+ if (null != str_yes_btn) {
+ this.str_yes_btn = str_yes_btn;
+ }
+ this.yesOnclickListener = yesOnclickListener;
+ }
+
+ public void setNo(String str_no_btn, onNoOnclickListener noOnclickListener) {
+ if (null != str_no_btn) {
+ this.str_no_btn = str_no_btn;
+ }
+ this.noOnclickListener = noOnclickListener;
+ }
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.endconf_dialog);
+ setCanceledOnTouchOutside(false);
+ initView();
+ initData();
+ initEvent();
+ }
+ public void initView() {
+ yes = findViewById(R.id.yes);
+ no = findViewById(R.id.no);
+ title = findViewById(R.id.title);
+ line = findViewById(R.id.line);
+ ll_check = findViewById(R.id.ll_check);
+ checkBox = findViewById(R.id.checkbox);
+ tv_cbdes = findViewById(R.id.tv_cbdes);
+ }
+ public void initData() {
+ if (null != str_title) {
+ title.setText(str_title);
+ title.setVisibility(View.VISIBLE);
+ }
+ if (null != str_yes_btn) {
+ yes.setText(str_yes_btn);
+ }
+ if (null != str_no_btn) {
+ no.setText(str_no_btn);
+ }
+ if (null != str_checkBox) {
+ ll_check.setVisibility(View.VISIBLE);
+ tv_cbdes.setText(str_checkBox);
+ checkBox.setOnCheckedChangeListener(checkedChangeListener);
+ }else {
+ ll_check.setVisibility(View.GONE);
+ }
+ }
+ private void initEvent() {
+ yes.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (null != yesOnclickListener) {
+ yesOnclickListener.onYesClick();
+ }
+ }
+ });
+
+ no.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (null != noOnclickListener) {
+ noOnclickListener.onNoClick();
+ }
+ }
+ });
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FloatNormalView.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FloatNormalView.java
new file mode 100644
index 0000000..daa8a3e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FloatNormalView.java
@@ -0,0 +1,283 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.os.Build;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.tengshisoft.chatmodule.R;
+
+import static com.tengshisoft.chatmodule.hwclud.utils.ScreenUtil.dp2ps;
+
+
+public class FloatNormalView extends FrameLayout {
+ private int footerBarHeight = 58;
+ private int s_floatViewHeight = 160;
+ private int s_floatViewWidth = 90;
+
+ private Context context = null;
+ private FrameLayout view = null;
+ private ImageView ivShowControlView = null;
+
+ private static WindowManager windowManager;
+
+ private float mTouchStartX;
+ private float mTouchStartY;
+ private long mLastTime, mCurTime;
+ private float x;
+ private float y;
+ private boolean initViewPlace = false;
+ private final FrameLayout fl_child;
+
+ private OnFloatClickListener mOnFloatListener;
+ private WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
+
+ public interface OnFloatClickListener {
+ void onClick();
+ }
+
+ public void setOnFloatClickListener(OnFloatClickListener onFloatListener) {
+ mOnFloatListener = onFloatListener;
+ }
+
+ public FloatNormalView(Context context) {
+ super(context);
+ this.context = context;
+ LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
+ view = findViewById(R.id.ll_float_normal);
+ fl_child = findViewById(R.id.fl_child);
+ windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+
+ initLayoutParams();
+ initEvent();
+ }
+
+ /**
+ * 获取子FrameLayout SurView的容器
+ */
+ public FrameLayout getChiidView() {
+ return fl_child;
+ }
+
+ /**
+ * 获取窗口
+ *
+ * @return
+ */
+ @Override
+ public FrameLayout getRootView() {
+ return this;
+ }
+
+ /**
+ * 初始化参数
+ */
+ private void initLayoutParams() {
+ //屏幕宽高
+ int screenWidth = windowManager.getDefaultDisplay().getWidth();
+ int screenHeight = windowManager.getDefaultDisplay().getHeight();
+ //总是出现在应用程序窗口之上。
+// lp.type = WindowManager.LayoutParams.TYPE_PHONE;
+ if (Build.VERSION.SDK_INT >= 21) {
+ lp.type = 2038;
+ } else {
+ lp.type = WindowManager.LayoutParams.TYPE_PHONE;
+ }
+
+ // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
+ // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
+ lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+
+ //悬浮窗默认显示的位置
+ lp.gravity = Gravity.START | Gravity.TOP;
+ if(isPad(context)){
+ lp.x = screenWidth - 425;
+ lp.y = screenHeight - 830;
+ }else{
+ lp.x = screenWidth - dp2ps(context,s_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context,s_floatViewHeight+footerBarHeight);
+ }
+
+ //悬浮窗的宽高
+ if (windowManager.getDefaultDisplay().getRotation() == Surface.ROTATION_0
+ || windowManager.getDefaultDisplay().getRotation() == Surface.ROTATION_180) {
+ if(isPad(context)){
+ lp.width = dp2ps(context, 135);
+ lp.height = dp2ps(context, 240);
+ }else{
+ lp.width = dp2ps(context, 90);
+ lp.height = dp2ps(context, 160);
+ }
+ } else {
+ if(isPad(context)){
+ lp.width = dp2ps(context, 240);
+ lp.height = dp2ps(context, 135);
+ }else{
+ lp.width = dp2ps(context, 160);
+ lp.height = dp2ps(context, 90);
+ }
+ }
+ lp.format = PixelFormat.TRANSPARENT;
+ windowManager.addView(this, lp);
+
+ }
+ /**
+ *
+ * @param b 竖屏
+ * @param popShow pop的显示隐藏
+ * @param height pop高度
+ */
+ public void postPoneRefreshFloatWindow(boolean b, boolean popShow,int height ) {
+ //屏幕宽高
+ int screenWidth = windowManager.getDefaultDisplay().getWidth();
+ int screenHeight = windowManager.getDefaultDisplay().getHeight();
+ if(b){//竖屏
+ if (popShow){
+ lp.width = dp2ps(context, s_floatViewWidth);
+ lp.height = dp2ps(context, s_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context,s_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context,s_floatViewHeight)-height;
+ }else {
+ lp.width = dp2ps(context, s_floatViewWidth);
+ lp.height = dp2ps(context, s_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context,s_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context,s_floatViewHeight+footerBarHeight);
+ }
+ } else{//横屏
+ int h_floatViewHeight = 90;
+ int h_floatViewWidth = 160;
+ if (popShow){
+ lp.width = dp2ps(context, h_floatViewWidth);
+ lp.height = dp2ps(context, h_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context, h_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context, h_floatViewHeight )-height;
+ }else {
+ lp.width = dp2ps(context, h_floatViewWidth);
+ lp.height = dp2ps(context, h_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context, h_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context, h_floatViewHeight +footerBarHeight);
+ }
+ }
+ init0(lp.x,lp.y);
+ windowManager.updateViewLayout(this, lp);
+ }
+ /**
+ *
+ * @param b 竖屏
+ * @param popShow pop的显示隐藏
+ * @param height pop高度
+ */
+ public void refreshFloatPos(boolean b, boolean popShow, int height ) {
+ //屏幕宽高
+ int screenWidth = windowManager.getDefaultDisplay().getWidth();
+ int screenHeight = windowManager.getDefaultDisplay().getHeight();
+ if(b){//竖屏
+ if (popShow){
+ lp.width = dp2ps(context, s_floatViewWidth);
+ lp.height = dp2ps(context, s_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context,s_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context,s_floatViewHeight+footerBarHeight)-height;
+ }else {
+ lp.width = dp2ps(context, s_floatViewWidth);
+ lp.height = dp2ps(context, s_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context,s_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context,s_floatViewHeight+footerBarHeight);
+ }
+ } else{//横屏
+ int h_floatViewHeight = 90;
+ int h_floatViewWidth = 160;
+ if (popShow){
+ lp.width = dp2ps(context, h_floatViewWidth);
+ lp.height = dp2ps(context, h_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context, h_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context, h_floatViewHeight +footerBarHeight)-height;
+ }else {
+ lp.width = dp2ps(context, h_floatViewWidth);
+ lp.height = dp2ps(context, h_floatViewHeight);
+ lp.x = screenWidth - dp2ps(context, h_floatViewWidth);
+ lp.y = screenHeight - dp2ps(context, h_floatViewHeight +footerBarHeight);
+ }
+ }
+ init0(lp.x,lp.y);
+ windowManager.updateViewLayout(this, lp);
+ }
+
+ /**
+ * 设置悬浮窗监听事件
+ */
+ private void initEvent() {
+ view.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ if (!initViewPlace) {
+ initViewPlace = true;
+ //获取初始位置
+ mTouchStartX += (event.getRawX() - lp.x);
+ mTouchStartY += (event.getRawY() - lp.y);
+ } else {
+ //根据上次手指离开的位置与此次点击的位置进行初始位置微调
+ mTouchStartX += (event.getRawX() - x);
+ mTouchStartY += (event.getRawY() - y);
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // 获取相对屏幕的坐标,以屏幕左上角为原点
+ x = event.getRawX();
+ y = event.getRawY();
+ updateViewPosition();
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mLastTime = mCurTime;
+ mCurTime = System.currentTimeMillis();
+ if (mCurTime - mLastTime < 300) {//双击事件
+ mCurTime = 0;
+ mLastTime = 0;
+ mOnFloatListener.onClick();
+ }
+ break;
+ }
+ return true;
+
+ }
+ });
+ }
+
+ /**
+ * 更新浮动窗口位置
+ */
+ private void updateViewPosition() {
+ lp.x = (int) (x - mTouchStartX);
+ lp.y = (int) (y - mTouchStartY);
+ windowManager.updateViewLayout(this, lp);
+ }
+
+ private void init0(float width,float height){
+ x=width;
+ y=height;
+ mTouchStartX=0;
+ mTouchStartY=0;
+ }
+
+ /**
+ * 判断当前设备是手机还是平板,代码来自 Google I/O App for Android
+ * @param context
+ * @return 平板返回 True,手机返回 False
+ */
+ public static boolean isPad(Context context) {
+ return (context.getResources().getConfiguration().screenLayout
+ & Configuration.SCREENLAYOUT_SIZE_MASK)
+ >= Configuration.SCREENLAYOUT_SIZE_LARGE;
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FloatingView.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FloatingView.java
new file mode 100644
index 0000000..5c2751c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FloatingView.java
@@ -0,0 +1,614 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Build;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.animation.OvershootInterpolator;
+import android.widget.FrameLayout;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+
+/**
+ * 悬浮窗视图
+ *
+ * @author PengZhenjin
+ * @date 2017-6-5
+ */
+public class FloatingView extends FrameLayout implements ViewTreeObserver.OnPreDrawListener {
+
+ private static final String TAG = "FloatingView";
+
+ /**
+ * 不需要移动的最低阈值(dp)
+ */
+ private static final float MOVE_THRESHOLD_DP = 8.0f;
+
+ /**
+ * 画面端移动动画的时长
+ */
+ private static final long MOVE_TO_EDGE_DURATION = 450L;
+
+ /**
+ * 画面端移动动画的系数
+ */
+ private static final float MOVE_TO_EDGE_OVERSHOOT_TENSION = 1.25f;
+
+ /**
+ * 默认的X坐标值
+ */
+ public static final int DEFAULT_X = Integer.MIN_VALUE;
+
+ /**
+ * 默认的Y坐标值
+ */
+ public static final int DEFAULT_Y = Integer.MIN_VALUE;
+
+ /**
+ * 默认的宽度
+ */
+ public static final int DEFAULT_WIDTH = ViewGroup.LayoutParams.WRAP_CONTENT;
+
+ /**
+ * 默认的高度
+ */
+ public static final int DEFAULT_HEIGHT = ViewGroup.LayoutParams.WRAP_CONTENT;
+
+ /**
+ * WindowManager
+ */
+ private WindowManager mWindowManager;
+
+ /**
+ * LayoutParams
+ */
+ private WindowManager.LayoutParams mParams;
+
+ /**
+ * DisplayMetrics
+ */
+ private DisplayMetrics mMetrics;
+
+ /**
+ * 悬浮窗的初始坐标
+ */
+ private int mInitX, mInitY;
+
+ /**
+ * 悬浮窗的触摸坐标
+ */
+ private float mViewTouchX, mViewTouchY;
+
+ /**
+ * 屏幕的触摸坐标
+ */
+ private float mScreenTouchX, mScreenTouchY;
+
+ /**
+ * 屏幕的触摸按下坐标(移动量判定用)
+ */
+ private float mScreenTouchDownX, mScreenTouchDownY;
+
+ /**
+ * 开始移动的标志
+ */
+ private boolean mIsMoveAccept;
+
+ /**
+ * 动画初始移动时的标志
+ */
+ private boolean mAnimateInitialMove;
+
+ /**
+ * 状态栏的高度
+ */
+ private int mBaseStatusBarHeight;
+
+ /**
+ * 当前状态栏的高度
+ */
+ private int mStatusBarHeight;
+
+ /**
+ * 导航条的高度
+ */
+ private int mBaseNavigationBarHeight;
+
+ /**
+ * 导航条的高度
+ * Placed bottom on the screen(tablet)
+ * Or placed vertically on the screen(phone)
+ */
+ private int mBaseNavigationBarRotatedHeight;
+
+ /**
+ * 当前导航条的垂直尺寸
+ */
+ private int mNavigationBarVerticalOffset;
+
+ /**
+ * 当前导航条的水平尺寸
+ */
+ private int mNavigationBarHorizontalOffset;
+
+ /**
+ * 移动动画
+ */
+ private ValueAnimator mMoveEdgeAnimator;
+
+ /**
+ * TimeInterpolator
+ */
+ private TimeInterpolator mMoveEdgeInterpolator;
+
+ /**
+ * 移动的界限Rect
+ */
+ private Rect mMoveLimitRect;
+
+ /**
+ * 显示位置的界限Rect
+ */
+ private Rect mPositionLimitRect;
+
+ /**
+ * 悬浮窗边缘的外边距
+ */
+ private int mOverMargin;
+
+ /**
+ * OnTouchListener
+ */
+ private OnTouchListener mOnTouchListener;
+
+ /**
+ * 移动方向
+ */
+ private int mMoveDirection;
+
+ /**
+ * 是否是平板电脑
+ */
+ private boolean mIsTablet;
+
+ /**
+ * 移动方向 - 默认
+ */
+ public static final int MOVE_DIRECTION_DEFAULT = 0;
+
+ /**
+ * 移动方向 - 左移动
+ */
+ public static final int MOVE_DIRECTION_LEFT = 1;
+
+ /**
+ * 移动方向 - 右移动
+ */
+ public static final int MOVE_DIRECTION_RIGHT = 2;
+
+ /**
+ * 移动方向 - 不移动
+ */
+ public static final int MOVE_DIRECTION_NONE = 3;
+
+ /**
+ * 移动方向
+ */
+ @IntDef({ MOVE_DIRECTION_DEFAULT, MOVE_DIRECTION_LEFT, MOVE_DIRECTION_RIGHT, MOVE_DIRECTION_NONE })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MoveDirection {}
+
+ /**
+ * 构造方法
+ *
+ * @param context 上下文
+ * @param x 悬浮窗在屏幕上的x坐标
+ * @param y 悬浮窗在屏幕上的y坐标
+ */
+ public FloatingView(@NonNull Context context, int x, int y) {
+ super(context);
+ this.mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ this.mMetrics = new DisplayMetrics();
+ this.mWindowManager.getDefaultDisplay().getMetrics(this.mMetrics);
+ this.mParams = new WindowManager.LayoutParams();
+ this.mParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ this.mParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ // 设置窗体显示类型
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ this.mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+ } else {
+ this.mParams.type = WindowManager.LayoutParams.TYPE_PHONE;
+ }
+ this.mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ this.mParams.format = PixelFormat.TRANSLUCENT;
+ this.mParams.gravity = Gravity.LEFT | Gravity.TOP;
+
+ this.mInitX = x;
+ this.mInitY = y;
+
+ this.mMoveEdgeInterpolator = new OvershootInterpolator(MOVE_TO_EDGE_OVERSHOOT_TENSION);
+ this.mMoveDirection = FloatingView.MOVE_DIRECTION_DEFAULT;
+
+ Resources resources = context.getResources();
+ this.mIsTablet = (resources.getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE;
+
+ this.mMoveLimitRect = new Rect();
+ this.mPositionLimitRect = new Rect();
+
+ this.mBaseStatusBarHeight = this.getSystemUiDimensionPixelSize(resources, "status_bar_height");
+ this.mStatusBarHeight = mBaseStatusBarHeight;
+
+ boolean hasMenuKey = ViewConfiguration.get(context).hasPermanentMenuKey();
+ boolean hasBackKey = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);
+ if (hasMenuKey || hasBackKey) {
+ this.mBaseNavigationBarHeight = 0;
+ this.mBaseNavigationBarRotatedHeight = 0;
+ }
+ else {
+ this.mBaseNavigationBarHeight = getSystemUiDimensionPixelSize(resources, "navigation_bar_height");
+ final String resName = mIsTablet ? "navigation_bar_height_landscape" : "navigation_bar_width";
+ this.mBaseNavigationBarRotatedHeight = getSystemUiDimensionPixelSize(resources, resName);
+ }
+
+ getViewTreeObserver().addOnPreDrawListener(this);
+ }
+
+ /**
+ * 获得系统ui维度(像素)
+ *
+ * @param resources {@link Resources}
+ * @param resName dimension resource name
+ *
+ * @return pixel size
+ */
+ private int getSystemUiDimensionPixelSize(Resources resources, String resName) {
+ int pixelSize = 0;
+ final int resId = resources.getIdentifier(resName, "dimen", "android");
+ if (resId > 0) {
+ pixelSize = resources.getDimensionPixelSize(resId);
+ }
+ return pixelSize;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ boolean isSizeChanged = w != oldw || h != oldh;
+ updateViewLayout(isSizeChanged);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateViewLayout(false);
+ }
+
+ @Override
+ public boolean onPreDraw() {
+ getViewTreeObserver().removeOnPreDrawListener(this);
+ if (this.mInitX == DEFAULT_X) {
+ this.mInitX = 0;
+ }
+ if (this.mInitY == DEFAULT_Y) {
+ this.mInitY = this.mMetrics.heightPixels - this.mStatusBarHeight - getMeasuredHeight();
+ }
+
+ // 悬浮窗的初始位置
+ this.mParams.x = this.mInitX;
+ this.mParams.y = this.mInitY;
+
+ if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_NONE) {
+ moveTo(this.mInitX, this.mInitY, this.mInitX, this.mInitY, false);
+ }
+ else {
+ moveToEdge(this.mInitX, this.mInitY, this.mAnimateInitialMove);
+ }
+
+ this.mWindowManager.updateViewLayout(this, this.mParams);
+
+ return true;
+ }
+
+ /**
+ * Called when the layout of the system has changed.
+ *
+ * @param isHideStatusBar If true, the status bar is hidden
+ * @param isHideNavigationBar If true, the navigation bar is hidden
+ * @param isPortrait If true, the device orientation is portrait
+ */
+ public void onUpdateSystemLayout(boolean isHideStatusBar, boolean isHideNavigationBar, boolean isPortrait) {
+ this.mStatusBarHeight = isHideStatusBar ? 0 : this.mBaseStatusBarHeight;
+ updateNavigationBarOffset(isHideNavigationBar, isPortrait);
+ updateViewLayout(true);
+ }
+
+ /**
+ * Update offset of NavigationBar.
+ *
+ * @param isHideNavigationBar If true, the navigation bar is hidden
+ * @param isPortrait If true, the device orientation is portrait
+ */
+ private void updateNavigationBarOffset(boolean isHideNavigationBar, boolean isPortrait) {
+ if (!isHideNavigationBar) {
+ this.mNavigationBarVerticalOffset = 0;
+ this.mNavigationBarHorizontalOffset = 0;
+ return;
+ }
+
+ // If the portrait, is displayed at the bottom of the screen
+ if (isPortrait) {
+ this.mNavigationBarVerticalOffset = this.mBaseNavigationBarHeight;
+ this.mNavigationBarHorizontalOffset = 0;
+ return;
+ }
+
+ // If it is a Tablet, it will appear at the bottom of the screen.
+ // If it is Phone, it will appear on the side of the screen
+ if (this.mIsTablet) {
+ this.mNavigationBarVerticalOffset = this.mBaseNavigationBarRotatedHeight;
+ this.mNavigationBarHorizontalOffset = 0;
+ }
+ else {
+ this.mNavigationBarVerticalOffset = 0;
+ this.mNavigationBarHorizontalOffset = this.mBaseNavigationBarRotatedHeight;
+ }
+ }
+
+ /**
+ * 更新悬浮窗布局
+ *
+ * @param isSizeChanged 悬浮窗大小是否有变化
+ */
+ private void updateViewLayout(boolean isSizeChanged) {
+ cancelAnimation();
+
+ int oldScreenHeight = this.mMetrics.heightPixels;
+ int oldScreenWidth = this.mMetrics.widthPixels;
+ int oldPositionLimitWidth = this.mPositionLimitRect.width();
+ int oldPositionLimitHeight = this.mPositionLimitRect.height();
+
+ this.mWindowManager.getDefaultDisplay().getMetrics(mMetrics);
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
+ int newScreenWidth = this.mMetrics.widthPixels;
+ int newScreenHeight = this.mMetrics.heightPixels;
+
+ // 设定移动范围
+ this.mMoveLimitRect.set(-width, -height * 2, newScreenWidth + width + this.mNavigationBarHorizontalOffset, newScreenHeight + height + this.mNavigationBarVerticalOffset);
+ this.mPositionLimitRect.set(-this.mOverMargin, 0, newScreenWidth - width + this.mOverMargin + this.mNavigationBarHorizontalOffset, newScreenHeight - this.mStatusBarHeight - height + this.mNavigationBarVerticalOffset);
+
+ // FloatingView size changed or device rotating
+ if (isSizeChanged || oldScreenWidth != newScreenWidth || oldScreenHeight != newScreenHeight) {
+ if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_DEFAULT) {
+ if (this.mParams.x > (newScreenWidth - width) / 2) {
+ this.mParams.x = this.mPositionLimitRect.right;
+ }
+ else {
+ this.mParams.x = this.mPositionLimitRect.left;
+ }
+ }
+ else if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_LEFT) {
+ this.mParams.x = this.mPositionLimitRect.left;
+ }
+ else if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_RIGHT) {
+ this.mParams.x = mPositionLimitRect.right;
+ }
+ else {
+ int newX = (int) (this.mParams.x * this.mPositionLimitRect.width() / (float) oldPositionLimitWidth + 0.5f);
+ this.mParams.x = Math.min(Math.max(this.mPositionLimitRect.left, newX), this.mPositionLimitRect.right);
+ }
+
+ int newY = (int) (this.mParams.y * this.mPositionLimitRect.height() / (float) oldPositionLimitHeight + 0.5f);
+ this.mParams.y = Math.min(Math.max(this.mPositionLimitRect.top, newY), this.mPositionLimitRect.bottom);
+ this.mWindowManager.updateViewLayout(this, this.mParams);
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ if (this.mMoveEdgeAnimator != null) {
+ this.mMoveEdgeAnimator.removeAllUpdateListeners();
+ }
+ super.onDetachedFromWindow();
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ this.mScreenTouchX = event.getRawX();
+ this.mScreenTouchY = event.getRawY();
+ int action = event.getAction();
+ if (action == MotionEvent.ACTION_DOWN) {
+ cancelAnimation();
+ this.mScreenTouchDownX = this.mScreenTouchX;
+ this.mScreenTouchDownY = this.mScreenTouchY;
+ this.mViewTouchX = event.getX();
+ this.mViewTouchY = event.getY();
+ this.mIsMoveAccept = false;
+ }
+ else if (action == MotionEvent.ACTION_MOVE) {
+ float moveThreshold = MOVE_THRESHOLD_DP * this.mMetrics.density;
+ if (!this.mIsMoveAccept && Math.abs(this.mScreenTouchX - this.mScreenTouchDownX) < moveThreshold && Math.abs(this.mScreenTouchY - this.mScreenTouchDownY) < moveThreshold) {
+ return true;
+ }
+ this.mIsMoveAccept = true;
+ this.updateViewPosition(getXByTouch(), getYByTouch());
+ }
+ else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ if (this.mIsMoveAccept) {
+ moveToEdge(true);
+ }
+ else {
+ int size = getChildCount();
+ for (int i = 0; i < size; i++) {
+ getChildAt(i).performClick();
+ }
+ }
+ }
+ if (this.mOnTouchListener != null) {
+ this.mOnTouchListener.onTouch(this, event);
+ }
+ return true;
+ }
+
+ @Override
+ public void setOnTouchListener(OnTouchListener listener) {
+ this.mOnTouchListener = listener;
+ }
+
+ /**
+ * 悬浮窗移动到边缘
+ *
+ * @param withAnimation 是否带动画
+ */
+ private void moveToEdge(boolean withAnimation) {
+ int currentX = getXByTouch();
+ int currentY = getYByTouch();
+ moveToEdge(currentX, currentY, withAnimation);
+ }
+
+ /**
+ * 悬浮窗移动悬浮窗到边缘
+ *
+ * @param startX
+ * @param startY
+ * @param withAnimation
+ */
+ private void moveToEdge(int startX, int startY, boolean withAnimation) {
+ int goalPositionX = startX;
+ int goalPositionY = startY;
+ if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_DEFAULT) {
+ boolean isMoveRightEdge = startX > (this.mMetrics.widthPixels - getWidth()) / 2;
+ goalPositionX = isMoveRightEdge ? this.mPositionLimitRect.right : this.mPositionLimitRect.left;
+ }
+ else if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_LEFT) {
+ goalPositionX = this.mPositionLimitRect.left;
+ }
+ else if (this.mMoveDirection == FloatingView.MOVE_DIRECTION_RIGHT) {
+ goalPositionX = this.mPositionLimitRect.right;
+ }
+ moveTo(startX, startY, goalPositionX, goalPositionY, withAnimation);
+ }
+
+ /**
+ * 移动悬浮窗
+ *
+ * @param currentX
+ * @param currentY
+ * @param goalPositionX
+ * @param goalPositionY
+ * @param withAnimation
+ */
+ private void moveTo(int currentX, int currentY, int goalPositionX, int goalPositionY, boolean withAnimation) {
+ goalPositionX = Math.min(Math.max(this.mPositionLimitRect.left, goalPositionX), this.mPositionLimitRect.right);
+ goalPositionY = Math.min(Math.max(this.mPositionLimitRect.top, goalPositionY), this.mPositionLimitRect.bottom);
+ if (withAnimation) {
+ this.mParams.y = goalPositionY;
+ this.mMoveEdgeAnimator = ValueAnimator.ofInt(currentX, goalPositionX);
+ this.mMoveEdgeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mParams.x = (Integer) animation.getAnimatedValue();
+ mWindowManager.updateViewLayout(FloatingView.this, mParams);
+ }
+ });
+ this.mMoveEdgeAnimator.setDuration(MOVE_TO_EDGE_DURATION);
+ this.mMoveEdgeAnimator.setInterpolator(this.mMoveEdgeInterpolator);
+ this.mMoveEdgeAnimator.start();
+ }
+ else {
+ if (this.mParams.x != goalPositionX || this.mParams.y != goalPositionY) {
+ this.mParams.x = goalPositionX;
+ this.mParams.y = goalPositionY;
+ this.mWindowManager.updateViewLayout(FloatingView.this, this.mParams);
+ }
+ }
+ this.mViewTouchX = 0;
+ this.mViewTouchY = 0;
+ this.mScreenTouchDownX = 0;
+ this.mScreenTouchDownY = 0;
+ this.mIsMoveAccept = false;
+ }
+
+ /**
+ * 取消动画
+ */
+ private void cancelAnimation() {
+ if (this.mMoveEdgeAnimator != null && this.mMoveEdgeAnimator.isStarted()) {
+ this.mMoveEdgeAnimator.cancel();
+ this.mMoveEdgeAnimator = null;
+ }
+ }
+
+ /**
+ * 设置悬浮窗边缘的外边距
+ *
+ * @param margin
+ */
+ public void setOverMargin(int margin) {
+ this.mOverMargin = margin;
+ }
+
+ /**
+ * 设置移动方向
+ *
+ * @param moveDirection
+ */
+ public void setMoveDirection(int moveDirection) {
+ this.mMoveDirection = moveDirection;
+ }
+
+ /**
+ * 设置悬浮窗移动时是否带动画
+ *
+ * @param animateInitialMove
+ */
+ public void setAnimateInitialMove(boolean animateInitialMove) {
+ this.mAnimateInitialMove = animateInitialMove;
+ }
+
+ /**
+ * 获取WindowLayoutParams
+ *
+ * @return
+ */
+ public WindowManager.LayoutParams getWindowLayoutParams() {
+ return this.mParams;
+ }
+
+ private int getXByTouch() {
+ return (int) (this.mScreenTouchX - this.mViewTouchX);
+ }
+
+ private int getYByTouch() {
+ return (int) (this.mScreenTouchY - this.mViewTouchY);
+ }
+
+ /**
+ * 更新悬浮窗在屏幕中的位置
+ *
+ * @param x
+ * @param y
+ */
+ private void updateViewPosition(int x, int y) {
+ this.mParams.x = x;
+ this.mParams.y = y;
+ this.mWindowManager.updateViewLayout(this, this.mParams);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FullScreenDialog.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FullScreenDialog.java
new file mode 100644
index 0000000..3aaf8bd
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/FullScreenDialog.java
@@ -0,0 +1,38 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.Window;
+import android.view.WindowManager;
+
+import androidx.appcompat.app.AppCompatDialog;
+
+
+public class FullScreenDialog extends AppCompatDialog {
+
+ public FullScreenDialog(Context context) {
+ super(context);
+ supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ //设置一个布局
+ Window window = this.getWindow();
+ if (window != null) {
+ window.getDecorView().setPadding(0, 0, 0, 0);
+ window.getDecorView().setBackgroundColor(Color.BLACK);
+ WindowManager.LayoutParams layoutParams = window.getAttributes();
+ layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
+ window.setAttributes(layoutParams);
+ }
+//// setContentView(R.layout.dialog_test_fullscreen);
+// //设置window背景,默认的背景会有Padding值,不能全屏。当然不一定要是透明,你可以设置其他背景,替换默认的背景即可。
+// getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+// //一定要在setContentView之后调用,否则无效
+// getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/GestureKtx.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/GestureKtx.kt
new file mode 100644
index 0000000..31c001e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/GestureKtx.kt
@@ -0,0 +1,31 @@
+package com.tengshisoft.chatmodule.hwclud.ui
+
+import android.annotation.SuppressLint
+import android.view.GestureDetector
+import android.view.MotionEvent
+import android.widget.FrameLayout
+
+/**
+ * Kotlin扩展函数
+ *
+ * 为系统ViewGroup增加手势控制事件
+ * @param singleClick 单击事件
+ * @param doubleClick 双击事件
+ * */
+@SuppressLint("ClickableViewAccessibility")
+fun FrameLayout.setGestureListener(singleClick: () -> Unit, doubleClick:() -> Unit) {
+ isLongClickable = true
+ val gestureScanner = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() {
+ override fun onSingleTapUp(e: MotionEvent?): Boolean {
+ singleClick()
+ return super.onSingleTapUp(e)
+ }
+ override fun onDoubleTap(e: MotionEvent?): Boolean {
+ doubleClick()
+ return super.onDoubleTap(e)
+ }
+ })
+ setOnTouchListener { _, event ->
+ gestureScanner.onTouchEvent(event)
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IMeetingBottomChanged.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IMeetingBottomChanged.java
new file mode 100644
index 0000000..8e21e25
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IMeetingBottomChanged.java
@@ -0,0 +1,21 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 会议底部控制view显示变化监听器
+ */
+public interface IMeetingBottomChanged {
+
+ /**
+ * 设置会议底部控制view是否课件
+ * @param visibility
+ */
+ void bottomChanged(int visibility);
+
+ /**
+ * 获取会议底部控制view是否可以
+ * @return 是否可以
+ */
+ boolean isAvailable() ;
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IPresenter.java
new file mode 100644
index 0000000..b43a6f7
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IPresenter.java
@@ -0,0 +1,25 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+
+
+/**
+ * @Time: 2021/5/31
+ * @Author: isoftstone
+ * @Description:
+ */
+public interface IPresenter {
+ /**
+ * 绑定view
+ *
+ * @param view view
+ * @param context context
+ */
+ void attachView(V view, Context context);
+
+ /**
+ * 分离view
+ */
+ void detachView();
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IView.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IView.java
new file mode 100644
index 0000000..37a868e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/IView.java
@@ -0,0 +1,40 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+/**
+ * @Time: 2021/5/31
+ * @Author: isoftstone
+ * @Description:
+ */
+public interface IView {
+ /**
+ * 显示加载框
+ */
+ void showLoading();
+
+ /**
+ * 显示加载框
+ *
+ * @param loading 提示信息
+ */
+ void showLoading(String loading);
+
+ /**
+ * 隐藏加载框
+ */
+ void dismissLoading();
+
+ /**
+ * 网络请求失败
+ *
+ * @param ex 异常信息
+ * @param code 错误码
+ * @param msg 错误信息
+ */
+ void onFail(Throwable ex, String code, String msg);
+
+ /**
+ * 网络错误
+ */
+ void onNetError();
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/LazyloadFragment.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/LazyloadFragment.java
new file mode 100644
index 0000000..512badb
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/LazyloadFragment.java
@@ -0,0 +1,188 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+
+import com.hjq.toast.ToastUtils;
+import com.tengshisoft.chatmodule.hwclud.listener.OrientationListener;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import java.lang.ref.WeakReference;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import butterknife.ButterKnife;
+
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+
+/**
+ * Fragment预加载问题的解决方案:
+ * 1.可以懒加载的Fragment
+ * 2.切换到其他页面时停止加载数据(可选)
+ */
+public abstract class LazyloadFragment extends Fragment {
+ /**
+ * 视图是否已经初初始化
+ */
+ protected boolean isInit = false;
+ protected boolean isLoad = false;
+ private int data = -1;
+ private static final String TAG = "LazyloadFragment";
+ private OrientationListener listener;
+ protected Boolean isAvailable = false;
+
+ /**
+ * 盲区角度
+ */
+ public int blindSize = 25;
+
+ private WeakReference viewReference;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ LogUtil.zzz(this.getClass().getName(), "onCreateView");
+ if (viewReference == null || viewReference.get() == null) {
+ View view = inflater.inflate(setContentView(), container, false);
+ ButterKnife.bind(this, view);
+ listener = new OrientationListener(getActivity());
+ listener.setOnOrientationListener(orientation -> {
+// LogUtil.zzz("updateRotation", "布局角度:" + orientation);
+ if (orientation > (45 + blindSize) && orientation < (135 - blindSize)) {
+ //横屏翻转
+ if (data != SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
+ getActivity().setRequestedOrientation(SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
+ data = SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+ }
+ } else if (orientation >= (135 + blindSize) && orientation <= (225 - blindSize)) {
+ //竖屏翻转 -反向竖屏
+ if (data != SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
+ getActivity().setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ data = SCREEN_ORIENTATION_PORTRAIT;
+ }
+ } else if (orientation > (225 + blindSize) && orientation < (315 - blindSize)) {
+ //横屏
+ if (data != SCREEN_ORIENTATION_LANDSCAPE) {
+ getActivity().setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ data = SCREEN_ORIENTATION_LANDSCAPE;
+ }
+ } else if ((orientation >= 0 && orientation <= (45 - blindSize)) || (orientation >= (315 + blindSize) && orientation <= 360)) {
+ //竖屏
+ if (data != SCREEN_ORIENTATION_PORTRAIT) {
+ getActivity().setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
+ data = SCREEN_ORIENTATION_PORTRAIT;
+ }
+ }
+ });
+ getData();
+ initView();
+ viewReference = new WeakReference<>(view);
+ }
+ isInit = true;
+ LogUtil.zzz("onCreateView", "isCanLoadData");
+ isCanLoadData();
+ return viewReference.get();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ initListener();
+ LogUtil.zzz(this.getClass().getName(), "onViewCreated");
+ }
+
+ /**
+ * 视图是否已经对用户可见,系统的方法
+ */
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ LogUtil.zzz("setUserVisibleHint", "isCanLoadData");
+ isAvailable = isVisibleToUser;
+ isCanLoadData();
+ }
+
+ /**
+ * 是否可以加载数据
+ * 可以加载数据的条件:
+ * 1.视图已经初始化
+ * 2.视图对用户可见
+ */
+ private void isCanLoadData() {
+ if (!isInit) {
+ return;
+ }
+ if (getUserVisibleHint()) {
+ LogUtil.zzz("isCanLoadData", "lazyLoad");
+ lazyLoad();
+ //注册监听
+ Log.d(TAG, "onPause: 注册监听");
+ listener.registerListener();
+
+ isLoad = true;
+ } else {
+ Log.d(TAG, "onPause: 反注册监听");
+ //界面不可见的时候要把监视器注销掉
+ listener.unregisterListener();
+ if (isLoad) {
+ stopLoad();
+ }
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ LogUtil.zzz(this.getClass().getName(), "onDestroy");
+ listener.unregisterListener();
+ }
+
+ /**
+ * 视图销毁的时候讲Fragment是否初始化的状态变为false
+ */
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ LogUtil.zzz(this.getClass().getName(), "onDestroyView");
+ isInit = false;
+ isLoad = false;
+ }
+
+ protected void showToast(String message) {
+ if (!TextUtils.isEmpty(message)) {
+ ToastUtils.show(message);
+ }
+ }
+
+ /**
+ * 设置Fragment要显示的布局
+ *
+ * @return 布局的layoutId
+ */
+ protected abstract int setContentView();
+
+ protected abstract void getData();
+
+ protected abstract void initView();
+
+ protected abstract void initListener();
+
+ /**
+ * 当视图初始化并且对用户可见的时候去真正的加载数据
+ */
+ protected abstract void lazyLoad();
+
+ /**
+ * 当视图已经对用户不可见并且加载过数据,如果需要在切换到其他页面时停止加载数据,可以调用此方法
+ */
+ protected void stopLoad() {
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ListPopWindow.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ListPopWindow.java
new file mode 100644
index 0000000..22d673d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ListPopWindow.java
@@ -0,0 +1,97 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.LinearLayout;
+
+import com.allen.library.SuperTextView;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.utils.ScreenUtil;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.util.List;
+
+import androidx.core.content.ContextCompat;
+
+/**
+ * @author limp
+ * @date 2020/12/16
+ */
+public class ListPopWindow extends Dialog {
+
+ public ListPopWindow(Context context, List list, boolean isSubtitleEnable, View.OnClickListener... onClickListeners) {
+ super(context, R.style.CloudDialog);
+ setContentView(R.layout.list_popwindow);
+ LinearLayout linearLayout = findViewById(R.id.pop);
+ for (int i = 0; i < list.size(); i++) {
+ SuperTextView textView = new SuperTextView(context);
+ textView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, ScreenUtil.dp2ps(context, 48)));
+ textView.setLeftString(list.get(i));
+ setRightDrawable(textView, isSubtitleEnable);
+ textView.setLeftTextGravity(1);
+ setTextColor(context,textView,list.get(i));
+ textView.setBottomDividerLineColor(context.getResources().getColor(R.color.video_line));
+ textView.setDividerLineType(SuperTextView.BOTTOM);
+ int finalI = i;
+ textView.setOnClickListener(v -> {
+ dismiss();
+ onClickListeners[finalI].onClick(v);
+ });
+ linearLayout.addView(textView);
+ }
+ Window window = getWindow();
+ window.setGravity(Gravity.BOTTOM);
+ window.setWindowAnimations(R.style.main_menu_animStyle);
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ private void setRightDrawable(SuperTextView textView, boolean isSubtitleEnable) {
+ String leftString = textView.getLeftString();
+ boolean isFirst = false;
+ if (leftString.equals(getContext().getString(R.string.cloudLink_meeting_disable_participant_caption))
+ || leftString.equals(getContext().getString(R.string.cloudLink_meeting_enable_participant_caption))) {
+ isFirst = EncryptedSPTool.getBoolean("App_isFirstPlaceSubtitle", true);
+ textView.getLeftTextView()
+ .setTextColor(ContextCompat.getColor(getContext(), isSubtitleEnable ? R.color.textWhiteColor : R.color.video_line));
+ textView.setEnabled(isSubtitleEnable);
+ }
+ if (leftString.equals(getContext().getString(R.string.cloudLink_meeting_disable_caption))
+ || leftString.equals(getContext().getString(R.string.cloudLink_meeting_enable_caption))) {
+ isFirst = EncryptedSPTool.getBoolean("App_isFirstSubtitle", true);
+ }
+ textView.setLeftTvDrawableRight(isFirst ? ContextCompat.getDrawable(getContext(), R.drawable.ic_subtitle_new) : null);
+ }
+
+ /**
+ * 根据会议中有无主持人,申请主持人和举手图标置灰或正常
+ * @param context
+ * @param textView
+ * @param s
+ */
+ private void setTextColor(Context context,SuperTextView textView,String s){
+ if (s.equals(context.getString(R.string.cloudLink_meeting_raiseHand))){
+ if (MeetingController.getInstance().isHasChairman()){
+ // 会议中有主席,举手正常
+ textView.setLeftTextColor(context.getResources().getColor(R.color.textWhiteColor));
+ }else {
+ // 会议中无主席,举手置灰
+ textView.setLeftTextColor(context.getResources().getColor(R.color.video_line));
+ }
+ }else if (s.equals(context.getString(R.string.cloudLink_meeting_applicationChairman))){
+ if (!MeetingController.getInstance().isHasChairman()){
+ // 会议中无主席,申请主持人正常
+ textView.setLeftTextColor(context.getResources().getColor(R.color.textWhiteColor));
+ }else {
+ // 会议中有主席,申请主持人置灰
+ textView.setLeftTextColor(context.getResources().getColor(R.color.video_line));
+ }
+ }else {
+ textView.setLeftTextColor(context.getResources().getColor(R.color.textWhiteColor));
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MeetingSubtitleView.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MeetingSubtitleView.java
new file mode 100644
index 0000000..523ddec
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MeetingSubtitleView.java
@@ -0,0 +1,326 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import com.huawei.ecterminalsdk.base.TsdkSubtitleContentInfo;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.ScreenUtil;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * AI字幕显示页面
+ */
+public class MeetingSubtitleView extends FrameLayout {
+ private static final String[] ACTION = {BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_INFO};
+ private static final int WINDOW_STATUS_DEFAULT = 0;
+ private static final int WINDOW_STATUS_MINIMIZE = 1;
+ private static final int WINDOW_STATUS_MAXIMIZE = 2;
+ private static final int DEF_LOCAL_HEIGHT = 110;
+ private static final int MAX_LOCAL_HEIGHT = 330;
+ private static final int MAX_LOCAL_HEIGHT_HZL = 200;
+ private static final int MIN_LOCAL_SIZE = 62;
+ private static final long DELAY_HIDE_TIME = 10000;
+ private final ValueAnimator mAnimator;
+ private final ViewGroup.LayoutParams mLayoutParams;
+ private int mOrientation;
+ private int mWindowStatus;
+ private boolean mIsNeedShow;
+ private MeetingSubtitleAdapter mMeetingSubtitleAdapter;
+ private RecyclerView mRecyclerView;
+ private float mOffset;
+
+ private final LocalBroadcastReceiver mBroadcastReceiver = (action, obj) -> {
+ if (action.equals(BroadcastConstant.ACTION_EVT_MEETING_SUBTITLE_INFO)) {
+ if (obj instanceof TsdkSubtitleContentInfo &&
+ !TextUtils.isEmpty(((TsdkSubtitleContentInfo) obj).getText())) {
+ post(() -> notifyMessageChanged(((TsdkSubtitleContentInfo) obj)));
+ }
+ }
+ };
+
+ private final Runnable mHideRunnable = () -> refreshMessageViewStatus(false);
+
+ public MeetingSubtitleView(@NonNull Context context) {
+ this(context, null);
+ }
+
+ public MeetingSubtitleView(@NonNull Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs, 0);
+ mLayoutParams = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ScreenUtil.sp2px(DEF_LOCAL_HEIGHT));
+ initView(context);
+ setVisibility(GONE);
+ setAlpha(0);
+ mAnimator = new ValueAnimator();
+ initAnimator();
+ }
+
+ private void initView(Context context) {
+ inflate(context, R.layout.meeting_subtitle_layout, this);
+ findViewById(R.id.subtitle_close).setOnClickListener(v -> hide());
+ findViewById(R.id.subtitle_min).setOnClickListener(v -> minimize(true));
+ findViewById(R.id.subtitle_size).setOnClickListener(v -> maximize(mWindowStatus != WINDOW_STATUS_MAXIMIZE));
+ findViewById(R.id.subtitle_up).setOnClickListener(v -> minimize(false));
+ mMeetingSubtitleAdapter = new MeetingSubtitleAdapter();
+ mRecyclerView = findViewById(R.id.subtitle_rv);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
+ mRecyclerView.setAdapter(mMeetingSubtitleAdapter);
+ mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
+ @Override
+ public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+ return true;
+ }
+
+ @Override
+ public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
+ }
+
+ @Override
+ public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+ }
+ });
+ Configuration configuration = getResources().getConfiguration();
+ mOrientation = configuration.orientation;
+ }
+
+ private void notifyMessageChanged(TsdkSubtitleContentInfo meetingSubtitle) {
+ if (!mIsNeedShow) {
+ return;
+ }
+ removeCallbacks(mHideRunnable);
+ refreshMessageViewStatus(mIsNeedShow);
+ if (mMeetingSubtitleAdapter != null) {
+ mMeetingSubtitleAdapter.addItem(meetingSubtitle);
+ mRecyclerView.scrollToPosition(mMeetingSubtitleAdapter.getItemCount() - 1);
+ }
+ if (findViewById(R.id.subtitle_up).getVisibility() == VISIBLE) {
+ findViewById(R.id.msg_subtitle_new).setVisibility(VISIBLE);
+ }
+ postDelayed(mHideRunnable, DELAY_HIDE_TIME);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ Configuration configuration = getResources().getConfiguration();
+ if (mOrientation != configuration.orientation) {
+ mOrientation = configuration.orientation;
+ refreshSubtitleView();
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void minimize(boolean isMinimize) {
+ mWindowStatus = isMinimize ? WINDOW_STATUS_MINIMIZE : WINDOW_STATUS_DEFAULT;
+ refreshSubtitleView();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void maximize(boolean isMaximize) {
+ mWindowStatus = isMaximize ? WINDOW_STATUS_MAXIMIZE : WINDOW_STATUS_DEFAULT;
+ refreshSubtitleView();
+ }
+
+ public void show() {
+ if (isShowing()) {
+ return;
+ }
+ mIsNeedShow = true;
+ LocalBroadcast.getInstance().registerBroadcast(mBroadcastReceiver, ACTION);
+ }
+
+ public void hide() {
+ removeCallbacks(mHideRunnable);
+ mIsNeedShow = false;
+ refreshMessageViewStatus(false);
+ LocalBroadcast.getInstance().unRegisterBroadcast(mBroadcastReceiver, ACTION);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void reset() {
+ mWindowStatus = WINDOW_STATUS_DEFAULT;
+ refreshSubtitleView();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ removeCallbacks(mHideRunnable);
+ LocalBroadcast.getInstance().unRegisterBroadcast(mBroadcastReceiver, ACTION);
+ super.onDetachedFromWindow();
+ }
+
+ private boolean isShowing() {
+ return getVisibility() == VISIBLE;
+ }
+
+ public boolean isEnable() {
+ return mIsNeedShow;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void refreshSubtitleView() {
+ switch (mWindowStatus) {
+ case WINDOW_STATUS_MINIMIZE:
+ mLayoutParams.width = ScreenUtil.sp2px(MIN_LOCAL_SIZE);
+ mLayoutParams.height = ScreenUtil.sp2px(MIN_LOCAL_SIZE);
+ break;
+ case WINDOW_STATUS_MAXIMIZE:
+ mLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ mLayoutParams.height = ScreenUtil.sp2px(mOrientation !=
+ ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ? MAX_LOCAL_HEIGHT_HZL : MAX_LOCAL_HEIGHT);
+ break;
+ default:
+ mLayoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ mLayoutParams.height = ScreenUtil.sp2px(DEF_LOCAL_HEIGHT);
+ break;
+ }
+ ImageButton imageButton = findViewById(R.id.subtitle_size);
+ imageButton.setImageResource(mWindowStatus == WINDOW_STATUS_MAXIMIZE ?
+ R.drawable.ic_subtitle_fullscreen_close : R.drawable.ic_subtitle_fullscreen_open);
+ boolean isMinimize = mWindowStatus == WINDOW_STATUS_MINIMIZE;
+ findViewById(R.id.subtitle_content).setVisibility(isMinimize ? View.GONE : View.VISIBLE);
+ findViewById(R.id.subtitle_up).setVisibility(isMinimize ? View.VISIBLE : View.GONE);
+ findViewById(R.id.msg_subtitle_new).setVisibility(GONE);
+ Optional.ofNullable(getLayoutParams()).ifPresent(layoutParams -> {
+ layoutParams.width = mLayoutParams.width;
+ layoutParams.height = mLayoutParams.height;
+ setLayoutParams(layoutParams);
+ });
+ mRecyclerView.scrollToPosition(mMeetingSubtitleAdapter.getItemCount() - 1);
+ }
+
+ private void refreshMessageViewStatus(boolean isNeedShow) {
+ if (isNeedShow) {
+ if (isShowing()) {
+ return;
+ }
+ setVisibility(VISIBLE);
+ } else {
+ if (!isShowing()) {
+ return;
+ }
+ }
+
+ if (mAnimator.isRunning()) {
+ mAnimator.cancel();
+ }
+ mAnimator.setFloatValues(mOffset, isNeedShow ? 1.0f : 0f);
+ mAnimator.start();
+ }
+
+ private void initAnimator() {
+ mAnimator.addUpdateListener(animation -> {
+ mOffset = (float) animation.getAnimatedValue();
+ setAlpha(mOffset);
+
+ });
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mOffset == 0) {
+ if (!mIsNeedShow && mMeetingSubtitleAdapter != null) {
+ mMeetingSubtitleAdapter.clear();
+ }
+ setVisibility(GONE);
+ reset();
+ }
+ }
+ });
+ }
+
+ public void setSubtitleEnable(boolean isSubtitleEnable) {
+ if (!isSubtitleEnable && isShowing()) {
+ removeCallbacks(mHideRunnable);
+ post(mHideRunnable);
+ }
+ }
+
+ private static class MeetingSubtitleAdapter extends RecyclerView.Adapter {
+
+ private final List mMeetingSubtitles;
+ private final Map mUserNameBgLevels;
+
+ public MeetingSubtitleAdapter() {
+ mMeetingSubtitles = new ArrayList<>();
+ mUserNameBgLevels = new HashMap<>();
+ }
+
+ @NonNull
+ @Override
+ public SubViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new SubViewHolder(inflate(parent.getContext(), R.layout.item_meeting_subtitle, null));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull SubViewHolder viewHolder, int position) {
+ TsdkSubtitleContentInfo meetingSubtitle = mMeetingSubtitles.get(position);
+ viewHolder.setText(R.id.userName, meetingSubtitle.getName());
+ viewHolder.setText(R.id.content, meetingSubtitle.getText());
+ if (!mUserNameBgLevels.containsKey(meetingSubtitle.getName())) {
+ mUserNameBgLevels.put(meetingSubtitle.getName(), position % 4);
+ }
+ int index = mUserNameBgLevels.get(meetingSubtitle.getName());
+ viewHolder.setLevel(index);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mMeetingSubtitles.size();
+ }
+
+ public void addItem(TsdkSubtitleContentInfo meetingSubtitle) {
+ mMeetingSubtitles.add(meetingSubtitle);
+ notifyItemInserted(mMeetingSubtitles.size() - 1);
+ }
+
+ public void clear() {
+ mMeetingSubtitles.clear();
+ notifyDataSetChanged();
+ }
+ }
+
+ private static class SubViewHolder extends RecyclerView.ViewHolder {
+
+ public SubViewHolder(@NonNull View itemView) {
+ super(itemView);
+ }
+
+ public void setText(@IdRes int viewId, CharSequence value) {
+ TextView view = itemView.findViewById(viewId);
+ view.setText(value);
+ }
+
+ public void setLevel(int level) {
+ itemView.findViewById(R.id.startHeader).getBackground().setLevel(level);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MeetingTitlePopWindow.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MeetingTitlePopWindow.java
new file mode 100644
index 0000000..148f234
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MeetingTitlePopWindow.java
@@ -0,0 +1,179 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.text.TextUtils;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.allen.library.SuperTextView;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.MeetingTitleInfoBean;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingTitleBarController;
+import com.tengshisoft.chatmodule.hwclud.manager.LoginMangerV2;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.PhoneUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UiUtils;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import razerdp.basepopup.BasePopupWindow;
+
+
+/**
+ * @Time: 2021/7/9
+ * @Author: isoftstone
+ * @Description: 会议头部信息PopWindow
+ */
+public class MeetingTitlePopWindow extends BasePopupWindow {
+
+ TextView tvTitle;
+ TextView tvTime;
+ SuperTextView tvConfId;
+ SuperTextView tvGuestPwd;
+ SuperTextView tvChairmanPwd;
+ LinearLayout linConf, linLeft, linBottom;
+ SuperTextView tvTerminal;
+ LinearLayout linPoint;
+ LinearLayout linChairmanPwd;
+ RelativeLayout linBitStream;
+ ImageView imageView;
+ public String defChairmanPwd = "*** ***";
+
+ public MeetingTitlePopWindow(Context context, int layoutType, boolean isHorizontal, MeetingTitleInfoBean bean,
+ View.OnClickListener onClickListener) {
+ super(context);
+ initView();
+ linLeft.setOnClickListener(v -> dismiss());
+ linBottom.setOnClickListener(v -> dismiss());
+ if (isHorizontal) {
+ linLeft.setVisibility(View.VISIBLE);
+ }
+ linBitStream.setOnClickListener(v -> {
+ dismiss();
+ onClickListener.onClick(v);
+ });
+ switch (layoutType) {
+ case BaseConfContract.LAYOUT_POINT_VOICE:
+ case BaseConfContract.LAYOUT_POINT_VIDEO:
+ linConf.setVisibility(View.GONE);
+ linPoint.setVisibility(View.VISIBLE);
+ setPointData(bean);
+ break;
+ case BaseConfContract.LAYOUT_CONF_VOICE:
+ case BaseConfContract.LAYOUT_CONF_VIDEO:
+ linConf.setVisibility(View.VISIBLE);
+ linPoint.setVisibility(View.GONE);
+ setConfData(bean);
+ break;
+ default:
+ break;
+ }
+ setStreamImg(MeetingTitleBarController.mCurrentNetLevel);
+ }
+
+ private void setConfData(MeetingTitleInfoBean bean) {
+ tvTitle.setText(bean.getSubject());
+ if (UIUtil.isService3()) {
+ if (MeetingController.getInstance().isChairman() && (
+ TextUtils.equals(bean.getScheduleUserName(), LoginMangerV2.getInstance().getDisplayName())
+ || !TextUtils.isEmpty(EncryptedSPTool.getString(Constant.MEETING_CHAIRMAN))
+ || MeetingController.getInstance().isHasChairman())) {
+ if (!TextUtils.isEmpty(bean.getChairmanPwd())) {
+ linChairmanPwd.setVisibility(View.VISIBLE);
+ } else {
+ linChairmanPwd.setVisibility(View.GONE);
+ }
+ } else {
+ linChairmanPwd.setVisibility(View.GONE);
+ }
+ tvConfId.setLeftString(UIUtil.splitString(bean.getConfID()));
+ if (TextUtils.isEmpty(bean.getGuestPwd())) {
+ tvGuestPwd.setVisibility(View.GONE);
+ } else {
+ tvGuestPwd.setLeftBottomString(bean.getGuestPwd());
+ }
+ tvChairmanPwd.setRightImageViewClickListener(imageView -> {
+ if (defChairmanPwd.equals(tvChairmanPwd.getLeftString())) {
+ if (TextUtils.isEmpty(bean.getChairmanPwd())) {
+ tvChairmanPwd.setLeftString(EncryptedSPTool.getString(Constant.MEETING_CHAIRMAN));
+ } else {
+ tvChairmanPwd.setLeftString(bean.getChairmanPwd());
+ }
+ tvChairmanPwd.setRightIcon(R.drawable.ic_eye);
+ } else {
+ tvChairmanPwd.setLeftString(defChairmanPwd);
+ tvChairmanPwd.setRightIcon(R.drawable.ic_password_not_view);
+ }
+ });
+ } else {
+ tvGuestPwd.setVisibility(View.GONE);
+ if (bean.getConfID().contains("*")) {
+ String[] split = bean.getConfID().split("\\*");
+ if (split.length == 1) {
+ tvConfId.setLeftString(UIUtil.splitString(split[0]));
+ } else if (split.length == 2) {
+ tvConfId.setLeftString(UIUtil.splitString(split[0]));
+ }
+ } else {
+ tvConfId.setLeftString(UIUtil.splitString(bean.getConfID()));
+ }
+
+ if (TextUtils.isEmpty(bean.getChairmanPwd()) && TextUtils.isEmpty(EncryptedSPTool.getString(Constant.MEETING_CHAIRMAN))) {
+ linChairmanPwd.setVisibility(View.GONE);
+ } else {
+ tvChairmanPwd.setRightImageViewClickListener(imageView -> {
+ if (defChairmanPwd.equals(tvChairmanPwd.getLeftString())) {
+ if (TextUtils.isEmpty(bean.getChairmanPwd())) {
+ tvChairmanPwd.setLeftString(EncryptedSPTool.getString(Constant.MEETING_CHAIRMAN));
+ } else {
+ tvChairmanPwd.setLeftString(bean.getChairmanPwd());
+ }
+ tvChairmanPwd.setRightIcon(R.drawable.ic_eye_white);
+ } else {
+ tvChairmanPwd.setLeftString(defChairmanPwd);
+ tvChairmanPwd.setRightIcon(R.drawable.ic_password_not_view_white);
+ }
+ });
+ }
+ }
+ tvTime.setText(bean.getTime());
+ tvConfId.setRightImageViewClickListener(imageView -> {
+ PhoneUtil.copyToClipboard(bean.getConfID());
+ });
+ }
+
+ private void setPointData(MeetingTitleInfoBean bean) {
+ tvTitle.setText(bean.getSubject());
+ tvTerminal.setLeftBottomString(bean.getTerminal());
+ setStreamImg(MeetingTitleBarController.mCurrentNetLevel);
+ }
+
+ public void setStreamImg(int netLevel) {
+ UiUtils.setNetLevel(netLevel, imageView);
+ }
+
+ @SuppressLint("WrongViewCast")
+ private void initView() {
+ View view = createPopupById(R.layout.activity_meeting_title_info);
+ linBitStream = view.findViewById(R.id.rel_bitStream);
+ tvTitle = view.findViewById(R.id.tv_title);
+ tvTime = view.findViewById(R.id.tv_time);
+ tvConfId = view.findViewById(R.id.tv_confId);
+ tvGuestPwd = view.findViewById(R.id.tv_guestPwd);
+ tvChairmanPwd = view.findViewById(R.id.tv_chairmanPwd);
+ linConf = view.findViewById(R.id.lin_conf);
+ linLeft = view.findViewById(R.id.lin_left);
+ linBottom = view.findViewById(R.id.lin_bottom);
+ tvTerminal = view.findViewById(R.id.tv_terminal);
+ linPoint = view.findViewById(R.id.lin_point);
+ linChairmanPwd = view.findViewById(R.id.lin_chairmanPwd);
+ imageView = view.findViewById(R.id.img_netLevel);
+ }
+
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MySponsorPageAdapter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MySponsorPageAdapter.java
new file mode 100644
index 0000000..ecdf1d0
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MySponsorPageAdapter.java
@@ -0,0 +1,308 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.ViewGroup;
+
+
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.core.beans.Member;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.PagerAdapter;
+
+import static com.tenlionsoft.baselib.constant.ConfConstant.ParticipantStatus.IN_CONF;
+
+
+public class MySponsorPageAdapter extends FragmentStatePagerAdapter {
+
+ private final int MEMBER_AUDIO = 2;
+ private final int MEMBER_ADD = 1;
+ private final int MEMBER_REDUCE = -1;
+ private final int MEMBER_NO_CHANGE = 0;
+
+ List memberList;
+ List hasAudioMemberList;
+ private Fragment[] mFragments;
+ private Fragment auxFragments;
+ private int mPages;
+ private Boolean isSVC;
+ private MyTsdkCallInfo myTsdkCallInfo;
+ private boolean isAuxData;
+ private boolean fromAux = false;//是否是切换辅流
+ private int memberStatus = MEMBER_NO_CHANGE;
+ private Member watchMember;
+ private int totalPages;
+ private boolean isRefreshView;
+
+ /**
+ * 构造函数
+ *
+ * @param fm fragment管理员对象
+ */
+ public MySponsorPageAdapter(FragmentManager fm, boolean isAuxData, List numbers, int pages, boolean misSVC, MyTsdkCallInfo mTysdkCallInfo, Member watch_member) {
+ super(fm);
+
+ if (isAuxData) {
+ mFragments = new Fragment[2 + pages];
+ } else {
+ mFragments = new Fragment[1 + pages];
+ }
+ hasAudioMemberList = new ArrayList<>(numbers);
+ Iterator iterator = numbers.iterator();
+ while (iterator.hasNext()) {
+ Member member = iterator.next();
+ if (member.getIsAudio()) {
+ iterator.remove();
+ }
+ }
+ memberList = numbers;
+ mPages = pages;
+ isSVC = misSVC;
+ this.isAuxData = isAuxData;
+
+ myTsdkCallInfo = mTysdkCallInfo;
+ watchMember = watch_member;
+ }
+
+ public void refreshMemberListAndPage(boolean isAuxData, List members, int pages,
+ boolean misSVC, MyTsdkCallInfo mTysdkCallInfo, Member watch_member) {
+ if (members == null) return;
+ LogUtil.zzz("SVC Adapter", "refreshMemberListAndPage members.size == " + members.size() + " hasAudioMemberList.size == " + hasAudioMemberList.size() + " memberList.size == " + memberList.size());
+ if (members.size() > hasAudioMemberList.size()) {
+ // 人数增加,判断增加的人是视频还是音频
+ for (Member m : members) {
+ boolean isEqu = false;
+ for (Member member : hasAudioMemberList) {
+ if (m.getNumber().equals(member.getNumber())) {
+ isEqu = true;
+ break;
+ }
+ }
+ if (!isEqu) {
+ if (m.getIsAudio()) {
+ memberStatus = MEMBER_AUDIO;
+ } else {
+ memberStatus = MEMBER_ADD;
+ break;
+ }
+ }
+ }
+ } else if (members.size() < hasAudioMemberList.size()) {
+ // 人数减少,判断减少的人是视频还是音频
+ for (Member member : hasAudioMemberList) {
+ boolean isEqu = false;
+ for (Member m : members) {
+ if (m.getNumber().equals(member.getNumber())) {
+ isEqu = true;
+ break;
+ }
+ }
+ if (!isEqu) {
+ if (member.getIsAudio()) {
+ memberStatus = MEMBER_AUDIO;
+ } else {
+ memberStatus = MEMBER_REDUCE;
+ break;
+ }
+ }
+ }
+ } else {
+ // 人数相等
+ boolean noChange = true;
+ for (Member member : hasAudioMemberList) {
+ boolean isEqu = false;
+ for (Member m : members) {
+ if (m.getNumber().equals(member.getNumber())) {
+ isEqu = true;
+ break;
+ }
+ }
+ noChange = noChange && isEqu;
+ }
+ if (noChange) {
+ memberStatus = MEMBER_AUDIO;
+ } else {
+ memberStatus = MEMBER_NO_CHANGE;
+ }
+ }
+ this.hasAudioMemberList = new ArrayList<>(members);
+ Iterator iterator = members.iterator();
+ while (iterator.hasNext()) {
+ Member member = iterator.next();
+ if (member.getIsAudio() || member.getStatus() != IN_CONF) {
+ iterator.remove();
+ }
+ }
+ this.memberList.clear();
+ this.memberList.addAll(members);
+ this.mPages = pages;
+ this.isSVC = misSVC;
+ this.myTsdkCallInfo = mTysdkCallInfo;
+ this.watchMember = watch_member;
+ isRefreshView = false;
+ if (getCount() != totalPages) {
+ isRefreshView = true;
+ }
+ totalPages = getCount();
+ Fragment[] fragments = new Fragment[totalPages];
+ if (this.isAuxData == isAuxData) {
+ this.fromAux = false;
+ if (mFragments.length != totalPages) {
+ for (int i = 0; i < fragments.length; i++) {
+ if (mFragments.length > i) {
+ fragments[i] = mFragments[i];
+ }
+ }
+ }
+ } else {
+ this.fromAux = true;
+ this.isAuxData = isAuxData;
+ }
+ mFragments = fragments;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public Object instantiateItem(@NonNull ViewGroup container, int position) {
+ Object f = super.instantiateItem(container, position);
+ if (f instanceof RemoteFragment) {
+ ((RemoteFragment) f).refreshParams(isSVC, myTsdkCallInfo, watchMember, new ArrayList<>(memberList), isAuxData);
+ }
+ if (f instanceof SvcVideoFragment) {
+ ((SvcVideoFragment) f).setMembers(memberList);
+ }
+ return f;
+ }
+
+ @NotNull
+ @Override
+ public Fragment getItem(int position) {
+ if (isAuxData && position == 0) {//辅流页面
+ if (auxFragments == null) {
+ auxFragments = AuxMeetingFragment.getInstance(myTsdkCallInfo);
+ }
+ return auxFragments;
+ } else if (isAuxData && position == 1 || (!isAuxData && position == 0)) {//选看大页面
+ position = isAuxData ? position - 1 : position;
+ if (!(mFragments[position] instanceof RemoteFragment)) {
+ mFragments[position] = new RemoteFragment(isSVC, myTsdkCallInfo, watchMember, memberList, isAuxData);
+ }
+ return mFragments[position];
+ } else if (isAuxData && position > 1 || (!isAuxData && position > 0)) {//SVC页面
+ position = isAuxData ? position - 1 : position;
+ if (!(mFragments[position] instanceof SvcVideoFragment)) {
+ mFragments[position] = SvcVideoFragment.Companion.getInstance(memberList, position, myTsdkCallInfo);
+ }
+ return mFragments[position];
+ }
+ return mFragments[position];
+ }
+
+ /**
+ * PagerAdapter.POSITION_UNCHANGED 不更新
+ * PagerAdapter.POSITION_NONE 更新
+ *
+ *
+ * 更有方案
+ * return PagerAdapter.POSITION_UNCHANGED
+ * 把与会者差异传给fragment,让fragment自己内部去局部更新
+ */
+ @Override
+ public int getItemPosition(@NotNull Object object) {
+ if (fromAux) {
+ // 如果辅流状态变化引起的notifyDataSetChanged,让它刷新
+ return PagerAdapter.POSITION_NONE;
+ }
+
+ if (object instanceof LazyloadFragment && !((LazyloadFragment) object).getUserVisibleHint()) {
+ //当前页不在画中画页面,也要把与会者列表给更新下,以免随机选看时选到失效的会场
+ if (object instanceof RemoteFragment) {
+ ((RemoteFragment) object).refreshParams(isSVC, myTsdkCallInfo, watchMember, new ArrayList<>(memberList), isAuxData);
+ }
+ return PagerAdapter.POSITION_UNCHANGED;
+ }
+
+ if (object instanceof AuxMeetingFragment) {//当前页是辅流
+ return PagerAdapter.POSITION_UNCHANGED;
+ } else if (object instanceof RemoteFragment) {//当前页是选看大页
+ List members = ((RemoteFragment) object).getMemberList();
+ if (members == null || members.isEmpty()) {
+ return PagerAdapter.POSITION_NONE;
+ } else if (members.size() != memberList.size()) {
+ ((RemoteFragment) object).refreshParams(isSVC, myTsdkCallInfo, watchMember, new ArrayList<>(memberList), isAuxData);
+ }
+ // 辅流页面缩放重置
+ if (auxFragments != null){
+ auxFragments.onResume();
+ }
+ return PagerAdapter.POSITION_UNCHANGED;
+ } else if (object instanceof SvcVideoFragment) {//当前页是svc页面
+ List members = ((SvcVideoFragment) object).getWatchMembers();
+ switch (memberStatus) {
+ case MEMBER_ADD://增加,当前页少于3个则刷新,否则不刷
+ return members.size() < 3 ? PagerAdapter.POSITION_NONE : PagerAdapter.POSITION_UNCHANGED;
+ case MEMBER_REDUCE://减少,当前页的都还在则不刷,否则刷新
+ HashMap watchMembers = new HashMap<>();
+ for (Member member : members) {
+ if (!watchMembers.containsKey(member.getNumber())) {
+ watchMembers.put(member.getNumber(), member);
+ }
+ }
+ int watchMemberCount = 0;
+ for (Member member : memberList) {
+ if (watchMembers.containsKey(member.getNumber())) {
+ watchMemberCount++;
+ }
+ }
+ if (isRefreshView) {
+ return PagerAdapter.POSITION_NONE;
+ }
+ return watchMemberCount == watchMembers.size() ? PagerAdapter.POSITION_UNCHANGED :
+ PagerAdapter.POSITION_NONE;
+ case MEMBER_AUDIO:
+ return PagerAdapter.POSITION_UNCHANGED;
+ }
+ return PagerAdapter.POSITION_NONE;
+ }
+ return PagerAdapter.POSITION_NONE;
+ }
+
+ @Override
+ public int getCount() {
+ if (isAuxData) {
+ return 2 + mPages;
+ } else {
+ return 1 + mPages;
+ }
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ super.notifyDataSetChanged();
+ }
+
+ /**
+ * 大画面刷新后同步watchmember状态,防止被清空
+ *
+ * @param member
+ */
+ public void setWatchMember(Member member) {
+ this.watchMember = member;
+ }
+
+ @Override
+ public void destroyItem(ViewGroup container, int position, Object object) {
+ super.destroyItem(container, position, object);
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MyWindowManager.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MyWindowManager.java
new file mode 100644
index 0000000..d3d6462
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/MyWindowManager.java
@@ -0,0 +1,86 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+
+/**
+ * Created by shiwe on 2017/3/7.
+ * 悬浮窗管理
+ * 创建,移除
+ * 单例模式
+ */
+
+public class MyWindowManager {
+ private FloatNormalView normalView;
+
+ private static MyWindowManager instance;
+
+ private MyWindowManager() {
+ }
+
+ public static MyWindowManager getInstance() {
+ if (instance == null) {
+ instance = new MyWindowManager();
+ }
+ return instance;
+ }
+
+
+ /**
+ * 创建小型悬浮窗
+ */
+ public void createNormalView(Context context) {
+ if (normalView == null) {
+ normalView = new FloatNormalView(context);
+ }
+ }
+
+ /**
+ * 获取悬浮窗对象
+ * @return
+ */
+ public FrameLayout getFloatView(){
+ return normalView.getChiidView();
+ }
+
+ /**
+ * 获取悬浮窗父布局
+ * @return
+ */
+ public FrameLayout getRootView(){
+ return normalView.getRootView();
+
+ }
+
+ public void reflushFloatWindow(boolean b,boolean popShow,int height ){
+ try {
+ normalView.refreshFloatPos(b,popShow,height);
+ }catch (NullPointerException e){
+ LogUtil.e(LogUtil.CLOUNDLINK,"normalView is null"+e.getMessage());
+ }
+ }
+ public void postPoneRefreshFloatWindow(boolean b,boolean popShow,int height ){
+ try {
+ normalView.postPoneRefreshFloatWindow(b,popShow,height);
+ }catch (NullPointerException e){
+ LogUtil.e(LogUtil.CLOUNDLINK,"normalView is null"+e.getMessage());
+ }
+ }
+ /**
+ * 移除悬浮窗
+ *
+ * @param context
+ */
+ public void removeNormalView(Context context) {
+ if (normalView != null) {
+ WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ windowManager.removeView(normalView);
+ normalView = null;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/NullMenuEditText.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/NullMenuEditText.java
new file mode 100644
index 0000000..e63884b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/NullMenuEditText.java
@@ -0,0 +1,188 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.method.HideReturnsTransformationMethod;
+import android.text.method.PasswordTransformationMethod;
+import android.util.AttributeSet;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.TextView;
+
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import java.lang.reflect.Field;
+
+import androidx.appcompat.widget.AppCompatEditText;
+import androidx.core.content.ContextCompat;
+
+/**
+ * @Time: 2021/7/13
+ * @Author: isoftstone
+ * @Description: 禁止复制可以粘贴功能的EditText(用于密码)
+ * 1.禁止复制;
+ * 2.禁止文本选中;
+ * 3.处理横屏;
+ * 4.用户选择操作无效化处理
+ * 5.处理小米/OPPO手机(反射 android.widget.Editor 修改弹框菜单不显示);
+ * 6.右侧按钮按钮实现密码明文显示(用于密码)
+ */
+public class NullMenuEditText extends AppCompatEditText implements View.OnFocusChangeListener {
+ /**
+ * 右侧按钮的引用
+ */
+ private Drawable mRightDrawable;
+ private Context mCt;
+ /**
+ * 密码是否明文
+ */
+ private boolean isDisplayPasswordFlag = false;
+ private static String TAG = "NullMenuEditText";
+
+
+ public NullMenuEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mCt = context;
+ // 禁止长按
+ setLongClickable(true);
+ // 禁止文本选中
+ setTextIsSelectable(true);
+ // EditText在横屏的时候会出现一个新的编辑界面,因此要禁止掉这个新的编辑界面;
+ // 新的编辑界面里有复制粘贴等功能按钮,目前测试是无效果的,以防外一,建议禁止掉。
+ // setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+ // 用户选择操作无效化处理
+ setCustomSelectionActionModeCallback(new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ for (int i = 0; i < menu.size(); i++) {
+ try {
+ MenuItem menuItem = menu.getItem(i);
+ int itemId = menuItem.getItemId();
+ if (itemId != android.R.id.paste) {
+ menu.removeItem(itemId);
+ }
+ } catch (IndexOutOfBoundsException e) {
+ LogUtil.zzz(TAG, "onCreateActionMode()", "menuItem is IndexOutOfBoundsException");
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ }
+ });
+ this.setOnFocusChangeListener(this);
+ }
+
+ /**
+ * 因为我们不能直接给EditText设置点击事件,所以我们用记住我们按下的位置来模拟点击事件 当我们按下的位置 在 EditText的宽度 -
+ * 图标到控件右边的间距 - 图标的宽度 和 EditText的宽度 - 图标到控件右边的间距之间我们就算点击了图标,竖直方向没有考虑
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ setInsertionDisabled();
+ break;
+ case MotionEvent.ACTION_UP:
+ if (getCompoundDrawables()[2] != null) {
+ boolean touchable =
+ event.getX() > (getWidth() - getPaddingRight() - mRightDrawable.getIntrinsicWidth())
+ && (event.getX() < ((getWidth() - getPaddingRight())));
+ if (touchable) {
+ setIsDisplayPasswordImageView(!isDisplayPasswordFlag);
+ isDisplayPasswordFlag = !isDisplayPasswordFlag;
+ setSelection(length());
+ return true;
+ }
+ }
+ break;
+ }
+ return super.onTouchEvent(event);
+ }
+
+ public void setIsDisplayPasswordImageView(boolean flag) {
+ if (flag) {
+ setRightDrawable(R.drawable.ic_eye);
+ this.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
+ } else {
+ setRightDrawable(R.drawable.ic_password_not_view);
+ this.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ }
+ }
+
+ /**
+ * 小米/OPPO手机上禁止复制功能
+ * 反射 android.widget.Editor 修改弹框菜单不显示
+ */
+ private void setInsertionDisabled() {
+ try {
+ Field editorField = TextView.class.getDeclaredField("mEditor");
+ editorField.setAccessible(true);
+ Object editorObject = editorField.get(this);
+ Class editorClass = Class.forName("android.widget.Editor");
+// Field mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled");
+// mInsertionControllerEnabledField.setAccessible(true);
+// mInsertionControllerEnabledField.set(editorObject, false);
+ Field mSelectionControllerEnabledField = editorClass.getDeclaredField("mSelectionControllerEnabled");
+ mSelectionControllerEnabledField.setAccessible(true);
+ mSelectionControllerEnabledField.set(editorObject, false);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public boolean onTextContextMenuItem(int id) {
+ LogUtil.zzz(TAG, "onTextContextMenuItem");
+ if (id == android.R.id.copy || id == android.R.id.copyUrl
+ || id == android.R.id.cut || id == android.R.id.selectAll) {
+ return true;
+ } else {
+ return super.onTextContextMenuItem(id);
+ }
+ }
+
+ @Override
+ public boolean isSuggestionsEnabled() {
+ return false;
+ }
+
+ /**
+ * 设置右侧图标的显示与隐藏,调用setCompoundDrawables为EditText绘制上去
+ */
+ private void setRightDrawable(int drawable) {
+ if (mRightDrawable != null) {
+ mRightDrawable = null;
+ }
+ mRightDrawable = ContextCompat.getDrawable(mCt, drawable);
+ mRightDrawable.setBounds(0, 0, mRightDrawable.getIntrinsicWidth(), mRightDrawable.getIntrinsicHeight());
+ setCompoundDrawables(getCompoundDrawables()[0], getCompoundDrawables()[1], mRightDrawable,
+ getCompoundDrawables()[3]);
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ // 光标位置定位到最后
+ setSelection(length());
+ }
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragment.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragment.java
new file mode 100644
index 0000000..5f80f86
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragment.java
@@ -0,0 +1,679 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.annotation.SuppressLint;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+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.FrameLayout;
+import android.widget.TextView;
+
+import com.huawei.ecterminalsdk.base.TsdkWatchSvcAttendees;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.R2;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo;
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver;
+import com.tengshisoft.chatmodule.hwclud.utils.ListTools;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Platform;
+import com.tengshisoft.chatmodule.hwclud.utils.ScreenUtil;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.BroadcastConstant;
+import com.tenlionsoft.baselib.core.beans.Member;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+import androidx.annotation.RequiresApi;
+import butterknife.BindView;
+
+/**
+ * @Time: 2021/9/01
+ * @Author: isoftstone
+ * @Description: 远程视频类
+ */
+@SuppressLint("ValidFragment")
+public class RemoteFragment extends LazyloadFragment implements RemoteFragmentContract.RemoteFragmentView,
+ IMeetingBottomChanged {
+
+ @BindView(R2.id.remote_video)
+ FrameLayout remoteVideo;
+ @BindView(R2.id.tvSVCMeetingName)
+ TextView tvSvcMeetingName;
+ @BindView(R2.id.local_big_video)
+ FrameLayout localBigVideo;
+
+ public static final int REFRESH_SC_STATE = 168;
+ private static final String TAG = "RemoteFragment";
+ public static final int UPDATE_VIDEO = 100;
+ public static final int SVC_REFRESH = 110;
+ public static final int WATCH_ATTENDEE = 120;
+ public static final int WATCH_ATTENDEE_FROM_NA = 130;
+ public static final int WHO_SAY = 119;
+ public static final int BAND_WATCH = 150;
+ public static final int BROAD_CANCEL = 160;
+ public static final int REFRESH_WATCH_ATTENDEE = 170;
+ private RemoteFragmentPresenter mPresenter;
+ private int mOrientation = 2;
+ private boolean isSvc;
+ private boolean hasAux = false;
+ private MyTsdkCallInfo mTsdkCallInfo;
+ private int lastWatchResult = 0;
+ /**
+ * 带宽计算的分辨率
+ */
+ private int[] bandVideoSize;
+ /**
+ * 选看-广播-点名的与会者
+ */
+ private Member member;
+ /**
+ * 语音激励与会者
+ */
+ private Member voiceMember;
+ private List mMemberList;
+ private boolean isAvailable = false;
+ private UiHandler uiHandler;
+ private String[] broadcastNames = new String[]{BroadcastConstant.ACTION_ADD_LOCAL_VIEW,
+ BroadcastConstant.ACTION_REFRESH_SVC_VIEW, BroadcastConstant.ACTION_HWCLOUDLINK_WATCH_ATTENDEE,
+ BroadcastConstant.ACTION_SPONSOR_MEETING_REFLUSHED, BroadcastConstant.ACTION_SPEAKER_LIST_IND,
+ BroadcastConstant.ACTION_BAND_CHANGED_WATCH, BroadcastConstant.ACTION_FLOAT_VISIBILITY,
+ BroadcastConstant.ACTION_SPONSOR_MEETING_BROAD_MEMBER_CANCEL, BroadcastConstant.ACTION_CONF_STATE_UPDATE,
+ BroadcastConstant.ACTION_SCCHANGEREFRESH,BroadcastConstant.ACTION_SESSION_MODIFIED_REFRESH};
+
+ public static class UiHandler extends Handler {
+
+ private WeakReference reference;
+
+ public UiHandler(RemoteFragment fragment) {
+ super(Looper.getMainLooper());
+ reference = new WeakReference<>(fragment);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void handleMessage(Message msg) {
+ super.handleMessage(msg);
+ LogUtil.zzz(TAG, "handleMessage: " + msg);
+ if (reference != null && reference.get() != null) {
+ switch (msg.what) {
+ case UPDATE_VIDEO:
+ // 根据会议类型更新视频视图
+ if (!reference.get().isSvc) {
+ if (null != reference.get().remoteVideo) {
+ reference.get().setAVCVideoContainer();
+ }
+ } else {
+ reference.get().setSVCVideoContainer();
+ }
+ break;
+ case SVC_REFRESH:
+ // 更新Svc视频视图
+ if (reference.get().isSvc) {
+ if (reference.get().remoteVideo != null && reference.get().tvSvcMeetingName != null) {
+ if (reference.get().remoteVideo.getVisibility() == View.GONE) {
+ reference.get().remoteVideo.setVisibility(View.VISIBLE);
+ }
+ List watchMembers = MeetingMgrV2.getInstance().getWatchMember();
+ if (ListTools.isThan(watchMembers,0)){
+ reference.get().tvSvcMeetingName.setText(watchMembers.get(0).getDisplayName());
+ }
+ }
+ }
+ break;
+ case WATCH_ATTENDEE:
+ // 选看会场
+ if (reference.get().isSvc) {
+ LogUtil.zzz(TAG, "WATCH_ATTENDEE isSvc");
+ reference.get().setSVCVideoContainer();
+ reference.get().svcWatchModel(reference.get().member);
+ } else {
+ LogUtil.zzz(TAG, "WATCH_ATTENDEE isAVC");
+ reference.get().setAVCVideoContainer();
+ MeetingMgrV2.getInstance().watchAttendee(reference.get().member);
+ }
+ break;
+
+ case WATCH_ATTENDEE_FROM_NA:
+ // 与会者列表选看会场
+ LogUtil.zzz(TAG, "WATCH_ATTENDEE_FROM_NA");
+ if (reference.get().isSvc) {
+ reference.get().setSVCVideoContainer();
+ reference.get().svcWatchModel(reference.get().member);
+ }
+ break;
+ case WHO_SAY:
+ // 语音激励
+ speakerListInd();
+ break;
+ case BAND_WATCH:
+ // 会议带宽变化
+ bandWatchChange();
+ break;
+ case BROAD_CANCEL:
+ reference.get().member = null;
+ break;
+ // 会话类型修改后,刷新窗口选看
+ case REFRESH_WATCH_ATTENDEE:
+ case REFRESH_SC_STATE:
+ // SC转换后画面卡住
+ scChangeRefresh();
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ /**
+ * SC转换后画面卡住 REFRESH_SC_STATE
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private void scChangeRefresh() {
+ if (!reference.get().isSvc) {
+ LogUtil.zzz(TAG, "REFRESH_SC_STATE is not SVC");
+ return;
+ }
+ if (reference.get().member == null) {
+ reference.get().member = reference.get().getWatchMember();
+ }
+ Member tempMember2;
+ if (reference.get().member == null) {
+ tempMember2 = MeetingController.getInstance().getVoiceWatchMember();
+ } else {
+ tempMember2 = reference.get().member;
+ MeetingController.getInstance().setVoiceWatchMember(null);
+ }
+ reference.get().svcWatchModel(tempMember2);
+ }
+
+ /**
+ * 会议带宽变化
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private void bandWatchChange() {
+ int[] videoSize = MeetingController.getInstance().getVideoSize(1);
+ if (reference.get().bandVideoSize != null && reference.get().bandVideoSize[0] == videoSize[0]) {
+ LogUtil.zzz(TAG, "BAND_WATCH", "the same resolution");
+ return;
+ }
+ reference.get().bandVideoSize = videoSize;
+ if (reference.get().member == null) {
+ reference.get().member = reference.get().getWatchMember();
+ }
+ Member tempMember;
+ if (reference.get().member == null) {
+ tempMember = MeetingController.getInstance().getVoiceWatchMember();
+ } else {
+ tempMember = reference.get().member;
+ MeetingController.getInstance().setVoiceWatchMember(null);
+ }
+ if (tempMember != null) {
+ LogUtil.zzz(TAG, "sc_change_BAND_WATCH:", "name is " + tempMember.getDisplayName());
+ } else {
+ LogUtil.zzz(TAG, "sc_change_BAND_WATCH:", "tempMember is null");
+ }
+ }
+
+ /**
+ * 语音激励 WHO_SAY
+ */
+ private void speakerListInd() {
+ if (reference.get().isSvc && reference.get().member == null) {
+ if (MeetingController.getInstance().isSvcBigConf() && !MeetingController.getInstance().isChairman()) {
+ LogUtil.zzz(TAG, "Voice SpeakMember Big is not Chairman");
+ return;
+ }
+ Member member = MeetingMgrV2.getInstance().getSpeakMember();
+ if (member == null) {
+ LogUtil.zzz(TAG, "Voice SpeakMember member null");
+ return;
+ }
+ if (TextUtils.isEmpty(member.getNumber())) {
+ return;
+ }
+ List memberList = MeetingMgrV2.getInstance().getCurrentConferenceMemberList();
+ if (memberList != null) {
+ for (Member m : memberList) {
+ if (m.getNumber().equals(member.getNumber())) {
+ if (m.isSelf()) {
+ LogUtil.zzz(TAG, "Voice SpeakMember watched self");
+ return;
+ }
+ }
+ }
+ }
+ if (reference.get().voiceMember == null || !member.getNumber().equals(reference.get().voiceMember.getNumber())) {
+ reference.get().voiceMember = member;
+ reference.get().watchVoiceSvcAttendees(member);
+ }
+ }
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void onResume() {
+ super.onResume();
+ if (isAvailable) {
+ if (isSvc) {
+ setSVCVideoContainer();
+ } else {
+ setAVCVideoContainer();
+ }
+ }
+ }
+
+ private LocalBroadcastReceiver receiver = new LocalBroadcastReceiver() {
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ public void onReceive(String broadcastName, Object obj) {
+ if (uiHandler == null) {
+ return;
+ }
+ switch (broadcastName) {
+ case BroadcastConstant.ACTION_FLOAT_VISIBILITY:
+ Platform.runUi(() -> {
+ mPresenter.setSVCVideoContainer(null, getFloatWindow());
+ });
+ break;
+ case BroadcastConstant.ACTION_CONF_STATE_UPDATE:
+ case BroadcastConstant.ACTION_ADD_LOCAL_VIEW:
+ Message message1 = uiHandler.obtainMessage();
+ message1.what = UPDATE_VIDEO;
+ uiHandler.sendMessage(message1);
+ break;
+ case BroadcastConstant.ACTION_REFRESH_SVC_VIEW:
+ Message message2 = uiHandler.obtainMessage();
+ message2.what = SVC_REFRESH;
+ uiHandler.sendMessage(message2);
+ break;
+ case BroadcastConstant.ACTION_HWCLOUDLINK_WATCH_ATTENDEE:
+ Message message3 = uiHandler.obtainMessage();
+ message3.what = WATCH_ATTENDEE;
+ if (obj != null) {
+ member = (Member) obj;
+ } else {
+ member = null;
+ }
+ uiHandler.sendMessage(message3);
+ break;
+ case BroadcastConstant.ACTION_SPONSOR_MEETING_REFLUSHED:
+ Message message4 = uiHandler.obtainMessage();
+ message4.what = WATCH_ATTENDEE_FROM_NA;
+ if (obj != null) {
+ member = (Member) obj;
+ } else {
+ member = null;
+ }
+ uiHandler.sendMessage(message4);
+ break;
+ case BroadcastConstant.ACTION_SPEAKER_LIST_IND:
+ Message message5 = uiHandler.obtainMessage();
+ message5.what = WHO_SAY;
+ uiHandler.sendMessage(message5);
+ break;
+ case BroadcastConstant.ACTION_BAND_CHANGED_WATCH:
+ Message message6 = uiHandler.obtainMessage();
+ message6.what = BAND_WATCH;
+ uiHandler.sendMessage(message6);
+ break;
+ case BroadcastConstant.ACTION_SPONSOR_MEETING_BROAD_MEMBER_CANCEL:
+ Message message7 = uiHandler.obtainMessage();
+ message7.what = BROAD_CANCEL;
+ uiHandler.sendMessage(message7);
+ break;
+ case BroadcastConstant.ACTION_SCCHANGEREFRESH:
+ Message message8 = uiHandler.obtainMessage();
+ message8.what = REFRESH_SC_STATE;
+ uiHandler.sendMessage(message8);
+ break;
+ case BroadcastConstant.ACTION_SESSION_MODIFIED_REFRESH:
+ Message message9 = uiHandler.obtainMessage();
+ message9.what = REFRESH_WATCH_ATTENDEE;
+ uiHandler.sendMessage(message9);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ public RemoteFragment() {
+
+ }
+
+ /**
+ * @param mIsSvc 是否是SVC会议
+ * @param myTsdkCallInfo TsdkCallInfo
+ * @param watchMember 是否有选看 ? 点名? 广播
+ * @param memberList 与会者列表 由于SVC大画面一个人时,直接获取时返回2个人,用这个确定是一个人
+ * @param mHasAux 是否辅流
+ */
+ @SuppressLint("ValidFragment")
+ public RemoteFragment(Boolean mIsSvc, MyTsdkCallInfo myTsdkCallInfo, Member watchMember, List memberList,
+ boolean mHasAux) {
+ this.isSvc = mIsSvc;
+ mTsdkCallInfo = myTsdkCallInfo;
+ member = watchMember;
+ mMemberList = memberList;
+ this.hasAux = mHasAux;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ MeetingController.getInstance().registerBottomChanged(this);
+ return super.onCreateView(inflater, container, savedInstanceState);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void refreshParams(Boolean mIsSvc, MyTsdkCallInfo myTsdkCallInfo, Member watchMember,
+ List memberList, boolean mHasAux) {
+ //此处mMemberList.size() 会存在空指针情况
+ if (mMemberList == null) {
+ LogUtil.zzz(TAG, "refreshParams mMemberList is null");
+ return;
+ }
+ LogUtil.zzz(TAG,
+ "refreshParams mMemberList size =" + mMemberList.size() + " memberList.size == " + memberList.size());
+ this.isSvc = mIsSvc;
+ this.mTsdkCallInfo = myTsdkCallInfo;
+ this.member = watchMember;
+ this.hasAux = mHasAux;
+ if (mMemberList.size() == 1) {
+ /**
+ * 因为当只有一个人的时候默认选看的是自己,当有人进来重新选看下,会选看到对方
+ */
+ this.mMemberList = memberList;
+ lazyLoad();
+ } else {
+ if (mMemberList.size() != memberList.size() && getUserVisibleHint()) {
+ int userId = MeetingMgrV2.getInstance().getWatchSingleUserId(member);
+ if (userId == 0) {
+ LogUtil.zzz(TAG, "refreshParams big watch fail number=" + member + " userId = " + userId);
+ this.mMemberList = memberList;
+ return;
+ }
+ // 大画面选看
+ if (MeetingController.getInstance().getSvcWatchStatus() == SvcWatchStatus.PIP_WATCH
+ && MeetingMgrV2.getInstance().getWatchMember().size() == 1
+ && (MeetingMgrV2.getInstance().getWatchMember().get(0).getUserId() == userId)
+ && lastWatchResult == 0) {
+ LogUtil.zzz(TAG, "refreshParams big watch cancel number=" + member + " userId = " + userId);
+ this.mMemberList = memberList;
+ // 针对重复选看长时间黑屏,重新绑定窗口
+ if (isSvc) {
+ setSVCVideoContainer();
+ } else {
+ setAVCVideoContainer();
+ }
+ return;
+ }
+ lazyLoad();
+ }
+ this.mMemberList = memberList;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void setAVCVideoContainer() {
+ if (isAdded()) {
+ mPresenter.setAVCVideoContainer(remoteVideo, getFloatWindow(), localBigVideo);
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void setSVCVideoContainer() {
+ if (isAdded()) {
+ mPresenter.setSVCVideoContainer(remoteVideo, getFloatWindow());
+ }
+ }
+
+ public List getMemberList() {
+ return this.mMemberList;
+ }
+
+ @Override
+ protected int setContentView() {
+ return R.layout.fragment_remote;
+ }
+
+ @Override
+ protected void getData() {
+
+ }
+
+ @Override
+ protected void initView() {
+ mPresenter = new RemoteFragmentPresenter();
+ uiHandler = new UiHandler(this);
+ Configuration configuration = this.getResources().getConfiguration();
+ mOrientation = configuration.orientation;
+ mPresenter.setAutoRotation(remoteVideo, true, mOrientation);
+ VideoMgr.getInstance().changeRotation(1);
+ if (tvSvcMeetingName != null) {
+ tvSvcMeetingName.setText("");
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ protected void initListener() {
+ // 注:此处点击监听为自定义
+ remoteVideo.setOnClickListener(view -> {
+ if (getActivity() instanceof SponsorMeetingActivity) {
+ ((SponsorMeetingActivity) getActivity()).showLabel();
+ }
+ });
+
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ @Override
+ protected void lazyLoad() {
+ if (!getUserVisibleHint()) {
+ return;
+ }
+ if (getActivity() != null && getActivity() instanceof SponsorMeetingActivity) {
+ ((SponsorMeetingActivity) getActivity()).setRemote(true);
+ }
+ if (isSvc) {
+ LogUtil.zzz(TAG, "lazyLoad isSvc:");
+ if (member == null) {
+ member = getWatchMember();
+ }
+ Member tempMember;
+ if (member == null) {
+ tempMember = MeetingController.getInstance().getVoiceWatchMember();
+ } else {
+ tempMember = member;
+ MeetingController.getInstance().setVoiceWatchMember(null);
+ }
+ svcWatchModel(tempMember);
+ } else {
+ setAVCVideoContainer();
+ if (hasAux && !MeetingController.getInstance().isBroadMember()) {
+ if (member == null) {
+ member = getWatchMember() == null ? MeetingController.getInstance().getWatchingMember() :
+ getWatchMember();
+ }
+ LogUtil.zzz(TAG, "lazyLoad isAvc");
+ MeetingMgrV2.getInstance().watchAttendee(member);
+ }
+ }
+ }
+
+ @Override
+ public void setUserVisibleHint(boolean isVisibleToUser) {
+ super.setUserVisibleHint(isVisibleToUser);
+ isAvailable = isVisibleToUser;
+ if (isVisibleToUser) {
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames);
+ } else {
+ member = null;
+ VideoMgr.getInstance().removeAllSvcVideoWindow();
+ if (mPresenter != null) {
+ mPresenter.removeSvcVideo();
+ }
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private void svcWatchModel(Member member) {
+ Member cacheMember = null;
+ if (mMemberList == null) {
+ LogUtil.zzz(TAG, "svcWatchModel mMemberList is null");
+ return;
+ }
+ if (member != null) {
+ LogUtil.zzz(TAG, "svcWatchModel userid = " + member.getUserId());
+ // 如果存在广播,不与本地缓存对象对比
+ if (!MeetingController.getInstance().isBroadMember()) {
+ Member acheMember = BaseAppContext.getInstance().getMember();
+ if (acheMember != null && !member.getNumber().equals(acheMember.getNumber())) {
+ cacheMember = acheMember;
+ }
+ }
+ if (member.getUserId() == 0) {
+ return;
+ }
+ } else {
+ if (BaseAppContext.getInstance().getMember() != null) {
+ cacheMember = BaseAppContext.getInstance().getMember();
+ }
+ }
+ setSVCVideoContainer();
+ int userId;
+ if (cacheMember != null) {
+ userId = cacheMember.getUserId();
+ LogUtil.zzz(TAG, "svcWatchModel cacheMember userId = " + userId);
+ } else {
+ userId = MeetingMgrV2.getInstance().getWatchSingleUserId(member);
+ }
+
+ if (userId == 0) {
+ LogUtil.zzz(TAG, "svcWatchModel user id = " + userId);
+ return;
+ }
+
+ watchSvcAttendees(MeetingController.getInstance().getSSrc(mTsdkCallInfo), userId);
+ }
+
+ private void watchSvcAttendees(int sSrc, int userId) {
+ LogUtil.zzz(TAG, "watchSvcAttendees userId = " + userId);
+ voiceMember = null;
+ TsdkWatchSvcAttendees watchSvcAttendees = new TsdkWatchSvcAttendees();
+ MeetingController.getInstance().setSvcWatchStatus(SvcWatchStatus.PIP_WATCH);
+ int[] videoSize = MeetingController.getInstance().getVideoSize(1);
+ watchSvcAttendees.setWidth(videoSize[0]);
+ watchSvcAttendees.setHeight(videoSize[1]);
+ watchSvcAttendees.setLableId(sSrc);
+ lastWatchResult = MeetingMgrV2.getInstance().watchSingleSvcAttendee(watchSvcAttendees, userId);
+ if (0 != lastWatchResult) {
+ BaseAppContext.getInstance().setMember(null);
+ LogUtil.zzz(TAG, "watchSvcAttendees fail result : " + lastWatchResult);
+ }
+ }
+
+ private void watchVoiceSvcAttendees(Member member) {
+ int ssrcTableStart = MeetingController.getInstance().getSSrc(mTsdkCallInfo);
+ TsdkWatchSvcAttendees watchSvcAttendees = new TsdkWatchSvcAttendees();
+ MeetingController.getInstance().setSvcWatchStatus(SvcWatchStatus.PIP_WATCH);
+ MeetingController.getInstance().setVoiceWatchMember(member);
+ int[] videoSize = MeetingController.getInstance().getVideoSize(1);
+ watchSvcAttendees.setWidth(videoSize[0]);
+ watchSvcAttendees.setHeight(videoSize[1]);
+ watchSvcAttendees.setLableId(ssrcTableStart);
+ int result = MeetingMgrV2.getInstance().watchSVCVoiceAttendee(watchSvcAttendees, member);
+ if (0 != result) {
+ LogUtil.zzz(TAG, "watchVoiceSvcAttendees fail result : " + result);
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ MeetingController.getInstance().unRegisterBottomChanged(this);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames);
+ try {
+ mPresenter.setAutoRotation(getContext(), false, mOrientation);
+ } catch (NullPointerException ignored) {
+ }
+ }
+
+ @Override
+ public void showNetworkError() {
+
+ }
+
+ @Override
+ public void showLoading() {
+
+ }
+
+ @Override
+ public void showLoading(String tip) {
+
+ }
+
+ @Override
+ public void hideLoading() {
+
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private FrameLayout getFloatWindow() {
+ FrameLayout floatWindow = null;
+ if (getActivity() instanceof SponsorMeetingActivity) {
+ floatWindow = ((SponsorMeetingActivity) getActivity()).getFloatWindow();
+ }
+ return floatWindow;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ private Member getWatchMember() {
+ Member member1 = null;
+ if (getActivity() instanceof SponsorMeetingActivity) {
+ member1 = ((SponsorMeetingActivity) getActivity()).getWatchMember();
+ }
+ return member1;
+ }
+
+ @Override
+ public void bottomChanged(int visibility) {
+ if (visibility == View.VISIBLE) {
+ setViewMarginBottom(tvSvcMeetingName, 56);
+ } else {
+ setViewMarginBottom(tvSvcMeetingName, 0);
+ }
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return isAvailable;
+ }
+
+ private void setViewMarginBottom(View view, int marginBottom) {
+ FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) view.getLayoutParams();
+ layoutParams.bottomMargin = ScreenUtil.dp2ps(requireContext(), marginBottom);
+ view.setLayoutParams(layoutParams);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragmentContract.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragmentContract.java
new file mode 100644
index 0000000..895731b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragmentContract.java
@@ -0,0 +1,42 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.ViewGroup;
+
+/**
+ * @Time: 2021/69/01
+ * @Author: isoftstone
+ * @Description: 远程视频接口类
+ */
+public interface RemoteFragmentContract {
+ interface RemoteFragmentView extends BaseContract.BaseView {
+
+ }
+
+ interface RemoteFragmentPresenter extends BaseContract.BasePresenter {
+ /**
+ * 填充Avc频视图
+ *
+ * @param remoteVideo 远程视频视图
+ * @param floatWindow 悬浮视频视图
+ * @param localBigVideo 本地视频视图
+ */
+ void setAVCVideoContainer(ViewGroup remoteVideo, ViewGroup floatWindow, ViewGroup localBigVideo);
+
+ /**
+ * 填充Svc视频视图
+ *
+ * @param confRemoteVideoA 远程视频视图A
+ * @param floatWindow 悬浮视频视图
+ */
+ void setSVCVideoContainer(ViewGroup confRemoteVideoA, ViewGroup floatWindow);
+
+ /**
+ * 设置视频自动旋转
+ *
+ * @param object 调用者对象
+ * @param isOpen 是否开启方向调整
+ * @param mOrientation 是否开启方向调整
+ */
+ void setAutoRotation(Object object, boolean isOpen, int mOrientation);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragmentPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragmentPresenter.java
new file mode 100644
index 0000000..0c68b20
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/RemoteFragmentPresenter.java
@@ -0,0 +1,129 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+
+/**
+ * @Time: 2021/9/01
+ * @Author: isoftstone
+ * @Description: 远程视频实现类
+ */
+public class RemoteFragmentPresenter implements RemoteFragmentContract.RemoteFragmentPresenter {
+ private static final String TAG = RemoteFragmentPresenter.class.getSimpleName();
+
+ @Override
+ public void setAVCVideoContainer(ViewGroup remoteVideo, ViewGroup floatWindow, ViewGroup localBigVideo) {
+ LogUtil.zzz(TAG, "floatWindow = " + floatWindow);
+ if (MeetingController.getInstance().isConf) {
+ if (remoteVideo != null) {
+ if (!(MeetingController.getInstance().conferenceRight || MeetingController.getInstance().isHasMember)) {
+ LogUtil.zzz(TAG, "setAVCVideoContainer local");
+ localBigVideo.setVisibility(View.VISIBLE);
+ addSurfaceView(localBigVideo, getLocalVideoView());
+ } else {
+ LogUtil.zzz(TAG, "setAVCVideoContainer remote");
+ localBigVideo.removeAllViews();
+ localBigVideo.setVisibility(View.GONE);
+ addSurfaceView(remoteVideo, getRemoteVideoView());
+ if (floatWindow != null) {
+ if (floatWindow.getVisibility() == View.VISIBLE) {
+ addSurfaceView(floatWindow, getLocalVideoView());
+ }
+ }
+ }
+ }
+ } else {
+ if (remoteVideo != null) {
+ addSurfaceView(remoteVideo, getRemoteVideoView());
+ }
+ if (floatWindow != null) {
+ if (floatWindow.getVisibility() == View.VISIBLE) {
+ addSurfaceView(floatWindow, getLocalVideoView());
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setSVCVideoContainer(ViewGroup confRemoteVideoA, ViewGroup floatWindow) {
+ LogUtil.zzz(TAG, "floatWindow = " + floatWindow);
+ if (floatWindow != null) {
+ if (floatWindow.getVisibility() == View.VISIBLE) {
+ addSurfaceView(floatWindow, getLocalVideoView());
+ }
+ }
+ if (confRemoteVideoA != null) {
+ if (!(MeetingController.getInstance().conferenceRight || MeetingController.getInstance().isHasMember)) {
+ LogUtil.zzz(TAG, "setSVCVideoContainer local");
+ addSurfaceView(confRemoteVideoA, getLocalVideoView());
+ } else {
+ LogUtil.zzz(TAG, "setSVCVideoContainer remote");
+ addSurfaceView(confRemoteVideoA, VideoMgr.getInstance().getSvcBigView());
+ }
+ }
+ }
+
+ public void setSVCVideoContainer(ViewGroup floatWindow) {
+ if (floatWindow != null) {
+ addSurfaceView(floatWindow, getLocalVideoView());
+ }
+ }
+
+ public void removeSvcVideo() {
+ removeSurfaceView(VideoMgr.getInstance().getSvcBigView());
+ }
+
+ @Override
+ public void setAutoRotation(Object object, boolean isOpen, int mOrientation) {
+ VideoMgr.getInstance().setAutoRotation(object, isOpen, mOrientation);
+ }
+
+ public SurfaceView getLocalVideoView() {
+ return VideoMgr.getInstance().getLocalVideoView();
+ }
+
+ public SurfaceView getRemoteVideoView() {
+ return VideoMgr.getInstance().getRemoteVideoView();
+ }
+
+ private void addSurfaceView(ViewGroup container, SurfaceView child) {
+ if (child == null) {
+ LogUtil.zzz(TAG, "child == null");
+ return;
+ }
+ if (child.getParent() != null) {
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ if (container == vGroup) {
+ LogUtil.zzz(TAG, "container == vGroup");
+ return;
+ }
+ vGroup.removeAllViews();
+ }
+ if (container == null) {
+ LogUtil.zzz(TAG, "container == null");
+ return;
+ }
+ container.addView(child);
+ }
+
+ public void removeSurfaceView(SurfaceView child) {
+ if (child == null) {
+ return;
+ }
+ if (child.getParent() != null) {
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ vGroup.removeAllViews();
+ }
+ }
+
+ @Override
+ public void detachView() {
+ LogUtil.zzz(TAG, "detachView");
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SVCVideoFragmentContract.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SVCVideoFragmentContract.java
new file mode 100644
index 0000000..5a2f09a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SVCVideoFragmentContract.java
@@ -0,0 +1,54 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+
+/**
+ * @Time: 2021/8/30
+ * @Author: isoftstone
+ * @Description: svc视频接口类
+ */
+public interface SVCVideoFragmentContract {
+ interface SVCVideoFragmentView extends BaseContract.BaseView {
+
+ }
+
+ interface SVCVideoFragmentPresenter extends BaseContract.BasePresenter {
+ /**
+ * 填充视频窗口
+ *
+ * @param floatWindow 本地视频小悬浮框
+ * @param confRemoteVideoA 远程视频A窗口
+ * @param confRemoteVideoB 远程视频B窗口
+ * @param confRemoteVideoC 远程视频C窗口
+ * @param confRemoteVideoD 远程视频D窗口
+ */
+ void setVideoContainer(ViewGroup floatWindow, ViewGroup confRemoteVideoA, ViewGroup confRemoteVideoB,
+ ViewGroup confRemoteVideoC, ViewGroup confRemoteVideoD);
+
+ /**
+ * 添加Video
+ *
+ * @param confRemoteVideoA 远程视频A窗口
+ */
+ void setVideoContainer(ViewGroup confRemoteVideoA);
+
+ /**
+ * 设置视频自动旋转
+ *
+ * @param object 调用者对象
+ * @param isOpen 是否开启方向调整
+ * @param mOrientation 是否开启方向调整
+ */
+ void setAutoRotation(Object object, boolean isOpen, int mOrientation);
+
+ /**
+ * 填充Surface
+ *
+ * @param container 父类view
+ * @param child 子类view
+ */
+ void addSurfaceView(ViewGroup container, SurfaceView child);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SVCVideoFragmentPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SVCVideoFragmentPresenter.java
new file mode 100644
index 0000000..ea61d8a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SVCVideoFragmentPresenter.java
@@ -0,0 +1,133 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+
+
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import java.util.ArrayList;
+
+/**
+ * @Time: 2021/8/30
+ * @Author: isoftstone
+ * @Description: svc视频会议控制类
+ */
+public class SVCVideoFragmentPresenter implements SVCVideoFragmentContract.SVCVideoFragmentPresenter {
+
+ private static final String TAG = SVCVideoFragmentPresenter.class.getSimpleName();
+ /**
+ * SVC视频对应显示视图
+ */
+ private static final int SVC_VIDEO_B = 0;
+ private static final int SVC_VIDEO_C = 1;
+ private static final int SVC_VIDEO_D = 2;
+
+ @Override
+ public void detachView() {
+ LogUtil.zzz(TAG, "detachView");
+ }
+
+ @Override
+ public void setVideoContainer(ViewGroup floatWindow, ViewGroup confRemoteVideoA, ViewGroup confRemoteVideoB,
+ ViewGroup confRemoteVideoC, ViewGroup confRemoteVideoD) {
+ ArrayList svcRemoteVideoView = getSvcRemoteVideoView();
+ if (confRemoteVideoA != null) {
+ addSurfaceView(confRemoteVideoA, VideoMgr.getInstance().getLocalVideoView());
+ VideoMgr.getInstance().setLocGroup(confRemoteVideoA);
+ }
+ if (svcRemoteVideoView != null && svcRemoteVideoView.size() > 0) {
+ for (int i = 0; i < svcRemoteVideoView.size(); i++) {
+ switch (i) {
+ case SVC_VIDEO_B:
+ addSurfaceView(confRemoteVideoB, svcRemoteVideoView.get(i));
+ break;
+ case SVC_VIDEO_C:
+ addSurfaceView(confRemoteVideoC, svcRemoteVideoView.get(i));
+ break;
+ case SVC_VIDEO_D:
+ addSurfaceView(confRemoteVideoD, svcRemoteVideoView.get(i));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+
+ public void removeVideo(boolean isAdded) {
+ ArrayList svcRemoteVideoView = getSvcRemoteVideoView();
+ LogUtil.zzz(TAG, "removeVideo", isAdded);
+ VideoMgr.getInstance().clearLocGroup();
+ if (isAdded) {
+ removeSurfaceView(VideoMgr.getInstance().getLocalVideoView());
+ }
+ if (svcRemoteVideoView != null && svcRemoteVideoView.size() > 0) {
+ for (int i = 0; i < svcRemoteVideoView.size(); i++) {
+ removeSurfaceView(svcRemoteVideoView.get(i));
+ }
+ }
+ }
+
+ public void setVideoContainer(ViewGroup floatWindow, ViewGroup confRemoteVideoA) {
+ if (confRemoteVideoA != null) {
+ addSurfaceView(confRemoteVideoA, VideoMgr.getInstance().getLocalVideoView());
+ }
+ if (floatWindow != null) {
+ addSurfaceView(floatWindow, getLocalVideoView());
+ }
+ }
+
+ @Override
+ public void setVideoContainer(ViewGroup confRemoteVideoA) {
+ if (confRemoteVideoA != null) {
+ addSurfaceView(confRemoteVideoA, getLocalVideoView());
+ }
+ }
+
+ public void setVideoContainer(ViewGroup container, int index) {
+ addSurfaceView(container, getSvcRemoteVideoView().get(index));
+ }
+
+ @Override
+ public void setAutoRotation(Object object, boolean b, int mOrientation) {
+ VideoMgr.getInstance().setAutoRotation(object, b, mOrientation);
+ }
+
+ public SurfaceView getLocalVideoView() {
+ return VideoMgr.getInstance().getLocalVideoView();
+ }
+
+ public ArrayList getSvcRemoteVideoView() {
+ return VideoMgr.getInstance().getSvcViewList();
+ }
+
+ @Override
+ public void addSurfaceView(ViewGroup container, SurfaceView child) {
+ if (child == null) {
+ return;
+ }
+ if (child.getParent() != null) {
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ if (vGroup == container) {
+ return;
+ }
+ vGroup.removeAllViews();
+ }
+ if (container == null) {
+ return;
+ }
+ container.addView(child);
+ }
+
+ public void removeSurfaceView(SurfaceView child) {
+ if (child == null) {
+ return;
+ }
+ if (child.getParent() != null) {
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ vGroup.removeAllViews();
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ScaleHelper.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ScaleHelper.java
new file mode 100644
index 0000000..078acb1
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ScaleHelper.java
@@ -0,0 +1,79 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.animation.AnimationUtils;
+import android.view.animation.Interpolator;
+
+/**
+ * @Time: 2021/8/4
+ * @Author: isoftstone
+ * @Description:
+ */
+public class ScaleHelper {
+ private long mStartTime;
+ private Interpolator mInterpolator;
+ private float mScale;
+ private float mToScale;
+ private int mStartX;
+ private int mDuration;
+ private boolean mFinished = true;
+ private int mStartY;
+
+ public void startScale(float scale, float toScale, int x, int y, Interpolator interpolator) {
+ mStartTime = AnimationUtils.currentAnimationTimeMillis();
+ mInterpolator = interpolator;
+ mScale = scale;
+ mToScale = toScale;
+ mStartX = x;
+ mStartY = y;
+ float d;
+ if (toScale > scale) {
+ d = toScale / scale;
+ } else {
+ d = scale / toScale;
+ }
+ if (d > 4) {
+ d = 4;
+ }
+ //倍数差值越大 执行时间越久 280 - 340
+ mDuration = (int) (220 + Math.sqrt(d * 3600));
+ mFinished = false;
+ }
+
+ /**
+ * 更新位置时调用此方法,如果返回true,则动画尚未完成。
+ */
+ public boolean computeScrollOffset() {
+ if (isFinished()) {
+ return false;
+ }
+ long time = AnimationUtils.currentAnimationTimeMillis();
+
+ final long elapsedTime = time - mStartTime;
+
+ final int duration = mDuration;
+ if (elapsedTime < duration) {
+ final float q = mInterpolator.getInterpolation(elapsedTime / (float) duration);
+ mScale = mScale + q * (mToScale - mScale);
+ } else {
+ mScale = mToScale;
+ mFinished = true;
+ }
+ return true;
+ }
+
+ private boolean isFinished() {
+ return mFinished;
+ }
+
+ public float getCurScale() {
+ return mScale;
+ }
+
+ public int getStartX() {
+ return mStartX;
+ }
+
+ public int getStartY() {
+ return mStartY;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ScaleView.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ScaleView.java
new file mode 100644
index 0000000..4b4276f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ScaleView.java
@@ -0,0 +1,271 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import androidx.annotation.Nullable;
+
+
+/**
+ * @Time: 2021/7/12
+ * @Author: isoftstone
+ * @Description:
+ */
+public class ScaleView extends FrameLayout implements View.OnTouchListener {
+
+ // 是否开启缩放
+ private boolean isCanTouch = true;
+ // 当前触摸的点数
+ private int point_num = 0;
+ // 最大缩放比例
+ public static final float SCALE_MAX = 2.0f;
+ // 最小缩放比例
+ private static final float SCALE_MIN = 1.0f;
+ private double oldDist = 0;
+ private double moveDist = 0;
+ // 针对控件的坐标系,即控件左上角为原点
+ private double moveX = 0;
+ private double moveY = 0;
+
+ private double downX = 0;
+ private double downY = 0;
+ // 针对屏幕的坐标系,即屏幕左上角为原点
+ private double moveRawX = 0;
+ private double moveRawY = 0;
+
+ // 当前缩放比例
+ private float nowScale = 0;
+
+ public ScaleView(Context context) {
+ this(context, null);
+ }
+
+ public ScaleView(Context context, @Nullable AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public ScaleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setOnTouchListener(this);
+ }
+
+
+ private RectF videoRectF;
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = MeasureSpec.getSize(widthMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+ videoRectF = new RectF(0, 0, width, height);
+
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+
+ if (!isCanTouch) {
+ return false;
+ }
+
+ switch (event.getAction() & MotionEvent.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN:
+ point_num = 1;
+ downX = event.getX();
+ downY = event.getY();
+ if (getScaleX() > 1.0) {
+ getParent().requestDisallowInterceptTouchEvent(true);
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (point_num == 1) {
+ clickListener.onClick(this);
+ }
+ point_num = 0;
+ downX = 0;
+ downY = 0;
+
+ break;
+ case MotionEvent.ACTION_MOVE:
+// LogUtil.zzz("ScaleView","getScaleX:"+getScaleX());
+ if (point_num == 1) {
+ getParent().requestDisallowInterceptTouchEvent(getScaleX() > 1.0 ? true : false);
+ // 只有一个手指的时候才有移动的操作
+ float lessX = (float) (downX - event.getX());
+ float lessY = (float) (downY - event.getY());
+ moveX = event.getX();
+ moveY = event.getY();
+ moveRawX = event.getRawX();
+ moveRawY = event.getRawY();
+ setSelfPivot(lessX, lessY);
+ //setPivot(getPivotX() + lessX, getPivotY() + lessY);
+ } else if (point_num == 2) {
+ // 只有2个手指的时候才有放大缩小的操作
+ moveDist = spacing(event);
+ double space = moveDist - oldDist;
+// float scale = (float) (getScaleX() + space / getWidth());
+ nowScale = (float) (getScaleX() + space / getWidth());
+ LogUtil.zzz("scaleView scale", nowScale + "");
+
+ if (nowScale < 1.0) {
+ // 缩小时回弹效果
+ setPivot(videoRectF.right / 2, videoRectF.bottom / 2);
+ }
+ if (nowScale < 0.4) {
+ break;
+ }
+ setScale(nowScale);
+ /* if (scale > SCALE_MIN && scale < SCALE_MAX) {
+ setScale(scale);
+ } else if (scale < SCALE_MIN) {
+ setScale(SCALE_MIN);
+ }*/
+ }
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // 两点按下时的距离
+ oldDist = spacing(event);
+ point_num += 1;
+
+ break;
+ case MotionEvent.ACTION_POINTER_UP:
+ point_num -= 1;
+ if (nowScale < SCALE_MIN) {
+ setScale(SCALE_MIN);
+ } else if (nowScale > SCALE_MAX) {
+ setScale(SCALE_MAX);
+ }
+
+ break;
+ case MotionEvent.ACTION_CANCEL:
+
+ break;
+ }
+ return true;
+
+ }
+
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return false;
+ }
+
+
+ public void setIsCanTouch(boolean canTouch) {
+ isCanTouch = canTouch;
+ }
+
+
+ private OnClickListener clickListener;
+
+ public void setClickListener(OnClickListener clickListener) {
+ this.clickListener = clickListener;
+ }
+
+
+ public interface OnClickListener {
+ void onClick(View view);
+
+ }
+
+ /**
+ * 触摸使用的移动事件
+ *
+ * @param lessX
+ * @param lessY
+ */
+ private void setSelfPivot(float lessX, float lessY) {
+ float setPivotX = 0;
+ float setPivotY = 0;
+ setPivotX = getPivotX() + lessX;
+ setPivotY = getPivotY() + lessY;
+/* LogUtil.zzz("SelfPivot", "setPivotX:" + setPivotX + " setPivotY:" + setPivotY
+ + " getWidth:" + getWidth() + " getHeight:" + getHeight());*/
+ if (setPivotX < 0 && setPivotY < 0) {
+ setPivotX = 0;
+ setPivotY = 0;
+ } else if (setPivotX > 0 && setPivotY < 0) {
+ setPivotY = 0;
+ if (setPivotX > getWidth()) {
+ setPivotX = getWidth();
+ }
+ } else if (setPivotX < 0 && setPivotY > 0) {
+ setPivotX = 0;
+ if (setPivotY > getHeight()) {
+ setPivotY = getHeight();
+ }
+ } else {
+ if (setPivotX > getWidth()) {
+ setPivotX = getWidth();
+ }
+ if (setPivotY > getHeight()) {
+ setPivotY = getHeight();
+ }
+ }
+ setPivot(setPivotX, setPivotY);
+ }
+
+ /**
+ * 计算两个点的距离
+ *
+ * @param event
+ * @return
+ */
+ private double spacing(MotionEvent event) {
+ if (event.getPointerCount() == 2) {
+ float x = event.getX(0) - event.getX(1);
+ float y = event.getY(0) - event.getY(1);
+ return Math.sqrt(x * x + y * y);
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * 平移画面,当画面的宽或高大于屏幕宽高时,调用此方法进行平移
+ *
+ * @param x
+ * @param y
+ */
+ public void setPivot(float x, float y) {
+ setPivotX(x);
+ setPivotY(y);
+ }
+
+ /**
+ * 设置放大缩小
+ *
+ * @param scale
+ */
+ public void setScale(float scale) {
+ setScaleX(scale);
+ setScaleY(scale);
+ }
+
+ /**
+ * 横竖屏切换时,保持原有比例
+ */
+ public void resetScale() {
+ setScaleX(nowScale);
+ setScaleY(nowScale);
+ setPivot(getWidth() / 2, getHeight() / 2);
+ }
+
+ /**
+ * 初始化比例,也就是原始比例
+ */
+ public void setInitScale() {
+ setScaleX(1.0f);
+ setScaleY(1.0f);
+ setPivot(getWidth() / 2, getHeight() / 2);
+ nowScale = 0;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SecondDialPlateControl.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SecondDialPlateControl.java
new file mode 100644
index 0000000..e905197
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SecondDialPlateControl.java
@@ -0,0 +1,63 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.text.InputType;
+import android.text.method.HideReturnsTransformationMethod;
+import android.text.method.PasswordTransformationMethod;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+
+
+/**
+ * 二次拨号控制器
+ */
+public class SecondDialPlateControl extends BaseDialPlateControl {
+ private int callID;
+ protected ImageView iv_pwd;
+ private boolean isDisplayPasswordFlag = false;
+
+ //02910382
+ public SecondDialPlateControl(View plate, int callID) {
+ super(plate);
+ this.callID = callID;
+ mNumInputEt.setInputType(InputType.TYPE_NULL);
+ mNumInputEt.setSelectAllOnFocus(false);
+ mNumInputEt.setSelected(false);
+ mNumInputEt.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ iv_pwd = plate.findViewById(R.id.iv_user_expand);
+ iv_pwd.setOnClickListener(v -> {
+ setIsDisplayPasswordImageView(!isDisplayPasswordFlag);
+ isDisplayPasswordFlag = !isDisplayPasswordFlag;
+ });
+ }
+
+ @Override
+ protected void handleOnClick(View v) {
+ Integer obj = (Integer) v.getTag();
+ int index = obj.intValue();
+ if (index != -1) {
+ mNumInputEt.append(CODE_ARRAY[index]);
+ mNumInputEt.setSelection(mNumInputEt.length());
+ CallMgrV2.getInstance().reDial(this.callID, index);
+ }
+ if (index == 11) {
+ mNumInputEt.setText("");
+ }
+ }
+
+ private void setIsDisplayPasswordImageView(boolean flag) {
+ if (flag) {
+ iv_pwd.setImageResource(R.drawable.ic_eye);
+ mNumInputEt.setTransformationMethod(HideReturnsTransformationMethod.getInstance());
+ } else {
+ iv_pwd.setImageResource(R.drawable.ic_password_not_view);
+ mNumInputEt.setTransformationMethod(PasswordTransformationMethod.getInstance());
+ }
+ }
+
+ @Override
+ protected void handleOnLongClick(View v) {
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMeetingContract.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMeetingContract.java
new file mode 100644
index 0000000..ca41bc6
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMeetingContract.java
@@ -0,0 +1,131 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.view.SurfaceView;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+
+/**
+ * @Time: 2021/9/2
+ * @Author: isoftstone
+ * @Description: 视频会议相关接口类
+ */
+public interface SponsorMeetingContract {
+
+ interface SponsorMeetingView extends BaseConfContract.BaseConfView {
+ /**
+ * 打开小画面
+ */
+ void openFloat();
+
+ /**
+ * 关闭小画面
+ */
+ void closeFloat();
+
+ /**
+ * 悬浮框是否关闭
+ *
+ * @return
+ */
+ boolean floatIsClose();
+
+ /**
+ * 获取本地画面视频视图
+ * @return 本地画面视频视图
+ */
+ FrameLayout getHideContainer();
+
+ /**
+ * 申请主席回调
+ *
+ * @param type
+ * @param name
+ * @param result
+ */
+ @Override
+ void handleChairmanResult(Constant.ParticipantEvent type, String name, int result);
+ }
+
+ interface SponsorPresenter extends BaseContract.BasePresenter {
+
+ /**
+ * 填充本地画面视频视图
+ *
+ * @param container
+ */
+ void setHideViewContainer(ViewGroup container);
+
+ /**
+ * 获取本地画面视频视图
+ * @return
+ */
+ SurfaceView getHideVideoView();
+
+ /**
+ * 设置视频自动旋转
+ *
+ * @param object 调用者对象
+ * @param isOpen 是否开启方向调整
+ * @param orientation 视频方向
+ */
+ void setAutoRotation(Object object, boolean isOpen, int orientation);
+
+ /**
+ * 视频摄像头切换
+ * @param close 是否关闭
+ * @param mCameraIndex 前后摄像头 0 为后置 1 为前置
+ * @return
+ */
+ boolean closeOrOpenCamera(boolean close, int mCameraIndex);
+
+ /**
+ * 小画面显示状态
+ *
+ * @return visibility
+ */
+ boolean floatIsClose();
+
+ /**
+ * 注册传感器广播通知
+ */
+ void registerSenor();
+
+ /**
+ * 刷新注册传感器
+ */
+ void reflushSensor();
+
+ /**
+ * 显示或隐藏小画面
+ *
+ * @param visibility
+ */
+ void closeOrOpenFloat(boolean visibility);
+
+ /**
+ * 本地视频转音频
+ */
+ void locVideoToVoice();
+
+ /**
+ * 申请主席
+ * @param chairmanPassword 主席密码
+ */
+ void requestChairman(String chairmanPassword);
+
+ /**
+ * 释放主席
+ */
+ void releaseChairman();
+
+ /**
+ * 转换SC状态
+ *
+ * @param obj 要接收的数据
+ */
+ void syncState(TsdkOnEvtDeviceStateNotify.Param obj);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMeetingPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMeetingPresenter.java
new file mode 100644
index 0000000..281b749
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMeetingPresenter.java
@@ -0,0 +1,450 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.PowerManager;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.huawei.ecterminalsdk.base.TsdkDeviceState;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.call.TsdkCall;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.CallConstant;
+import com.tengshisoft.chatmodule.beans.TSDKErrorConstant;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.AudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.SwitchAudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr;
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.Platform;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+/**
+ * @Time: 2021/9/2
+ * @Author: isoftstone
+ * @Description: 视频会议实现控制类
+ */
+public class SponsorMeetingPresenter extends BaseConfPresenter implements SponsorMeetingContract.SponsorPresenter,
+ SensorEventListener {
+
+ private static final String TAG = "SponsorMeetingPresenter";
+ private SponsorMeetingContract.SponsorMeetingView meetingView;
+ private boolean isOpenVideo;
+ private CloudLinkDialog releaseChairmanDialog;
+ private final int LEVEL_AND_FLAGS = 32;
+ public SponsorMeetingPresenter(SponsorMeetingContract.SponsorMeetingView mView, Context context) {
+ super(mView, context);
+ this.meetingView = mView;
+ }
+
+ public void handUpSelf(boolean isHandUp) {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ int result = MeetingMgrV2.getInstance().handup(isHandUp, self);
+ LogUtil.zzz(TAG, "handUpSelf()", "isHandUp is " + isHandUp + "; result is " + result);
+ if (isHandUp) {
+ switch (result) {
+ case 0:
+ mView.showCustomToast(R.string.cloudLink_meeting_handupSuccess);
+ break;
+ case TSDKErrorConstant.TSDK_MEETING_NOT_CHAIN_CODE:
+ mView.showCustomToast(R.string.cloudLink_meeting_notChainmanHandupFail);
+ break;
+ case TSDKErrorConstant.TSDK_MEETING_HAND_UP_FIT_CODE:
+ // 快速点击导致两次举手状态一致导致举手失败
+ break;
+ default:
+ mView.showCustomToast(R.string.cloudLink_meeting_handupFail);
+ break;
+ }
+ } else {
+ switch (result) {
+ case 0:
+ mView.showCustomToast(R.string.cloudLink_meeting_handDownSuccess);
+ break;
+ case TSDKErrorConstant.TSDK_MEETING_HAND_UP_FIT_CODE:
+ // 快速点击导致两次举手状态一致导致举手失败
+ break;
+ default:
+ mView.showCustomToast(R.string.cloudLink_meeting_cancelHandupFail);
+ break;
+ }
+ }
+
+ }
+
+ @Override
+ public void detachView() {
+ if (sensorManager != null) {
+ sensorManager.unregisterListener(this);// 注销传感器监听
+ }
+
+ if (wakeLock != null) {
+ if (wakeLock.isHeld()) {
+ wakeLock.release(); //释放电源锁
+ }
+ wakeLock = null;
+ }
+ super.detachView();
+ }
+
+ @Override
+ public void setHideViewContainer(ViewGroup container) {
+ Platform.runUiDelay(() -> addSurfaceView(container, getHideVideoView()), ConstantsV2.DELAY_MILLIS_100);
+ }
+
+ @Override
+ public SurfaceView getHideVideoView() {
+ return VideoMgr.getInstance().getLocalHideView();
+ }
+
+ private void addSurfaceView(ViewGroup container, SurfaceView child) {
+ if (child == null || container == null) {
+ LogUtil.zzz(TAG, "LocalHideView", "addSurfaceView view == null");
+ return;
+ }
+ ViewGroup vGroup = (ViewGroup) child.getParent();
+ child.setVisibility(View.GONE);
+ if (vGroup == null) {
+ container.removeAllViews();
+ container.addView(child);
+ } else if (container != vGroup) {
+ container.removeAllViews();
+ vGroup.removeAllViews();
+ container.addView(child);
+ }
+ container.setVisibility(View.VISIBLE);
+ child.setVisibility(View.VISIBLE);
+ LogUtil.zzz(TAG, "LocalHideView", "localHideView add localHideView");
+ }
+
+ /**
+ * avc&svc会议关闭打开本地摄像头
+ *
+ * @param close true为关闭 false为打开
+ * @param mCameraIndex 0后置 1前置
+ * @return closeOrOpenLocalVideo
+ */
+ @Override
+ public boolean closeOrOpenCamera(boolean close, int mCameraIndex) {
+ LogUtil.zzz(TAG, "closeOrOpenCamera2", close, LogUtil.getInstance().getSimpleExtraInfo());
+ int callID = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ if (callID == 0) {
+ return false;
+ }
+ try {
+ if (close) {
+ CallMgrV2.getInstance().closeCamera(callID);
+ } else {
+ CallMgrV2.getInstance().openCamera(callID, mCameraIndex);
+ VideoMgr.getInstance().setVideoOrient(callID, mCameraIndex);
+ }
+ mView.updateView(Constant.UPDATE_VIDEO_STATUS, close, "");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public void syncState(TsdkOnEvtDeviceStateNotify.Param obj) {
+ LogUtil.zzz(TAG, "Switching SC State obj = " + obj);
+ TsdkDeviceState deviceState = obj.getDeviceState();
+ // 0 开 1关
+ if (deviceState.getCameraState() == 1) {
+ LogUtil.zzz(TAG, "Switching SC State", "open camera");
+ CallMgrV2.getInstance().openCamera(obj.getCallId(), deviceState.getCameraIndex());
+ VideoMgr.getInstance().setVideoOrient(obj.getCallId(), deviceState.getCameraIndex() == 0 ?
+ CallConstant.BACK_CAMERA : CallConstant.FRONT_CAMERA);
+ mView.updateView(Constant.UPDATE_VIDEO_STATUS, true, "");
+ } else {
+ LogUtil.zzz(TAG, "Switching SC State", "close camera");
+ CallMgrV2.getInstance().closeCamera(obj.getCallId());
+ }
+ if (deviceState.getMicState() == 1) { // 开音
+ CallMgrV2.getInstance().muteMic(obj.getCallId(), true);
+ mView.updateView(Constant.CALL_UPDATE_VOICE, true, "");
+ }
+ new AudioRouteManager.Builder()
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .setIsVideo(deviceState.getSpeakerState() == 1)
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .build();
+ }
+
+ /**
+ * @param isMute ture是静音
+ * @return
+ */
+ public boolean muteSelf(boolean isMute) {
+ // true 是静音
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ LogUtil.zzz(TAG, "Mute fail self = null");
+ return false;
+ }
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, isMute);
+ if (result == 0) {
+ mView.updateView(Constant.CALL_UPDATE_VOICE, isMute, "");
+ }
+ return result == 0;
+ }
+
+ /**
+ * 关闭点对点视频摄像头
+ *
+ * @param close 摄像头是否关闭
+ * @param callID 会议标识
+ * @param cameraIndex 0 后置 1前置
+ */
+ public void closeOrOpenCamera(boolean close, int callID, int cameraIndex) {
+ LogUtil.zzz(TAG, "closeOrOpenCamera1", close, LogUtil.getInstance().getSimpleExtraInfo());
+ try {
+ if (close) {
+ CallMgrV2.getInstance().closeCamera(callID);
+ } else {
+ CallMgrV2.getInstance().openCamera(callID, cameraIndex);
+ VideoMgr.getInstance().setVideoOrient(callID, cameraIndex == 0 ? CallConstant.BACK_CAMERA :
+ CallConstant.FRONT_CAMERA);
+ }
+ mView.updateView(Constant.UPDATE_VIDEO_STATUS, close, "");
+ } catch (NullPointerException np) {
+ LogUtil.zzz(TAG, "closeCallOrOpenLocalVideo: " + np.getMessage());
+ }
+ }
+
+ @Override
+ public boolean floatIsClose() {
+ if (meetingView != null) {
+ return meetingView.floatIsClose();
+ }
+ return false;
+ }
+
+ private PowerManager.WakeLock wakeLock;
+ private SensorManager sensorManager;
+ private Sensor mSensor;
+
+ @SuppressLint("InvalidWakeLockTag")
+ @Override
+ public void registerSenor() {
+ PowerManager localPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ wakeLock = localPowerManager.newWakeLock(LEVEL_AND_FLAGS, "MyPower");
+ mSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ sensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+
+ @Override
+ public void reflushSensor() {
+ if (sensorManager != null) {
+ //注册传感器 第一个参数为距离监听器,第二个是传感器类型,第三个是延迟类型
+ sensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+
+ /**
+ * 小画面显示隐藏
+ *
+ * @param isVisibility
+ */
+ @Override
+ public void closeOrOpenFloat(boolean isVisibility) {
+ if (isVisibility) {
+ meetingView.openFloat();
+ } else {
+ meetingView.closeFloat();
+ }
+ }
+
+ @Override
+ public void locVideoToVoice() {
+ TsdkManager.getInstance().getCallManager().getCallByCallId(CallMgrV2.getInstance().getCallId()).delVideo();
+ }
+
+ @Override
+ public void requestChairman(String chairmanPassword) {
+ LogUtil.zzz(TAG, "requestChairman");
+ int result = MeetingMgrV2.getInstance().requestChairman(chairmanPassword);
+ //会议已存在主席
+ if (result == TSDKErrorConstant.TSDK_MEETING_EXIST_CHAIN_MAN_CODE) {
+ MeetingController.getInstance().setChairman(false);
+ meetingView.showCustomToast(R.string.cloudLink_meeting_requestChairmanFailed);
+ } else if (result == 0) {
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, chairmanPassword);
+ }
+ }
+
+ @Override
+ public void releaseChairman() {
+ releaseChairmanDialog();
+ }
+
+ @Override
+ public void setAutoRotation(Object object, boolean isOpen, int orientation) {
+ VideoMgr.getInstance().setAutoRotation(object, isOpen, orientation);
+ }
+
+ public void switchCamera() {
+ int meetingCallID = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ int callId = CallMgrV2.getInstance().getCallId();
+
+ // 矫正异常场景下摄像头打开着,但是存储的变量是false,根据图标打开状态矫正为打开。
+ if (isOpenVideo != MeetingController.getInstance().isVideoOpen) {
+ MeetingController.getInstance().isVideoOpen = isOpenVideo;
+ }
+ // 摄像头关闭之后不让切换,目前用的变量控制,之后需要修改。
+ if (!MeetingController.getInstance().isVideoOpen) {
+ LogUtil.zzz(TAG, "Don't switch when the camera is off");
+ return;
+ }
+ if (meetingCallID == 0) {
+ if (callId != 0) {
+ MeetingController.getInstance().cameraIndex = !MeetingController.getInstance().cameraIndex;
+ CallMgrV2.getInstance().switchCamera(callId, MeetingController.getInstance().cameraIndex ?
+ CallConstant.BACK_CAMERA : CallConstant.FRONT_CAMERA);
+ }
+ } else {
+ MeetingController.getInstance().cameraIndex = !MeetingController.getInstance().cameraIndex;
+ CallMgrV2.getInstance().switchCamera(meetingCallID, MeetingController.getInstance().cameraIndex ?
+ CallConstant.BACK_CAMERA : CallConstant.FRONT_CAMERA);
+ }
+ }
+
+ /**
+ * 发送屏幕共享辅流
+ */
+ public int sendAuxData() {
+ LogUtil.zzz(TAG, "sendAuxData: screen share");
+ int callID = MeetingMgrV2.getInstance().getCurrentConferenceCallID();
+ TsdkCall tsdkCall = TsdkManager.getInstance().getCallManager().getCallByCallId(callID);
+ if (tsdkCall == null) {
+ meetingView.showCustomToast(R.string.cloudLink_meeting_shareFailed);
+ LogUtil.zzz(TAG, "sendAuxData", "send aux data failed. tsdkCall = null");
+ return -1;
+ }
+ int result = tsdkCall.startAuxData();
+ if (result != 0) {
+ LogUtil.zzz(TAG, "sendAuxData", "send aux data exception result = " + result);
+ meetingView.showCustomToast(R.string.cloudLink_share_state_exception);
+ return result;
+ }
+ return result;
+ }
+
+ public void showBitStream(boolean isAudio) {
+ mView.showStreamDialog(isAudio);
+ }
+
+ public void showAudioBitStream() {
+ showBitStream(true);
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (wakeLock == null) {
+ LogUtil.zzz(TAG, "onSensorChanged", "wakeLock is null");
+ return;
+ }
+ float[] its = event.values;
+ if (its != null && event.sensor.getType() == Sensor.TYPE_PROXIMITY) {
+ // 经过测试,当手贴近距离感应器的时候its[0]返回值为0.0,当手离开时返回1.0
+ if (its[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 i) {
+ LogUtil.zzz(TAG, "onAccuracyChanged");
+ }
+
+ /**
+ * 根据图标的显示标记摄像头的打开/关闭状态
+ *
+ * @param isOpenVideo 摄像头的状态
+ */
+ public void setOpenVideo(boolean isOpenVideo) {
+ this.isOpenVideo = isOpenVideo;
+ LogUtil.zzz(TAG, "setOpenVideo isOpenVideo : ", isOpenVideo);
+ }
+
+ public void recordConf() {
+ if (MeetingController.getInstance().isChairman()) {
+ // MeetingMgrV2.getInstance().recordConf();
+ }
+ }
+
+ private void releaseChairmanDialog() {
+ if (releaseChairmanDialog == null) {
+ releaseChairmanDialog = new CloudLinkDialog(context);
+ }
+ if (releaseChairmanDialog.isShowing()) {
+ return;
+ }
+ releaseChairmanDialog.setStr_message(context.getString(R.string.cloudLink_meeting_releaseChairmanTip), null);
+ releaseChairmanDialog.setYes(context.getString(R.string.cloudLink_sure), null, () -> {
+ LogUtil.zzz(TAG, "releaseChairmanDialog Yes");
+ releaseTheHost();
+ releaseChairmanDialog.dismiss();
+ });
+ releaseChairmanDialog.setNo(context.getString(R.string.cloudLink_cancel), null, () -> {
+ LogUtil.zzz(TAG, "releaseChairmanDialog No");
+ releaseChairmanDialog.dismiss();
+ });
+ releaseChairmanDialog.show();
+ }
+
+ private void releaseTheHost() {
+ int result = MeetingMgrV2.getInstance().releaseChairman();
+ if (result == 0) {
+ MeetingController.getInstance().setChairman(false);
+ mView.showCustomToast(context.getString(R.string.cloudLink_meeting_releaseChairmanSuccess));
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, Constant.TEMP_CONF_INFO);
+ if (MeetingController.getInstance().isSvcBigConf() && MeetingController.getInstance().isWatchMember()) {
+ LocalBroadcast.getInstance().sendBroadcast(Constant.SPONSOR_MEETING_WATCH_MEMBER, null);
+ MeetingController.getInstance().setWatchMember(false);
+ MeetingController.getInstance().setWatchingMember(null);
+ }
+ }
+ }
+
+ public void setConfSubtitle(boolean isSubtitleEnable) {
+ boolean isPendingEnable = !isSubtitleEnable;
+ int result = MeetingMgrV2.getInstance().setConfSubtitle(isPendingEnable);
+ if (result == 0) {
+ mView.showCustomToast(isSubtitleEnable ? R.string.cloudLink_meeting_caption_disabled :
+ R.string.cloudLink_meeting_caption_enabled);
+ } else {
+ mView.showCustomToast(isSubtitleEnable ? R.string.cloudLink_meeting_disabled_caption_failed :
+ R.string.cloudLink_meeting_caption_enable_failed);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMorePopWindow.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMorePopWindow.java
new file mode 100644
index 0000000..03d183a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SponsorMorePopWindow.java
@@ -0,0 +1,355 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Dialog;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.text.TextUtils;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import com.allen.library.SuperTextView;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tengshisoft.chatmodule.hwclud.utils.UIUtil;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import java.util.Optional;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.content.ContextCompat;
+
+/**
+ * @Time: 2021/6/25
+ * @Author: isoftstone
+ * @Description: 视频会议更多弹框
+ */
+public class SponsorMorePopWindow extends Dialog {
+
+ private boolean isHandUp = true;
+ /**
+ * 是否会议
+ */
+ private boolean isConf;
+ private SponsorMeetingPresenter mPresenter;
+ private int tempRedusChecked;
+ private boolean isAuxData;
+ private boolean isRemote;
+ private LinearLayout chairmanView;
+ private LinearLayout notChairmanView;
+ private SponsorMeetingActivity mSponsorMeetingActivity;
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public SponsorMorePopWindow(SponsorMeetingActivity sponsorMeetingActivity, boolean flag, SponsorMeetingPresenter presenter,
+ int tempRedusChecked, boolean isAuxData, boolean isRemote) {
+ super(sponsorMeetingActivity, R.style.CloudDialog);
+ mSponsorMeetingActivity = sponsorMeetingActivity;
+ this.isConf = flag;
+ this.mPresenter = presenter;
+ this.tempRedusChecked = tempRedusChecked;
+ this.isAuxData = isAuxData;
+ this.isRemote = isRemote;
+ setContentView(isConf ? R.layout.pop_is_chairman : R.layout.popup_call_video_view);
+ Window window = getWindow();
+ window.setGravity(Gravity.BOTTOM);
+ window.setWindowAnimations(R.style.main_menu_animStyle);
+ window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+ initListener();
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void initListener() {
+ if (isConf) {
+ // 非主持人显示View
+ chairmanView = findViewById(R.id.ll_chairman_view);
+ // 非主持人显示View
+ notChairmanView = findViewById(R.id.ll_not_chairman_view);
+ FrameLayout fl_header = findViewById(R.id.fl_header);
+ LogUtil.zzz("SponsorMorePopWindow", "isChairman == " + MeetingController.getInstance().isChairman());
+ if (MeetingController.getInstance().isChairman()) {
+ initChairmanData();
+ } else {
+ initNoChairmanData();
+ }
+ fl_header.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismiss();
+ }
+ });
+ } else {
+ SuperTextView switchCamera = findViewById(R.id.st_switch_camera);
+ // 转语音通话
+ SuperTextView switchVoice = findViewById(R.id.switch_voice);
+ SuperTextView hideWindow = findViewById(R.id.hide_window);
+ initHideWindow(hideWindow);
+ Optional.ofNullable(switchVoice).ifPresent(view->view.setOnClickListener(v -> {
+ mPresenter.locVideoToVoice();
+ dismiss();
+ }));
+ initSwitchCamera(switchCamera);
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void initChairmanData() {
+ notChairmanView.setVisibility(View.GONE);
+ chairmanView.setVisibility(View.VISIBLE);
+ // 锁定会议
+ SuperTextView lockConfView = findViewById(R.id.tv_lock);
+ Optional.ofNullable(lockConfView).ifPresent(lockConf -> {
+ if (MeetingController.getInstance().isMeetingIsLock()) {
+ lockConf.setLeftString(getContext().getString(R.string.cloudLink_meeting_unlock));
+ } else {
+ lockConf.setLeftString(getContext().getString(R.string.cloudLink_meeting_lock));
+ }
+ lockConf.setOnClickListener(v -> {
+ mPresenter.meetingLock();
+ dismiss();
+ });
+ });
+
+ // 延长会议
+ SuperTextView extendConfView = findViewById(R.id.extend_conf);
+ Optional.ofNullable(extendConfView).ifPresent(extendConf->{
+ if (TextUtils.isEmpty(MeetingController.getInstance().getEndTime())) {
+ extendConf.setVisibility(View.GONE);
+ } else {
+ extendConf.setVisibility(View.VISIBLE);
+ }
+ extendConf.setOnClickListener(v -> {
+ mPresenter.postponeConf();
+ dismiss();
+ });
+ });
+
+
+ SuperTextView chairmanView = findViewById(R.id.chairman);
+ Optional.ofNullable(chairmanView).ifPresent(chairman -> {
+ chairman.setLeftString(getContext().getString(R.string.cloudLink_meeting_releaseChairman));
+ // 释放主席
+ chairman.setOnClickListener(v -> {
+ mPresenter.releaseChairman();
+ dismiss();
+ });
+ });
+
+ SuperTextView hideWindow = findViewById(R.id.hide_window);
+ initHideWindow(hideWindow);
+ SuperTextView switchCamera = findViewById(R.id.st_switch_camera);
+ initSwitchCamera(switchCamera);
+
+ SuperTextView screenCap = findViewById(R.id.screenCap);
+ startScreenCap(screenCap);
+
+ SuperTextView superTextView = findViewById(R.id.openSubtitle);
+ Optional.ofNullable(superTextView).ifPresent(view -> view.setOnClickListener(v -> {
+ boolean isEnable = ((SuperTextView) v).getLeftString().equals(getContext().getString(R.string.cloudLink_meeting_disable_caption));
+ mPresenter.setConfSubtitle(isEnable);
+ dismiss();
+ EncryptedSPTool.putBoolean("App_isFirstSubtitle", false);
+ }));
+ initPlaceSubtitleListener();
+
+ SuperTextView invitationTextView = findViewById(R.id.witch_invitation);
+ Optional.ofNullable(invitationTextView).ifPresent(witchInvitation -> {
+ String account = EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT);
+ String pwd = EncryptedSPTool.getString(account);
+ if (isConf && MeetingController.getInstance().isChairman() && !TextUtils.isEmpty(pwd)) {
+ witchInvitation.setVisibility(View.VISIBLE);
+ witchInvitation.setOnClickListener(v -> {
+ dismiss();
+ mSponsorMeetingActivity.jumpParticipants(true);
+ });
+ }
+ });
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void initNoChairmanData() {
+ chairmanView.setVisibility(View.GONE);
+ notChairmanView.setVisibility(View.VISIBLE);
+
+ Member member = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (member != null) {
+ isHandUp = !(member.isHandUp() || member.isReqTalk());
+ }
+ boolean hasChairman = MeetingController.getInstance().isHasChairman();
+ SuperTextView handUp = findViewById(R.id.handup);
+ SuperTextView chairman = findViewById(R.id.quest_chairman);
+ Optional.ofNullable(handUp).ifPresent(view -> {
+ view.getLeftTextView()
+ .setTextColor(ContextCompat.getColor(getContext(), hasChairman ? R.color.textWhiteColor : R.color.video_line));
+ view.setLeftString(getContext().getString(isHandUp?
+ R.string.cloudLink_meeting_raiseHand:R.string.cloudLink_meeting_cancelHandup));
+ view.setOnClickListener(v -> {
+ // 会中无主持人不能进行举手操作
+ if (hasChairman) {
+ mPresenter.handUpSelf(isHandUp);
+ }
+ dismiss();
+ });
+ });
+ Optional.ofNullable(chairman).ifPresent(view -> {
+ view.getLeftTextView()
+ .setTextColor(ContextCompat.getColor(getContext(), hasChairman ? R.color.video_line : R.color.textWhiteColor));
+ // 申请主席
+ view.setLeftString(getContext().getString(R.string.cloudLink_meeting_applicationChairman));
+ String vmrId =
+ EncryptedSPTool.getString(Constant.VMR_ACCESSNUMBER) + EncryptedSPTool.getString(Constant.VMR_CONF_ID);
+ view.setOnClickListener(v -> {
+ if (!MeetingController.getInstance().isHasChairman()) {
+ MeetingController.getInstance().isShowDialog = true;
+ if (!UIUtil.isService3() && !TextUtils.isEmpty(vmrId) && vmrId.equals(EncryptedSPTool.getString(Constant.IS_VMR_2_ID))) {
+ // 2.0vmr自动申请
+ mPresenter.requestChairman(EncryptedSPTool.getString(Constant.VMR_CHAIRMANPWD));
+ } else if (UIUtil.isService3() && vmrId.equals(EncryptedSPTool.getString(Constant.IS_VMR_3_ID))) {
+ mPresenter.requestChairman(MeetingController.getInstance().getChairman3Pwd());
+ } else {
+ mPresenter.requestChairman("");
+ }
+ dismiss();
+ }
+ });
+ });
+
+ // 切换摄像头
+ SuperTextView switchCamera = findViewById(R.id.st_switch_camera2);
+ initSwitchCamera(switchCamera);
+ SuperTextView hideWindow = findViewById(R.id.tv_hide_window);
+ initHideWindow(hideWindow);
+ SuperTextView screenCap = findViewById(R.id.tv_screen_cap);
+ startScreenCap(screenCap);
+ initPlaceSubtitleListener();
+ }
+
+ /**
+ * 开始录制
+ *
+ * @param screenCap
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void startScreenCap(SuperTextView screenCap) {
+ Optional.ofNullable(screenCap).ifPresent(view -> view.setOnClickListener(v -> {
+ mPresenter.recordConf();
+ dismiss();
+ }));
+ }
+
+ private void initSwitchCamera(SuperTextView switchCamera) {
+ if (isAuxData && tempRedusChecked == 0) {
+ // 共享画面时,不让切换摄像头
+ switchCamera.setVisibility(View.GONE);
+ } else {
+ switchCamera.setVisibility(View.VISIBLE);
+ }
+ // 摄像头关闭之后不让切换,目前用的变量控制,之后需要修改。
+ if (!MeetingController.getInstance().isVideoOpen) {
+ switchCamera.getLeftTextView().setTextColor(ContextCompat.getColor(getContext(), R.color.video_line));
+ } else {
+ switchCamera.getLeftTextView().setTextColor(ContextCompat.getColor(getContext(), R.color.textWhiteColor));
+ }
+ switchCamera.setOnClickListener(v -> {
+ mPresenter.switchCamera();
+ dismiss();
+ });
+ }
+
+ /**
+ * 小窗口显示与隐藏
+ *
+ * @param hideWindow
+ */
+ private void initHideWindow(SuperTextView hideWindow) {
+ boolean visibility = !mPresenter.floatIsClose();
+ if (isRemote && (MeetingController.getInstance().videoJoinMeeting > 1)) {
+ hideWindow.setVisibility(View.VISIBLE);
+ } else {
+ hideWindow.setVisibility(View.GONE);
+ }
+ if (visibility) {
+ hideWindow.setLeftString(getContext().getString(R.string.cloudLink_meeting_showWindow));
+ } else {
+ hideWindow.setLeftString(getContext().getString(R.string.cloudLink_meeting_hideWindow));
+ }
+ hideWindow.setOnClickListener(v -> {
+ mPresenter.closeOrOpenFloat(visibility);
+ if (visibility) {
+ hideWindow.setLeftString(getContext().getString(R.string.cloudLink_meeting_showWindow));
+ } else {
+ hideWindow.setLeftString(getContext().getString(R.string.cloudLink_meeting_hideWindow));
+ }
+ dismiss();
+ });
+ }
+
+ /**
+ * 数据状态改变刷新view
+ */
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void refreshView() {
+ if (isConf) {
+ LogUtil.zzz("SponsorMorePopWindow", "isChairman == " + MeetingController.getInstance().isChairman());
+ if (MeetingController.getInstance().isChairman()) {
+ initChairmanData();
+ } else {
+ initNoChairmanData();
+ }
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void initPlaceSubtitleListener() {
+ SuperTextView textView = findViewById(MeetingController.getInstance().isChairman() ?
+ R.id.openPlaceSubtitle : R.id.childOpenPlaceSubtitle);
+ Optional.ofNullable(textView).ifPresent(view -> view.setOnClickListener(v -> {
+ boolean isShow = ((SuperTextView) v).getLeftString().equals(getContext().getString(R.string.cloudLink_meeting_enable_participant_caption));
+ mSponsorMeetingActivity.setSubtitleViewVisibility(isShow);
+ dismiss();
+ EncryptedSPTool.putBoolean("App_isFirstPlaceSubtitle", false);
+ }));
+ }
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void setPlaceSubtitleIsOpen(boolean isEnable) {
+ boolean isFirst = EncryptedSPTool.getBoolean("App_isFirstPlaceSubtitle", true);
+ Drawable drawable = isFirst ? ContextCompat.getDrawable(getContext(), R.drawable.ic_subtitle_new) : null;
+ SuperTextView textView = findViewById(MeetingController.getInstance().isChairman() ?
+ R.id.openPlaceSubtitle : R.id.childOpenPlaceSubtitle);
+ Optional.ofNullable(textView).ifPresent(view -> {
+ view.setLeftTvDrawableRight(drawable);
+ view.setLeftString(getContext().getString(isEnable ?
+ R.string.cloudLink_meeting_disable_participant_caption : R.string.cloudLink_meeting_enable_participant_caption));
+ });
+ }
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ public void setSubtitleStatus(boolean isConfSubtitleEnable, boolean isSubtitleEnable) {
+ SuperTextView superTextView = findViewById(R.id.openSubtitle);
+ Optional.ofNullable(superTextView).ifPresent(view -> {
+ boolean isFirst = EncryptedSPTool.getBoolean("App_isFirstSubtitle", true);
+ Drawable drawable = isFirst ? ContextCompat.getDrawable(getContext(), R.drawable.ic_subtitle_new) : null;
+ view.setLeftTvDrawableRight(drawable);
+ view.setLeftString(getContext().getString(isSubtitleEnable ?
+ R.string.cloudLink_meeting_disable_caption : R.string.cloudLink_meeting_enable_caption));
+ view.setVisibility(isConfSubtitleEnable && MeetingController.getInstance().isChairman() ? View.VISIBLE : View.GONE);
+ });
+ updatePlaceSubtitleButton(findViewById(R.id.openPlaceSubtitle), isConfSubtitleEnable, isSubtitleEnable);
+ updatePlaceSubtitleButton(findViewById(R.id.childOpenPlaceSubtitle), isConfSubtitleEnable, isSubtitleEnable);
+ }
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private void updatePlaceSubtitleButton(SuperTextView textView, boolean isConfSubtitleEnable, boolean isSubtitleEnable) {
+ Optional.ofNullable(textView).ifPresent(view -> {
+ view.setVisibility(isConfSubtitleEnable ? View.VISIBLE : View.GONE);
+ view.getLeftTextView()
+ .setTextColor(ContextCompat.getColor(getContext(), isSubtitleEnable ? R.color.textWhiteColor : R.color.video_line));
+ view.setEnabled(isSubtitleEnable);
+ });
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/StreamInfoAdapter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/StreamInfoAdapter.java
new file mode 100644
index 0000000..f5dc814
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/StreamInfoAdapter.java
@@ -0,0 +1,96 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.huawei.ecterminalsdk.base.TsdkCallVideoStreamInfo;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tenlionsoft.baselib.core.beans.Member;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+public class StreamInfoAdapter extends RecyclerView.Adapter {
+
+ private List streamInfoList = new ArrayList<>();
+
+ public void setStreamInfoList(List streamInfoList) {
+ this.streamInfoList = streamInfoList;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public StreamViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new StreamViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_stream_info_item, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull StreamViewHolder holder, int position) {
+ TsdkCallVideoStreamInfo streamInfo = streamInfoList.get(position);
+ List watchMember = MeetingMgrV2.getInstance().getWatchMember();
+ if (position == 0) {
+ holder.stream_info_type_name.setText(holder.itemView.getContext().getString(R.string.cloudLink_localSend));
+ if (TextUtils.isEmpty(streamInfo.getEncoderSize())) {
+ holder.text_0_3_2.setText("-");
+ holder.text_5_3.setText("-");
+ holder.text_6_3.setText("-");
+ holder.text_10_3.setText("-");
+ holder.text_7_3.setText("-");
+ holder.text_8_3.setText("-");
+ holder.text_9_3.setText("-");
+ } else {
+ holder.text_0_3_2.setText(TextUtils.isEmpty(streamInfo.getEncodeName()) ? "-" : streamInfo.getEncodeName());
+ holder.text_5_3.setText(String.valueOf(streamInfo.getSendBitRate()));
+ holder.text_6_3.setText(TextUtils.isEmpty(streamInfo.getEncoderSize()) ? "-" : streamInfo.getEncoderSize());
+ holder.text_10_3.setText(String.valueOf(streamInfo.getSendFrameRate()));
+ holder.text_7_3.setText(String.valueOf(streamInfo.getSendLossFraction()));
+ holder.text_8_3.setText(String.valueOf(streamInfo.getSendDelay()));
+ holder.text_9_3.setText(String.valueOf(streamInfo.getSendJitter()));
+ }
+ } else {
+ for (Member member: watchMember) {
+ if (member.getDecodeSsrc() == streamInfo.getDecodeSsrc()) {
+// holder.stream_info_type_name.setText(holder.itemView.getContext().getString(R.string.cloudLink_localReceive));
+ holder.stream_info_type_name.setText(member.getDisplayName());
+ }
+ }
+ holder.text_0_3_2.setText(TextUtils.isEmpty(streamInfo.getDecodeName()) ? "" : streamInfo.getDecodeName());
+ holder.text_5_3.setText(String.valueOf(streamInfo.getRecvBitRate()));
+ holder.text_6_3.setText(TextUtils.isEmpty(streamInfo.getDecoderSize()) ? "" : streamInfo.getDecoderSize());
+ holder.text_10_3.setText(String.valueOf(streamInfo.getRecvFrameRate()));
+ holder.text_7_3.setText(String.valueOf(streamInfo.getRecvLossFraction()));
+ holder.text_8_3.setText(String.valueOf(streamInfo.getRecvDelay()));
+ holder.text_9_3.setText(String.valueOf(streamInfo.getRecvJitter()));
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ return streamInfoList.size();
+ }
+
+ public static class StreamViewHolder extends RecyclerView.ViewHolder {
+
+ TextView stream_info_type_name, text_0_3_2, text_5_3, text_6_3, text_10_3, text_7_3, text_8_3, text_9_3;
+
+ public StreamViewHolder(@NonNull View itemView) {
+ super(itemView);
+ stream_info_type_name = itemView.findViewById(R.id.stream_info_type_name);
+ text_0_3_2 = itemView.findViewById(R.id.text_0_3_2);
+ text_5_3 = itemView.findViewById(R.id.text_5_3);
+ text_6_3 = itemView.findViewById(R.id.text_6_3);
+ text_10_3 = itemView.findViewById(R.id.text_10_3);
+ text_7_3 = itemView.findViewById(R.id.text_7_3);
+ text_8_3 = itemView.findViewById(R.id.text_8_3);
+ text_9_3 = itemView.findViewById(R.id.text_9_3);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/StreamInfoDialog.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/StreamInfoDialog.java
new file mode 100644
index 0000000..1dbc52d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/StreamInfoDialog.java
@@ -0,0 +1,218 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.huawei.ecterminalsdk.base.TsdkCallVideoStreamInfo;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+/**
+ * dialog
+ */
+public class StreamInfoDialog extends Dialog {
+
+ private LinearLayout ll_aux_pop;
+ private boolean mIsAux;
+ private StreamInfoAdapter streamInfoAdapter;
+
+ public StreamInfoDialog(@NonNull Context context, Boolean isAudio, boolean isAux) {
+ super(context, R.style.CloudDialog);
+ this.isAudio = isAudio;
+ this.mIsAux = isAux;
+ }
+
+ private Boolean isAudio;
+ private TextView text_0_1;
+ private TextView text_0_2;
+ private RecyclerView streamInfoRv;
+ private TextView text_0_1_2_0;
+ private TextView text_0_2_2_0;
+ private TextView text_1_1;
+ private TextView text_1_2;
+ private TextView text_2_1;
+ private TextView text_2_2;
+ private TextView text_3_1;
+ private TextView text_3_2;
+ private TextView text_4_1;
+ private TextView text_4_2;
+ private TextView text_5_1_0;
+ private TextView text_5_2_0;
+ private TextView text_6_1_0;
+ private TextView text_6_2_0;
+ private TextView text_7_1_0;
+ private TextView text_7_2_0;
+ private TextView text_8_1_0;
+ private TextView text_8_2_0;
+ private TextView text_9_1_0;
+ private TextView text_9_2_0;
+ private TextView text_10_1_0;
+ private TextView text_10_2_0;
+ private ImageView close;
+ private Map textMap = new HashMap<>();
+
+ private onNoOnclickListener noOnclickListener;//点击事件
+
+
+ public interface onNoOnclickListener {
+ void onNoClick();
+ }
+
+ public void setNo(onNoOnclickListener noOnclickListener){
+ this.noOnclickListener = noOnclickListener;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (isAudio){
+ setContentView(R.layout.stream_info_isaudio);
+ }else {
+ setContentView(R.layout.stream_info);
+ }
+ setCanceledOnTouchOutside(true);
+ initView();
+ initData();
+ initEvent();
+ }
+
+ public void initView() {
+ close = findViewById(R.id.close);
+ text_1_1 = findViewById(R.id.text_1_1);
+ text_1_2 = findViewById(R.id.text_1_2);
+ text_2_1 = findViewById(R.id.text_2_1);
+ text_2_2 = findViewById(R.id.text_2_2);
+ text_3_1 = findViewById(R.id.text_3_1);
+ text_3_2 = findViewById(R.id.text_3_2);
+ text_4_1 = findViewById(R.id.text_4_1);
+ text_4_2 = findViewById(R.id.text_4_2);
+ text_0_1 = findViewById(R.id.text_0_1); //协议类型 音频 发送
+ text_0_2 = findViewById(R.id.text_0_2);//协议类型 音频 接收
+ streamInfoRv = findViewById(R.id.stream_info_rv);
+ text_5_1_0 = findViewById(R.id.text_5_1_0);
+ text_5_2_0 = findViewById(R.id.text_5_2_0);
+ text_6_1_0 = findViewById(R.id.text_6_1_0);
+ text_6_2_0 = findViewById(R.id.text_6_2_0);
+ text_7_1_0 = findViewById(R.id.text_7_1_0);
+ text_7_2_0 = findViewById(R.id.text_7_2_0);
+ text_8_1_0 = findViewById(R.id.text_8_1_0);
+ text_8_2_0 = findViewById(R.id.text_8_2_0);
+ text_9_1_0 = findViewById(R.id.text_9_1_0);
+ text_9_2_0 = findViewById(R.id.text_9_2_0);
+ text_10_1_0 = findViewById(R.id.text_10_1_0);
+ text_10_2_0 = findViewById(R.id.text_10_2_0);
+ text_0_1_2_0 = findViewById(R.id.text_0_1_2_0);//协议类型 辅流 发送
+ text_0_2_2_0 = findViewById(R.id.text_0_2_2_0);//协议类型 辅流 接收
+ ll_aux_pop = findViewById(R.id.ll_aux_pop);//辅流的数据
+ if (streamInfoRv != null) {
+ streamInfoRv.setLayoutManager(new LinearLayoutManager(getContext(), RecyclerView.VERTICAL, false));
+ streamInfoAdapter = new StreamInfoAdapter();
+ streamInfoRv.setAdapter(streamInfoAdapter);
+ }
+ }
+
+ private void visibilityAux() {
+ if (mIsAux) {
+ ll_aux_pop.setVisibility(View.VISIBLE);
+ } else {
+ ll_aux_pop.setVisibility(View.GONE);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public void initData() {
+ if (isAudio) {
+ text_0_1.setText(textMap.get("text_0_1") != null ? textMap.get("text_0_1").toString() : "");
+ text_0_2.setText(textMap.get("text_0_2") != null ? textMap.get("text_0_2").toString() : "");
+ text_1_1.setText(textMap.get("text_1_1") != null ? textMap.get("text_1_1").toString() : "");
+ text_1_2.setText(textMap.get("text_1_2") != null ? textMap.get("text_1_2").toString() : "");
+ text_2_1.setText(textMap.get("text_2_1") != null ? textMap.get("text_2_1").toString() : "");
+ text_2_2.setText(textMap.get("text_2_2") != null ? textMap.get("text_2_2").toString() : "");
+ text_3_1.setText(textMap.get("text_3_1") != null ? textMap.get("text_3_1").toString() : "");
+ text_3_2.setText(textMap.get("text_3_2") != null ? textMap.get("text_3_2").toString() : "");
+ text_4_1.setText(textMap.get("text_4_1") != null ? textMap.get("text_4_1").toString() : "");
+ text_4_2.setText(textMap.get("text_4_2") != null ? textMap.get("text_4_2").toString() : "");
+ } else {
+ // 音频
+ text_0_1.setText(textMap.get("text_0_1") != null ? textMap.get("text_0_1").toString() : "");
+ text_0_2.setText(textMap.get("text_0_2") != null ? textMap.get("text_0_2").toString() : "");
+ text_1_1.setText(textMap.get("text_1_1") != null ? textMap.get("text_1_1").toString() : "");
+ text_1_2.setText(textMap.get("text_1_2") != null ? textMap.get("text_1_2").toString() : "");
+ text_2_1.setText(textMap.get("text_2_1") != null ? textMap.get("text_2_1").toString() : "");
+ text_2_2.setText(textMap.get("text_2_2") != null ? textMap.get("text_2_2").toString() : "");
+ text_3_1.setText(textMap.get("text_3_1") != null ? textMap.get("text_3_1").toString() : "");
+ text_3_2.setText(textMap.get("text_3_2") != null ? textMap.get("text_3_2").toString() : "");
+ text_4_1.setText(textMap.get("text_4_1") != null ? textMap.get("text_4_1").toString() : "");
+ text_4_2.setText(textMap.get("text_4_2") != null ? textMap.get("text_4_2").toString() : "");
+ // 视频
+ List svcStreamInfoList = (List) textMap.get("text_0_1_2");
+ if (svcStreamInfoList != null) {
+ streamInfoAdapter.setStreamInfoList(svcStreamInfoList);
+ }
+
+ // 辅流
+ boolean isSendAux = MeetingController.getInstance().isAux();
+ if (isSendAux) {
+ text_0_1_2_0.setText(textMap.get("text_0_1_2_0") != null ? textMap.get("text_0_1_2_0").toString() : "");
+ text_5_1_0.setText(textMap.get("text_5_1_0") != null ? textMap.get("text_5_1_0").toString() : "");
+ text_6_1_0.setText(textMap.get("text_6_1_0") != null ? textMap.get("text_6_1_0").toString() : "");
+ text_7_1_0.setText(textMap.get("text_7_1_0") != null ? textMap.get("text_7_1_0").toString() : "");
+ text_8_1_0.setText(textMap.get("text_8_1_0") != null ? textMap.get("text_8_1_0").toString() : "");
+ text_9_1_0.setText(textMap.get("text_9_1_0") != null ? textMap.get("text_9_1_0").toString() : "");
+ text_10_1_0.setText(textMap.get("text_10_1_0") != null ? textMap.get("text_10_1_0").toString() : "");
+ text_0_2_2_0.setText("-");
+ text_5_2_0.setText("-");
+ text_6_2_0.setText("-");
+ text_7_2_0.setText("-");
+ text_8_2_0.setText("-");
+ text_9_2_0.setText("-");
+ text_10_2_0.setText("-");
+
+ } else {
+ text_0_1_2_0.setText("-");
+ text_5_1_0.setText("-");
+ text_6_1_0.setText("-");
+ text_7_1_0.setText("-");
+ text_8_1_0.setText("-");
+ text_9_1_0.setText("-");
+ text_10_1_0.setText("-");
+ text_0_2_2_0.setText(textMap.get("text_0_2_2_0") != null ? textMap.get("text_0_2_2_0").toString() : "");
+ text_5_2_0.setText(textMap.get("text_5_2_0") != null ? textMap.get("text_5_2_0").toString() : "");
+ text_6_2_0.setText(textMap.get("text_6_2_0") != null ? textMap.get("text_6_2_0").toString() : "");
+ text_7_2_0.setText(textMap.get("text_7_2_0") != null ? textMap.get("text_7_2_0").toString() : "");
+ text_8_2_0.setText(textMap.get("text_8_2_0") != null ? textMap.get("text_8_2_0").toString() : "");
+ text_9_2_0.setText(textMap.get("text_9_2_0") != null ? textMap.get("text_9_2_0").toString() : "");
+ text_10_2_0.setText(textMap.get("text_10_2_0") != null ? textMap.get("text_10_2_0").toString() : "");
+ }
+ visibilityAux();
+ }
+ }
+
+ private void initEvent() {
+ close.setOnClickListener(v -> {
+ if(null != noOnclickListener){
+ noOnclickListener.onNoClick();
+ }
+ });
+ }
+
+ public void refresh(Map map, boolean isAux){
+ this.textMap = map;
+ this.mIsAux = isAux;
+ initData();
+ }
+}
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SvcVideoFragment.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SvcVideoFragment.kt
new file mode 100644
index 0000000..82fe854
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/SvcVideoFragment.kt
@@ -0,0 +1,729 @@
+package com.tengshisoft.chatmodule.hwclud.ui
+
+import android.os.*
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.RelativeLayout
+import androidx.annotation.RequiresApi
+import com.hjq.toast.ToastUtils
+import com.huawei.ecterminalsdk.base.TsdkConfSpeakerInfo
+import com.huawei.ecterminalsdk.base.TsdkWatchSvcAttendees
+import com.tengshisoft.chatmodule.R
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity
+import com.tengshisoft.chatmodule.beans.MyTsdkCallInfo
+import com.tengshisoft.chatmodule.beans.SvcWatchStatus
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController
+import com.tengshisoft.chatmodule.hwclud.manager.LoginMangerV2
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2
+import com.tengshisoft.chatmodule.hwclud.manager.VideoMgr
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcast
+import com.tengshisoft.chatmodule.hwclud.receiver.LocalBroadcastReceiver
+import com.tengshisoft.chatmodule.hwclud.utils.*
+import com.tenlionsoft.baselib.constant.BroadcastConstant
+import com.tenlionsoft.baselib.constant.ConfConstant
+import com.tenlionsoft.baselib.core.beans.Member
+import kotlinx.android.synthetic.main.fragment_svc_video.*
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import java.io.Serializable
+import java.lang.ref.WeakReference
+import java.util.*
+
+/**
+ * @Time: 2021/8/30
+ * @Author: isoftstone
+ * @Description: SVC 画廊Fragment
+ */
+class SvcVideoFragment : LazyloadFragment(), SVCVideoFragmentContract.SVCVideoFragmentView,
+ IMeetingBottomChanged {
+
+ companion object {
+ private const val TAG = "SVCVideoFragment"
+ const val SVC_REFRESH = 110
+ const val ADD_LOCAL_VIEW = 111
+ const val WHO_SAY = 119
+ const val BAND_WATCH = 150
+ const val EXTRA_MEMBER_LIST = "member_list"
+ const val EXTRA_PAGE = "page"
+ const val EXTRA_CALL_INFO = "call_info"
+ const val REFRESH_SC_STATE = 168
+
+ fun getInstance(
+ mMemberList: List,
+ page: Int,
+ myJoinTSDKCallInfo: MyTsdkCallInfo
+ ): SvcVideoFragment {
+ val fragment = SvcVideoFragment()
+ val bundle = Bundle()
+ bundle.putSerializable(EXTRA_MEMBER_LIST, mMemberList as Serializable)
+ bundle.putInt(EXTRA_PAGE, page)
+ bundle.putSerializable(EXTRA_CALL_INFO, myJoinTSDKCallInfo)
+ fragment.arguments = bundle
+ return fragment
+ }
+ }
+
+ /**
+ * 带宽计算的分辨率
+ */
+ private var bandVideoSize: IntArray? = null
+ private var mMemberList: List? = null
+ private var page: Int = 0
+ private var myJoinTSDkCallInfo: MyTsdkCallInfo? = null
+ private var mPresenter: SVCVideoFragmentPresenter? = null
+ private var uiHandler: UIHandler? = null
+ private var windowAmount = 4
+ private val broadcastNames = arrayOf(
+ BroadcastConstant.ACTION_REFRESH_SVC_VIEW, BroadcastConstant.ACTION_ADD_LOCAL_VIEW,
+ BroadcastConstant.ACTION_SPEAKER_LIST_IND,
+ BroadcastConstant.ACTION_BAND_CHANGED_WATCH, BroadcastConstant.ACTION_SCCHANGEREFRESH
+ )
+
+ private val receiver = LocalBroadcastReceiver { broadcastName, obj ->
+ if (uiHandler == null) {
+ return@LocalBroadcastReceiver
+ }
+ when (broadcastName) {
+ BroadcastConstant.ACTION_REFRESH_SVC_VIEW -> {
+ LogUtil.zzz(TAG, "SVConReceive: broadcastNam" + "e = REFRESH_SVC_VIEW")
+ val message = uiHandler!!.obtainMessage()
+ message.what = SVC_REFRESH
+ uiHandler!!.sendMessage(message)
+ }
+ BroadcastConstant.ACTION_ADD_LOCAL_VIEW -> {
+ LogUtil.zzz(TAG, "SVConReceive: broadcastName = ADD_LOCAL_VIEW")
+ val message1 = uiHandler!!.obtainMessage()
+ message1.what = ADD_LOCAL_VIEW
+ uiHandler!!.sendMessage(message1)
+ }
+ BroadcastConstant.ACTION_SPEAKER_LIST_IND -> {
+ LogUtil.zzz(TAG, "onReceive: SPEAKER_LIST_IND")
+ val message2 = uiHandler!!.obtainMessage()
+ message2.what = WHO_SAY
+ message2.obj = obj
+ uiHandler!!.sendMessage(message2)
+ }
+ BroadcastConstant.ACTION_BAND_CHANGED_WATCH -> {
+ val message3 = uiHandler!!.obtainMessage()
+ message3.what = BAND_WATCH
+ message3.obj = obj
+ uiHandler!!.sendMessage(message3)
+ }
+ BroadcastConstant.ACTION_SCCHANGEREFRESH -> {
+ val message4 = uiHandler!!.obtainMessage()
+ message4.what = REFRESH_SC_STATE
+ message4.obj = obj
+ uiHandler!!.sendMessage(message4)
+ }
+ else -> {
+ }
+ }
+ }
+
+ class UIHandler(fragment: SvcVideoFragment?) : Handler(Looper.getMainLooper()) {
+ private val reference: WeakReference?
+
+ init {
+ reference = WeakReference(fragment)
+ }
+
+ override fun handleMessage(msg: Message) {
+ super.handleMessage(msg)
+ reference?.get()?.let {
+ val svcTSDKAttendees = MeetingMgrV2.getInstance().watchMember
+ when (msg.what) {
+ // 更新Svc视频视图
+ SVC_REFRESH -> {
+ LogUtil.zzz(TAG, "handleMessage: SVC_REFRESH page == ${it.page}")
+ if (MeetingMgrV2.getInstance().selfAttendee == null) {
+ return
+ }
+ refreshSvcView(it, svcTSDKAttendees)
+ }
+ // 添加本地视频视图
+ ADD_LOCAL_VIEW -> {
+ LogUtil.zzz(TAG, "handleMessage: ADD_LOCAL_VIEW$msg")
+ if (it.myJoinTSDkCallInfo!!.isSVC) {
+ if (it.windowAmount == 4) {
+ it.mPresenter?.setVideoContainer(
+ it.floatWindow,
+ it.conf_remote_video_a
+ )
+ } else {
+ it.mPresenter?.setVideoContainer(
+ it.floatWindow,
+ it.conf_remote_video_e
+ )
+ }
+ }
+ }
+ // 语音激励获取声音最大的人的信息处理
+ WHO_SAY -> {
+ // 语音激励返回对象中number开始可能为空,用userId替换
+ val speak = msg.obj as TsdkConfSpeakerInfo
+ val userId = if ((speak.speakers != null) && (speak.speakers.size > 0)) {
+ speak.speakers[0].baseInfo.userId
+ } else {
+ 0
+ }
+ speakerListInd(it, userId, svcTSDKAttendees)
+ }
+ // 实时带宽信息变化
+ BAND_WATCH -> {
+ val videoSize = MeetingController.getInstance().getVideoSize(1)
+ if (null != reference.get()!!.bandVideoSize && reference.get()!!.bandVideoSize?.get(
+ 0
+ ) == videoSize[0]
+ ) {
+ LogUtil.zzz(TAG, "BAND_WATCH", "the same resolution")
+ return@let
+ }
+ reference.get()!!.bandVideoSize = videoSize
+ LogUtil.zzz(TAG, "BAND_WATCH", "svcWatchModel")
+ it.svcWatchModel(it.windowAmount)
+ }
+ // Svc 会议发起选看
+ REFRESH_SC_STATE -> {
+ LogUtil.zzz(TAG, "REFRESH_SC_STATE", "svcWatchModel")
+ it.svcWatchModel(it.windowAmount)
+ }
+ else -> {
+
+ }
+ }
+ }
+ }
+
+ /**
+ * 语音激励获取声音最大的人的信息处理
+ */
+ private fun speakerListInd(
+ it: SvcVideoFragment,
+ userId: Int,
+ svcTSDKAttendees: MutableList
+ ) {
+ if (it.windowAmount == 4) {
+ it.rl_a?.setBackgroundResource(R.drawable.frame_black)
+ it.rl_b?.setBackgroundResource(R.drawable.frame_black)
+ it.rl_c?.setBackgroundResource(R.drawable.frame_black)
+ it.rl_d?.setBackgroundResource(R.drawable.frame_black)
+
+ if (MeetingMgrV2.getInstance().speakMember == null) {
+ return
+ }
+ // 0
+ if (MeetingMgrV2.getInstance().selfAttendee != null && userId != 0 && userId == MeetingMgrV2.getInstance().selfAttendee.userId) {
+ it.rl_a?.setBackgroundResource(R.drawable.frame_blue)
+ return
+ }
+ // 1
+ if (svcTSDKAttendees.size > 0 && userId != 0 && userId == svcTSDKAttendees[0].userId) {
+ it.rl_b?.setBackgroundResource(R.drawable.frame_blue)
+ return
+ }
+ // 2
+ if (svcTSDKAttendees.size > 1 && userId != 0 && userId == svcTSDKAttendees[1].userId) {
+ it.rl_c?.setBackgroundResource(R.drawable.frame_blue)
+ return
+ }
+ // 3
+ if (svcTSDKAttendees.size > 2 && userId != 0 && userId == svcTSDKAttendees[2].userId) {
+ it.rl_d?.setBackgroundResource(R.drawable.frame_blue)
+ }
+ } else if (it.windowAmount == 2) {
+ it.rl_e?.setBackgroundResource(R.drawable.frame_black)
+ it.rl_f?.setBackgroundResource(R.drawable.frame_black)
+ if (MeetingMgrV2.getInstance().speakMember == null) {
+ return
+ }
+ if (MeetingMgrV2.getInstance().selfAttendee != null && userId != 0 && userId == MeetingMgrV2
+ .getInstance().selfAttendee.userId
+ ) {
+ it.rl_e?.setBackgroundResource(R.drawable.frame_blue)
+ return
+ }
+ if (svcTSDKAttendees.isNotEmpty() && userId != 0 && userId == svcTSDKAttendees[0].userId) {
+ it.rl_f?.setBackgroundResource(R.drawable.frame_blue)
+ }
+ }
+ }
+
+ /**
+ * 更新Svc视频视图
+ * @param svcTSDKAttendees svc会议与会者列表
+ */
+ private fun refreshSvcView(it: SvcVideoFragment, svcTSDKAttendees: MutableList) {
+ if (it.windowAmount == 4) {
+ if (it.cl_two_person?.visibility == View.VISIBLE) {
+ it.cl_two_person?.visibility = View.GONE
+ }
+ if (it.cl_four_person?.visibility == View.GONE) {
+ it.cl_four_person?.visibility = View.VISIBLE
+ }
+ it.tv_name_a?.text = MeetingMgrV2.getInstance().selfAttendee.displayName
+ if (svcTSDKAttendees.size == 2) {
+ if (it.conf_remote_video_d?.visibility == View.VISIBLE) {
+ it.conf_remote_video_d?.visibility = View.GONE
+ }
+ it.tv_name_d?.text = ""
+ }
+ var i = 0
+ while (i < svcTSDKAttendees.size) {
+ when (i) {
+ 0 -> it.tv_name_b?.text = svcTSDKAttendees[i].displayName
+ 1 -> it.tv_name_c?.text = svcTSDKAttendees[i].displayName
+ 2 -> {
+ if (it.conf_remote_video_d?.visibility == View.GONE) {
+ it.conf_remote_video_d?.visibility = View.VISIBLE
+ }
+ it.tv_name_d?.text = svcTSDKAttendees[i].displayName
+ }
+ else -> {
+ }
+ }
+ i++
+ }
+ } else {
+ if (it.cl_two_person?.visibility == View.GONE) {
+ it.cl_two_person?.visibility = View.VISIBLE
+ }
+ if (it.cl_four_person?.visibility == View.VISIBLE) {
+ it.cl_four_person?.visibility = View.GONE
+ }
+ it.tv_name_e?.text = MeetingMgrV2.getInstance().selfAttendee.displayName
+ if (svcTSDKAttendees.size > 0) {
+ it.tv_name_f?.text = svcTSDKAttendees[0].displayName
+ }
+ }
+ }
+ }
+
+ fun setMembers(members: List?) {
+ mMemberList = members
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ MeetingController.getInstance().registerBottomChanged(this)
+ mPresenter = SVCVideoFragmentPresenter()
+ return super.onCreateView(inflater, container, savedInstanceState)
+ }
+
+ override fun setContentView(): Int {
+ return R.layout.fragment_svc_video
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ override fun getData() {
+ val argument = arguments
+ mMemberList = argument?.getSerializable(EXTRA_MEMBER_LIST) as List
+ page = argument.getInt(EXTRA_PAGE)
+ myJoinTSDkCallInfo = argument.getSerializable(EXTRA_CALL_INFO) as MyTsdkCallInfo
+ }
+
+ override fun initView() {
+ uiHandler = UIHandler(this)
+ // 预加载的时候已经知道是两个还是4个,所以应设置好预览的状态,不然划过来的时候是默认的4个,一下子又刷成两个
+ if (mMemberList == null) return
+ if (mMemberList!!.size == 2 || (mMemberList!!.size - 1) % 3 == 1 && (mMemberList!!.size - 1) / 3 + 1 == page) {
+ if (cl_two_person?.visibility == View.GONE) {
+ cl_two_person?.visibility = View.VISIBLE
+ }
+ if (cl_four_person?.visibility == View.VISIBLE) {
+ cl_four_person!!.visibility = View.GONE
+ }
+ windowAmount = 2
+ } else {
+ if (cl_two_person?.visibility == View.VISIBLE) {
+ cl_two_person?.visibility = View.GONE
+ }
+ if (cl_four_person?.visibility == View.GONE) {
+ cl_four_person!!.visibility = View.VISIBLE
+ }
+ windowAmount = 4
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ override fun initListener() {
+ container_fl.setOnClickListener {
+ showLabel()
+ }
+ // 4窗口点击事件
+ conf_remote_video_a.setGestureListener({
+ showLabel()
+ }, {
+ // 自己不选看
+ })
+ conf_remote_video_b.setGestureListener({
+ showLabel()
+ }, {
+ doubleClickRemoteVideo(0)
+ })
+ conf_remote_video_c.setGestureListener({
+ showLabel()
+ }, {
+ doubleClickRemoteVideo(1)
+ })
+ conf_remote_video_d.setGestureListener({
+ showLabel()
+ }, {
+ doubleClickRemoteVideo(2)
+ })
+
+ // 2窗口点击事件
+ conf_remote_video_e.setGestureListener({
+ showLabel()
+ }, {
+ // 自己不选看
+ })
+ conf_remote_video_f.setGestureListener({
+ showLabel()
+ }, {
+ if (MeetingController.getInstance().videoJoinMeeting < 3) {
+ return@setGestureListener
+ }
+ doubleClickRemoteVideo(0)
+ })
+ }
+
+ /**
+ * 双击远程视频窗口事件处理
+ *
+ * @param index 远程视频窗口位置
+ */
+ private fun doubleClickRemoteVideo(index: Int) {
+ // SVC双平面广播和点名 无感知、可选看
+ if (MeetingController.getInstance().broadIngMember != null) {
+ ToastUtils.show(getString(R.string.cloudLink_meeting_isBroading))
+ return
+ }
+ // 大会模式 不可选看
+ if (MeetingController.getInstance().isSvcBigConf && !MeetingController.getInstance().isHasChairman) {
+ ToastUtils.show(getString(R.string.cloudLink_svc_big_conf_hint))
+ return
+ }
+ if (ListTools.isThan(MeetingMgrV2.getInstance().watchMember, index)) {
+ val member: Member = MeetingMgrV2.getInstance().watchMember[index]
+ LocalBroadcast.getInstance()
+ .sendBroadcast(Constant.SPONSOR_MEETING_WATCH_MEMBER, member)
+ }
+ }
+
+
+ private fun fillContainer(windowAmount: Int) {
+ if (VideoMgr.getInstance().localVideoView != null) {
+ VideoMgr.getInstance().localVideoView.visibility = View.VISIBLE
+ }
+ when (windowAmount) {
+ 2 -> {
+ mPresenter?.setVideoContainer(
+ floatWindow,
+ conf_remote_video_e, conf_remote_video_f,
+ null, null
+ )
+ }
+ 4 -> {
+ mPresenter?.setVideoContainer(
+ floatWindow,
+ conf_remote_video_a, conf_remote_video_b,
+ conf_remote_video_c, conf_remote_video_d
+ )
+ }
+ }
+ }
+
+// override fun onConfigurationChanged(newConfig: Configuration) {
+// super.onConfigurationChanged(newConfig)
+// if (mPresenter != null) {
+// setAutoDir(newConfig)
+// }
+// }
+
+ /**
+ * 横竖屏切换时,让小画面里面的内容也跟着旋转
+ */
+// private fun setAutoDir(configuration: Configuration) {
+// val mOrientation = configuration.orientation
+// mPresenter?.setAutoRotation(conf_remote_video_a, true, mOrientation)
+// mPresenter?.setAutoRotation(conf_remote_video_b, true, mOrientation)
+// mPresenter?.setAutoRotation(conf_remote_video_c, true, mOrientation)
+// mPresenter?.setAutoRotation(conf_remote_video_d, true, mOrientation)
+// mPresenter?.setAutoRotation(conf_remote_video_e, true, mOrientation)
+// mPresenter?.setAutoRotation(conf_remote_video_f, true, mOrientation)
+// mPresenter?.setAutoRotation(floatWindow, true, mOrientation)
+// }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ override fun lazyLoad() {
+ LogUtil.zzz(TAG, "lazyLoad: page == $page")
+ if (activity != null && activity is SponsorMeetingActivity) {
+ (activity as SponsorMeetingActivity).setRemote(false)
+ }
+ //两个与会者 || 计算完前面页数分配后剩一个人&& 最后一页 加载2个人的页面
+ if (mMemberList == null) return
+ if (mMemberList!!.size == 2 || (mMemberList!!.size - 1) % 3 == 1 && (mMemberList!!.size - 1) / 3 + 1 == page) {
+ if (cl_two_person?.visibility == View.GONE) {
+ cl_two_person?.visibility = View.VISIBLE
+ }
+ if (cl_four_person?.visibility == View.VISIBLE) {
+ cl_four_person!!.visibility = View.GONE
+ }
+ windowAmount = 2
+
+ } else {
+ if (cl_two_person?.visibility == View.VISIBLE) {
+ cl_two_person?.visibility = View.GONE
+ }
+ if (cl_four_person?.visibility == View.GONE) {
+ cl_four_person?.visibility = View.VISIBLE
+ }
+ windowAmount = 4
+ }
+ setWatchMembers()
+ fillContainer(windowAmount)
+ LogUtil.zzz(TAG, "lazyLoad", "svcWatchModel")
+ svcWatchModel(windowAmount)
+ }
+
+ private var watchMembers: List = emptyList()
+
+ /**
+ * 设置选看与会者
+ * 移除音频入会 或者已离会的与会者 或者自己
+ */
+ private fun setWatchMembers() {
+ val sipNumber = LoginMangerV2.getInstance().terminal
+ var attendeeList: MutableList = ArrayList(mMemberList!!)
+ val it = attendeeList.iterator()
+ while (it.hasNext()) {
+ val attendee = it.next()
+ if (attendee.isSelf || UIUtil.getFormatNumber(attendee.number) == sipNumber || attendee.isAudio || attendee.status != ConfConstant.ParticipantStatus.IN_CONF) {
+ it.remove()
+ }
+ }
+
+ if (attendeeList.size > 3 && attendeeList.size >= page * 3) {
+ attendeeList = attendeeList.subList((page - 1) * 3, page * 3)
+ }
+ if (attendeeList.size > 3 && attendeeList.size < page * 3) {
+ attendeeList = attendeeList.subList((page - 1) * 3, attendeeList.size)
+ }
+ watchMembers = ArrayList(attendeeList)
+ }
+
+ fun getWatchMembers(): List {
+ return watchMembers
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ LogUtil.zzz(TAG, "onViewCreated: page == $page")
+ if (isLoad) {
+ fillContainer(windowAmount)
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ LogUtil.zzz(TAG, "onPause removeSurfaceView$isVisible")
+ if (isVisible) {
+ mPresenter?.removeSurfaceView(VideoMgr.getInstance().localVideoView)
+ }
+ }
+
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun onResumeLoadSurface() {
+ LogUtil.zzz(TAG, "onResumeLoadSurface: page == $page")
+ if (activity != null && activity is SponsorMeetingActivity) {
+ (activity as SponsorMeetingActivity).setRemote(false)
+ }
+ //两个与会者 || 计算完前面页数分配后剩一个人&& 最后一页 加载2个人的页面
+ if (mMemberList == null) return
+ if (mMemberList!!.size == 2 || (mMemberList!!.size - 1) % 3 == 1 && (mMemberList!!.size - 1) / 3 + 1 == page) {
+ if (cl_two_person?.visibility == View.GONE) {
+ cl_two_person?.visibility = View.VISIBLE
+ }
+ if (cl_four_person?.visibility == View.VISIBLE) {
+ cl_four_person!!.visibility = View.GONE
+ }
+ windowAmount = 2
+
+ } else {
+ if (cl_two_person?.visibility == View.VISIBLE) {
+ cl_two_person?.visibility = View.GONE
+ }
+ if (cl_four_person?.visibility == View.GONE) {
+ cl_four_person?.visibility = View.VISIBLE
+ }
+ windowAmount = 4
+ }
+ fillContainer(windowAmount)
+ LogUtil.zzz(TAG, "onResumeLoadSurface", "svcWatchModel")
+ svcWatchModel(windowAmount)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ override fun onResume() {
+ super.onResume()
+ LogUtil.zzz(TAG, "onResume $isAvailable")
+ if (isAvailable) {
+ onResumeLoadSurface()
+ }
+ }
+
+ /**
+ * 最关键部分
+ * @param isVisibleToUser 画面是否可见
+ */
+ @Suppress("DEPRECATION")
+ override fun setUserVisibleHint(isVisibleToUser: Boolean) {
+ super.setUserVisibleHint(isVisibleToUser)
+ isAvailable = isVisibleToUser
+ if (isVisibleToUser) {
+ //画面可见时,注册广播
+ LocalBroadcast.getInstance().registerBroadcast(receiver, broadcastNames)
+ } else {
+ mPresenter?.let {
+ VideoMgr.getInstance().removeAllSvcVideoWindow()
+ it.removeVideo(isAdded)
+ }
+ //画面不可见时,及时反注册广播
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames)
+ }
+ }
+
+ private fun svcWatchModel(windowAmount: Int) {
+ if (!MeetingController.getInstance().svcBigConfListFlag) {
+ watchSvcAttendeesDel(windowAmount)
+ } else {
+ watchSvcAttendees(windowAmount)
+ }
+ if (getWatchMembers().size == 2) {
+ conf_remote_video_d?.removeAllViews()
+ }
+ MeetingController.getInstance().svcBigConfListFlag =
+ !MeetingController.getInstance().svcBigConfListFlag
+ }
+
+ private fun watchSvcAttendeesDel(windowAmount: Int) {
+ GlobalScope.launch {
+ LogUtil.zzz(TAG, "SVC watchSvcAttendeesDel: $windowAmount page == $page")
+ if (myJoinTSDkCallInfo == null) return@launch
+ var sSrcTableEnd = myJoinTSDkCallInfo!!.ssrcTableEnd
+
+ /**
+ * 条件1.SVC第一个画面是自己
+ * 条件2.自己的画面从本地拿
+ * 结论:如果windowAmount == 2 选看第二个与会者,如果windowAmount == 4,选看后三个与会者
+ * 里的修改对应了VideoMgr.setSvcWatchIndAdd方法的修改
+ */
+ val realCount = windowAmount - 1
+ val watchAttendeeList: MutableList = ArrayList()
+ MeetingController.getInstance().svcWatchStatus = SvcWatchStatus.GALLERY_WATCH
+ for (i in 0 until realCount) {
+ val watchSvcAttendees = TsdkWatchSvcAttendees()
+ val videoSize = MeetingController.getInstance().getVideoSize(realCount)
+ watchSvcAttendees.width = videoSize[0]
+ watchSvcAttendees.height = videoSize[1]
+ watchSvcAttendees.lableId = sSrcTableEnd--
+ watchAttendeeList.add(watchSvcAttendees)
+ }
+ val result = MeetingMgrV2.getInstance().watchFourSvcAttendee(watchAttendeeList, page)
+ if (0 != result) {
+ LogUtil.zzz(TAG, "SVC watchSvcAttendeesDel fail result : $result")
+ }
+ }
+ }
+
+ /**
+ * 观看与会者接口调用的数据处理
+ * 以下代码为文档上示范代码
+ * @param windowAmount 观看数量 1或3
+ */
+ private fun watchSvcAttendees(windowAmount: Int) {
+ GlobalScope.launch {
+ LogUtil.zzz(TAG, "SVC watchSvcAttendees: $windowAmount page == $page")
+ if (myJoinTSDkCallInfo == null) return@launch
+ val sSrcTableStart = myJoinTSDkCallInfo!!.ssrcTableStart
+ /**
+ * 条件1.SVC第一个画面是自己
+ * 条件2.自己的画面从本地拿
+ * 结论:如果windowAmount == 2 选看第二个与会者,如果windowAmount == 4,选看后三个与会者
+ * 里的修改对应了VideoMgr.setSvcWatchIndAdd方法的修改
+ */
+ MeetingController.getInstance().svcWatchStatus = SvcWatchStatus.GALLERY_WATCH
+ val realCount = windowAmount - 1
+ val watchAttendeeList: MutableList = ArrayList()
+ var i = sSrcTableStart
+ while (i < sSrcTableStart + realCount) {
+ val watchSvcAttendees = TsdkWatchSvcAttendees()
+ val videoSize = MeetingController.getInstance().getVideoSize(realCount)
+ watchSvcAttendees.width = videoSize[0]
+ watchSvcAttendees.height = videoSize[1]
+ watchSvcAttendees.lableId = i
+ watchAttendeeList.add(watchSvcAttendees)
+ i++
+ }
+ val result = MeetingMgrV2.getInstance().watchFourSvcAttendee(watchAttendeeList, page)
+ if (0 != result) {
+ LogUtil.zzz(TAG, "watchSvcAttendee fail result : $result")
+ }
+ }
+ }
+
+ override fun onDestroyView() {
+ super.onDestroyView()
+ MeetingController.getInstance().unRegisterBottomChanged(this)
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ LogUtil.zzz(TAG, "onDestroy: page == $page")
+ //这句很关键 第一次进入时SDK机制引起的莫名错误 这句防止广播重复注册
+ LocalBroadcast.getInstance().unRegisterBroadcast(receiver, broadcastNames)
+ }
+
+ override fun showNetworkError() {}
+ override fun showLoading() {}
+ override fun showLoading(tip: String?) {}
+ override fun hideLoading() {}
+
+ private val floatWindow: FrameLayout?
+ get() = null
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun showLabel() {
+ if (activity is SponsorMeetingActivity) {
+ (activity as SponsorMeetingActivity?)!!.showLabel()
+ }
+ }
+
+ override fun bottomChanged(visibility: Int) {
+ if (visibility == View.VISIBLE) {
+ setViewMarginBottom(tv_name_c, 56)
+ setViewMarginBottom(tv_name_d, 56)
+ setViewMarginBottom(tv_name_f, 56)
+ } else {
+ setViewMarginBottom(tv_name_c, 0)
+ setViewMarginBottom(tv_name_d, 0)
+ setViewMarginBottom(tv_name_f, 0)
+ }
+ }
+
+ override fun isAvailable(): Boolean {
+ return isAvailable
+ }
+
+ private fun setViewMarginBottom(view: View?, marginBottom: Int) {
+ val layoutParams = view?.layoutParams as RelativeLayout.LayoutParams
+ layoutParams.bottomMargin = ScreenUtil.dp2ps(requireContext(), marginBottom.toFloat())
+ view.layoutParams = layoutParams
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VideoLayout.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VideoLayout.kt
new file mode 100644
index 0000000..229a62e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VideoLayout.kt
@@ -0,0 +1,33 @@
+package com.tengshisoft.chatmodule.hwclud.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+
+/**
+ * @author Yason
+ * @date 2021/1/4
+ * @explain SVC画廊模式四个或三个与会者时的页面计算
+ * */
+class VideoLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) {
+
+ /**
+ * Android horizontal and vertical screen switching will redraw the view and calculate the width
+ * and height of the view. We manually designed the view length ratio of 16:9 in onMeasure
+ * measurement to achieve the standard video resolution size and solve the problem of black edge
+ * */
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val widthMode = MeasureSpec.getMode(widthMeasureSpec)
+ val heightMode = MeasureSpec.getMode(heightMeasureSpec)
+ var widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ var heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ if (widthSize > heightSize) {
+ widthSize = (heightSize * 1f / 9 * 16).toInt()
+ } else {
+ heightSize = (widthSize * 1f / 9 * 16).toInt()
+ }
+ val newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode)
+ val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode)
+ super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec)
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VideoTwoLayout.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VideoTwoLayout.kt
new file mode 100644
index 0000000..3c75f30
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VideoTwoLayout.kt
@@ -0,0 +1,33 @@
+package com.tengshisoft.chatmodule.hwclud.ui
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.FrameLayout
+
+/**
+ * @author Yason
+ * @date 2021/1/4
+ * @explain SVC画廊模式两个与会者时的页面计算
+ * */
+class VideoTwoLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) {
+
+ /**
+ * Android horizontal and vertical screen switching will redraw the view and calculate the width
+ * and height of the view. We manually designed the view length ratio of 16:9 in onMeasure
+ * measurement to achieve the standard video resolution size and solve the problem of black edge
+ * */
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ val widthMode = MeasureSpec.getMode(widthMeasureSpec)
+ val heightMode = MeasureSpec.getMode(heightMeasureSpec)
+ var widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ val heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ widthSize = if (widthSize > heightSize) { // 横屏
+ (heightSize * 1f / 2 / 9 * 16).toInt()
+ } else { // 竖屏
+ (heightSize * 1f / 2 / 16 * 9).toInt()
+ }
+ val newWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, widthMode)
+ val newHeightMeasureSpec = MeasureSpec.makeMeasureSpec(heightSize, heightMode)
+ super.onMeasure(newWidthMeasureSpec, newHeightMeasureSpec)
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VoiceConfPresenter.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VoiceConfPresenter.java
new file mode 100644
index 0000000..251cc44
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/VoiceConfPresenter.java
@@ -0,0 +1,142 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.app.Activity;
+
+import com.huawei.ecterminalsdk.base.TsdkDeviceState;
+import com.huawei.ecterminalsdk.base.TsdkOnEvtDeviceStateNotify;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.beans.TSDKErrorConstant;
+import com.tengshisoft.chatmodule.hwclud.controller.MeetingController;
+import com.tengshisoft.chatmodule.hwclud.manager.AudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.SwitchAudioRouteManager;
+import com.tengshisoft.chatmodule.hwclud.utils.Constant;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+/**
+ * @Time: 2021/9/2
+ * @Author: isoftstone
+ * @Description: 语音会议控制类
+ */
+public class VoiceConfPresenter extends BaseConfPresenter {
+ private static final String TAG = VoiceConfPresenter.class.getSimpleName();
+
+ public VoiceConfPresenter(BaseConfContract.BaseConfView mView, Activity context) {
+ super(mView, context);
+ this.mView = mView;
+ }
+
+ @Override
+ public void detachView() {
+ super.detachView();
+ }
+
+ public void showBitStream() {
+ mView.showStreamDialog(true);
+ }
+
+ public void requestChairman(String chairmanPassword) {
+ int result = MeetingMgrV2.getInstance().requestChairman(chairmanPassword);
+ // 会议已存在主席
+ if (result == TSDKErrorConstant.TSDK_MEETING_EXIST_CHAIN_MAN_CODE) {
+ MeetingController.getInstance().setChairman(false);
+ mView.showCustomToast(R.string.cloudLink_meeting_requestChairmanFailed);
+ } else if (result == 0) {
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, chairmanPassword);
+ }
+ }
+
+ public void releaseChairman() {
+ int result = MeetingMgrV2.getInstance().releaseChairman();
+ if (result == 0) {
+ mView.showCustomToast(context.getString(R.string.cloudLink_meeting_releaseChairmanSuccess));
+ EncryptedSPTool.putString(Constant.MEETING_CHAIRMAN, "");
+ }
+ }
+
+ public void handUpSelf(boolean isHandUp) {
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ return;
+ }
+ int result = MeetingMgrV2.getInstance().handup(isHandUp, self);
+ LogUtil.zzz(TAG, "handUpSelf()", "isHandUp is " + isHandUp + "; result is " + result);
+ if (isHandUp) {
+ switch (result) {
+ case 0:
+ mView.showCustomToast(R.string.cloudLink_meeting_handupSuccess);
+ break;
+ case TSDKErrorConstant.TSDK_MEETING_NOT_CHAIN_CODE:
+ mView.showCustomToast(R.string.cloudLink_meeting_notChainmanHandupFail);
+ break;
+ case TSDKErrorConstant.TSDK_MEETING_HAND_UP_FIT_CODE:
+ // 快速点击导致两次举手状态一致导致举手失败
+ break;
+ default:
+ mView.showCustomToast(R.string.cloudLink_meeting_handupFail);
+ break;
+ }
+ } else {
+ switch (result) {
+ case 0:
+ mView.showCustomToast(R.string.cloudLink_meeting_handDownSuccess);
+ break;
+ case TSDKErrorConstant.TSDK_MEETING_HAND_UP_FIT_CODE:
+ // 快速点击导致两次举手状态一致导致举手失败
+ break;
+ default:
+ mView.showCustomToast(R.string.cloudLink_meeting_cancelHandupFail);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 转换SC
+ * @param obj 设备状态
+ */
+ public void syncState(TsdkOnEvtDeviceStateNotify.Param obj) {
+ LogUtil.zzz(TAG, "Switching SC State obj = " + obj);
+ TsdkDeviceState deviceState = obj.getDeviceState();
+ // 开音
+ CallMgrV2.getInstance().muteMic(obj.getCallId(), deviceState.getMicState() == 1);
+ new AudioRouteManager.Builder()
+ .setHasSetting(SwitchAudioRouteManager.Companion.getInstance().isSettingAudioRoute())
+ .setIsVideo(deviceState.getSpeakerState() == 1)
+ .setHeadsetPlug(SwitchAudioRouteManager.Companion.getInstance().getHeadsetPlug())
+ .build();
+ }
+
+ /**
+ * @param isMute ture是静音
+ * @return
+ */
+ public boolean muteSelf(boolean isMute) {
+ // true 是静音
+ Member self = MeetingMgrV2.getInstance().getCurrentConferenceSelf();
+ if (self == null) {
+ LogUtil.zzz(TAG, "Mute fail self = null");
+ return false;
+ }
+ int result = MeetingMgrV2.getInstance().muteAttendee(self, isMute);
+ if (result == 0) {
+ mView.updateView(Constant.CALL_UPDATE_VOICE, isMute, "");
+ }
+ return result == 0;
+ }
+
+ public void setConfSubtitle(boolean isSubtitleEnable) {
+ boolean isPendingEnable = !isSubtitleEnable;
+ int result = MeetingMgrV2.getInstance().setConfSubtitle(isPendingEnable);
+ if (result == 0) {
+ mView.showCustomToast(isSubtitleEnable ? R.string.cloudLink_meeting_caption_disabled :
+ R.string.cloudLink_meeting_caption_enabled);
+ } else {
+ mView.showCustomToast(isSubtitleEnable ? R.string.cloudLink_meeting_disabled_caption_failed :
+ R.string.cloudLink_meeting_caption_enable_failed);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ZoomLayout.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ZoomLayout.java
new file mode 100644
index 0000000..aca7adf
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/ui/ZoomLayout.java
@@ -0,0 +1,503 @@
+package com.tengshisoft.chatmodule.hwclud.ui;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.widget.OverScroller;
+import android.widget.RelativeLayout;
+
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.utils.LogUtil;
+
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+
+/**
+ * @Time: 2021/8/4
+ * @Author: isoftstone
+ * @Description:
+ */
+public class ZoomLayout extends RelativeLayout {
+ private static final String TAG = "ZoomLayout";
+ private static final float DEFAULT_MIN_ZOOM = 1.0f;
+ private static final float DEFAULT_MAX_ZOOM = 2.0f;
+ private static final float DEFAULT_DOUBLE_CLICK_ZOOM = 2.0f;
+
+ private float mDoubleClickZoom;
+ private float mMinZoom;
+ private float mMaxZoom;
+ private float mCurrentZoom = 1;
+ private int mMinimumVelocity;
+ private int mMaximumVelocity;
+ // 是否已经开始滑动
+ private boolean mScrollBegin;
+
+ private ScaleGestureDetector mScaleDetector;
+ private GestureDetector mGestureDetector;
+ private OverScroller mOverScroller;
+ private ScaleHelper mScaleHelper;
+ private AccelerateInterpolator mAccelerateInterpolator;
+ private DecelerateInterpolator mDecelerateInterpolator;
+ private ZoomLayoutGestureListener mZoomLayoutGestureListener;
+ private int mLastChildHeight;
+ private int mLastChildWidth;
+ private int mLastHeight;
+ private int mLastWidth;
+ private int mLastCenterX;
+ private int mLastCenterY;
+ private boolean mNeedReScale;
+
+ public ZoomLayout(Context context) {
+ super(context);
+ init(context, null);
+ }
+
+ public ZoomLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ public ZoomLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs);
+ }
+
+
+ private void init(Context context, @Nullable AttributeSet attrs) {
+ mScaleDetector = new ScaleGestureDetector(context, mSimpleOnScaleGestureListener);
+ mGestureDetector = new GestureDetector(context, mSimpleOnGestureListener);
+ mOverScroller = new OverScroller(getContext());
+ mScaleHelper = new ScaleHelper();
+ final ViewConfiguration configuration = ViewConfiguration.get(getContext());
+ mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+ mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+ setWillNotDraw(false);
+ if (attrs != null) {
+ TypedArray array = null;
+ try {
+ array = context.obtainStyledAttributes(attrs, R.styleable.ZoomLayout);
+ mMinZoom = array.getFloat(R.styleable.ZoomLayout_min_zoom, DEFAULT_MIN_ZOOM);
+ mMaxZoom = array.getFloat(R.styleable.ZoomLayout_max_zoom, DEFAULT_MAX_ZOOM);
+ mDoubleClickZoom = array.getFloat(R.styleable.ZoomLayout_double_click_zoom, DEFAULT_DOUBLE_CLICK_ZOOM);
+ if (mDoubleClickZoom > mMaxZoom) {
+ mDoubleClickZoom = mMaxZoom;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, TAG, e);
+ } finally {
+ if (array != null) {
+ array.recycle();
+ }
+ }
+ }
+ }
+
+ private ScaleGestureDetector.SimpleOnScaleGestureListener mSimpleOnScaleGestureListener = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
+ @Override
+ public boolean onScale(ScaleGestureDetector detector) {
+ if (!isEnabled()) {
+ return false;
+ }
+ float newScale;
+ newScale = mCurrentZoom * detector.getScaleFactor();
+/* if (newScale > mMaxZoom) {
+ newScale = mMaxZoom;
+ } else if (newScale < mMinZoom) {
+ newScale = mMinZoom;
+ }*/
+ setScale(newScale, (int) detector.getFocusX(), (int) detector.getFocusY());
+ return true;
+ }
+
+ @Override
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ if (mZoomLayoutGestureListener != null) {
+ mZoomLayoutGestureListener.onScaleGestureBegin();
+ }
+ return true;
+ }
+
+ @Override
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ float newScale;
+ newScale = mCurrentZoom * detector.getScaleFactor();
+ if (newScale > mMaxZoom) {
+ newScale = mMaxZoom;
+ } else if (newScale < mMinZoom) {
+ newScale = mMinZoom;
+ }
+ setScale(newScale, (int) detector.getFocusX(), (int) detector.getFocusY());
+ }
+ };
+
+ private GestureDetector.SimpleOnGestureListener mSimpleOnGestureListener = new GestureDetector.SimpleOnGestureListener() {
+
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ if (!mOverScroller.isFinished()) {
+ mOverScroller.abortAnimation();
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ // 双击缩放
+/* float newScale;
+ if (mCurrentZoom < 1) {
+ newScale = 1;
+ } else if (mCurrentZoom < mDoubleClickZoom) {
+ newScale = mDoubleClickZoom;
+ } else {
+ newScale = 1;
+ }
+ smoothScale(newScale, (int) e.getX(), (int) e.getY());
+ if (mZoomLayoutGestureListener != null) {
+ mZoomLayoutGestureListener.onDoubleTap();
+ }*/
+ return true;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
+ if (!isEnabled()) {
+ return false;
+ }
+ if (!mScrollBegin) {
+ mScrollBegin = true;
+ if (mZoomLayoutGestureListener != null) {
+ mZoomLayoutGestureListener.onScrollBegin();
+ }
+ }
+ processScroll((int) distanceX, (int) distanceY, getScrollRangeX(), getScrollRangeY());
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
+ if (!isEnabled()) {
+ return false;
+ }
+ fling((int) -velocityX, (int) -velocityY);
+ return true;
+ }
+ };
+
+
+ private boolean fling(int velocityX, int velocityY) {
+ if (Math.abs(velocityX) < mMinimumVelocity) {
+ velocityX = 0;
+ }
+ if (Math.abs(velocityY) < mMinimumVelocity) {
+ velocityY = 0;
+ }
+ final int scrollY = getScrollY();
+ final int scrollX = getScrollX();
+ final boolean canFlingX = (scrollX > 0 || velocityX > 0) &&
+ (scrollX < getScrollRangeX() || velocityX < 0);
+ final boolean canFlingY = (scrollY > 0 || velocityY > 0) &&
+ (scrollY < getScrollRangeY() || velocityY < 0);
+ boolean canFling = canFlingY || canFlingX;
+ if (canFling) {
+ velocityX = Math.max(-mMaximumVelocity, Math.min(velocityX, mMaximumVelocity));
+ velocityY = Math.max(-mMaximumVelocity, Math.min(velocityY, mMaximumVelocity));
+ int height = getHeight() - getPaddingBottom() - getPaddingTop();
+ int width = getWidth() - getPaddingRight() - getPaddingLeft();
+ int bottom = getContentHeight();
+ int right = getContentWidth();
+ mOverScroller.fling(getScrollX(), getScrollY(), velocityX, velocityY, 0, Math.max(0, right - width), 0,
+ Math.max(0, bottom - height), 0, 0);
+ notifyInvalidate();
+ return true;
+ }
+ return false;
+ }
+
+ public void smoothScale(float newScale, int centerX, int centerY) {
+ if (mCurrentZoom > newScale) {
+ if (mAccelerateInterpolator == null) {
+ mAccelerateInterpolator = new AccelerateInterpolator();
+ }
+ mScaleHelper.startScale(mCurrentZoom, newScale, centerX, centerY, mAccelerateInterpolator);
+ } else {
+ if (mDecelerateInterpolator == null) {
+ mDecelerateInterpolator = new DecelerateInterpolator();
+ }
+ mScaleHelper.startScale(mCurrentZoom, newScale, centerX, centerY, mDecelerateInterpolator);
+ }
+ notifyInvalidate();
+ }
+
+ private void notifyInvalidate() {
+ // 效果和 invalidate 一样,但是会使得动画更平滑
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+
+
+ public void setScale(float scale, int centerX, int centerY) {
+ mLastCenterX = centerX;
+ mLastCenterY = centerY;
+ float preScale = mCurrentZoom;
+ mCurrentZoom = scale;
+ int sX = getScrollX();
+ int sY = getScrollY();
+ int dx = (int) ((sX + centerX) * (scale / preScale - 1));
+ int dy = (int) ((sY + centerY) * (scale / preScale - 1));
+ if (getScrollRangeX() < 0) {
+ child().setPivotX(child().getWidth() / 2);
+ child().setTranslationX(0);
+ } else {
+ child().setPivotX(0);
+ int willTranslateX = -(child().getLeft());
+ child().setTranslationX(willTranslateX);
+ }
+ if (getScrollRangeY() < 0) {
+ child().setPivotY(child().getHeight() / 2);
+ child().setTranslationY(0);
+ } else {
+ int willTranslateY = -(child().getTop());
+ child().setTranslationY(willTranslateY);
+ child().setPivotY(0);
+ }
+ child().setScaleX(mCurrentZoom);
+ child().setScaleY(mCurrentZoom);
+ processScroll(dx, dy, getScrollRangeX(), getScrollRangeY());
+ notifyInvalidate();
+ }
+
+
+ private void processScroll(int deltaX, int deltaY,
+ int scrollRangeX, int scrollRangeY) {
+ int oldScrollX = getScrollX();
+ int oldScrollY = getScrollY();
+ int newScrollX = oldScrollX + deltaX;
+ int newScrollY = oldScrollY + deltaY;
+ final int left = 0;
+ final int right = scrollRangeX;
+ final int top = 0;
+ final int bottom = scrollRangeY;
+
+ if (newScrollX > right) {
+ newScrollX = right;
+ } else if (newScrollX < left) {
+ newScrollX = left;
+ }
+
+ if (newScrollY > bottom) {
+ newScrollY = bottom;
+ } else if (newScrollY < top) {
+ newScrollY = top;
+ }
+ if (newScrollX < 0) {
+ newScrollX = 0;
+ }
+ if (newScrollY < 0) {
+ newScrollY = 0;
+ }
+// LogUtil.zzz(TAG, "newScrollX = " + newScrollX + " ,newScrollY = " + newScrollY);
+ scrollTo(newScrollX, newScrollY);
+ }
+
+
+ private int getScrollRangeX() {
+ final int contentWidth = getWidth() - getPaddingRight() - getPaddingLeft();
+ return (getContentWidth() - contentWidth);
+ }
+
+ private int getContentWidth() {
+ return (int) (child().getWidth() * mCurrentZoom);
+ }
+
+ private int getScrollRangeY() {
+ final int contentHeight = getHeight() - getPaddingBottom() - getPaddingTop();
+ return getContentHeight() - contentHeight;
+ }
+
+ private int getContentHeight() {
+ return (int) (child().getHeight() * mCurrentZoom);
+ }
+
+ private View child() {
+ return getChildAt(0);
+
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ if (mNeedReScale) {
+ // 需要重新刷新,因为宽高已经发生变化
+ setScale(mCurrentZoom, mLastCenterX, mLastCenterY);
+ mNeedReScale = false;
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ child().setClickable(true);
+ if (child().getHeight() < getHeight() || child().getWidth() < getWidth()) {
+ setGravity(Gravity.CENTER);
+ } else {
+ setGravity(Gravity.TOP);
+ }
+ if (mLastChildWidth != child().getWidth() || mLastChildHeight != child().getHeight() || mLastWidth != getWidth()
+ || mLastHeight != getHeight()) {
+ // 宽高变化后,记录需要重新刷新,放在下次 onLayout 处理,避免 View 的一些配置:比如 getTop() 没有初始化好
+ // 下次放在 onLayout 处理的原因是 setGravity 会在 onLayout 确定完位置,这时候去 setScale 导致位置的变化就不会导致用户看到
+ // 闪一下的问题
+ mNeedReScale = true;
+ }
+ mLastChildWidth = child().getWidth();
+ mLastChildHeight = child().getHeight();
+ mLastWidth = child().getWidth();
+ mLastHeight = getHeight();
+ if (mNeedReScale) {
+ notifyInvalidate();
+ }
+ }
+
+ @Override
+ public void computeScroll() {
+ super.computeScroll();
+ if (mScaleHelper.computeScrollOffset()) {
+ setScale(mScaleHelper.getCurScale(), mScaleHelper.getStartX(), mScaleHelper.getStartY());
+ }
+ if (mOverScroller.computeScrollOffset()) {
+ int oldX = getScrollX();
+ int oldY = getScrollY();
+ int x = mOverScroller.getCurrX();
+ int y = mOverScroller.getCurrY();
+ if (oldX != x || oldY != y) {
+ final int rangeY = getScrollRangeY();
+ final int rangeX = getScrollRangeX();
+ processScroll(x - oldX, y - oldY, rangeX, rangeY);
+ }
+ if (!mOverScroller.isFinished()) {
+ notifyInvalidate();
+ }
+ }
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+
+ switch (ev.getAction() & MotionEvent.ACTION_MASK){
+ case MotionEvent.ACTION_UP:
+ // 最后一根手指抬起的时候,重置 mScrollBegin 为 false
+ mScrollBegin = false;
+ getParent().requestDisallowInterceptTouchEvent(false);
+ break;
+ case MotionEvent.ACTION_POINTER_DOWN:
+ // 双指按下时拦截父控件事件,否则横向水平缩放会被viewPager拦截
+ getParent().requestDisallowInterceptTouchEvent(true);
+ break;
+
+ }
+
+ mGestureDetector.onTouchEvent(ev);
+ mScaleDetector.onTouchEvent(ev);
+ return super.dispatchTouchEvent(ev);
+ }
+
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+ final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+ getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin
+ + widthUsed, lp.width);
+ final int usedTotal = getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin +
+ heightUsed;
+ final int childHeightMeasureSpec;
+ if (lp.height == LayoutParams.WRAP_CONTENT) {
+ childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
+ MeasureSpec.UNSPECIFIED);
+ } else {
+ childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+ getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin
+ + heightUsed, lp.height);
+ }
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+
+ /**
+ * 是否可以在水平方向上滚动
+ * ViewPager 通过这个方法判断子 View 是否可以水平滚动,解决与画廊滑动冲突
+ */
+ @Override
+ public boolean canScrollHorizontally(int direction) {
+ if (direction > 0) {
+ return getScrollX() < getScrollRangeX();
+ } else {
+ return getScrollX() > 0 && getScrollRangeX() > 0;
+ }
+ }
+
+ /**
+ * 是否可以在竖直方向上滚动
+ * 同上
+ */
+ @Override
+ public boolean canScrollVertically(int direction) {
+ if (direction > 0) {
+ return getScrollY() < getScrollRangeY();
+ } else {
+ return getScrollY() > 0 && getScrollRangeY() > 0;
+ }
+ }
+
+ /**
+ * 是否重置数据
+ *
+ */
+ public void resetView(){
+ if (mCurrentZoom > mMinZoom){
+ LogUtil.zzz(TAG,"resetView");
+ setScale(mMinZoom, 0, 0);
+ }
+ }
+
+ public void changeWindow(boolean isPort){
+ if (!isPort){
+ scrollTo(getScrollRangeX(),0);
+ }
+ }
+
+
+ public void setZoomLayoutGestureListener(ZoomLayoutGestureListener zoomLayoutGestureListener) {
+ mZoomLayoutGestureListener = zoomLayoutGestureListener;
+ }
+
+ public interface ZoomLayoutGestureListener {
+ void onScrollBegin();
+ void onScaleGestureBegin();
+ void onDoubleTap();
+ }
+
+ private OnClickListener clickListener;
+
+ public void setClickListener(OnClickListener clickListener) {
+ this.clickListener = clickListener;
+ }
+ public interface OnClickListener {
+ void onClick(View view);
+
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ActivityUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ActivityUtil.java
new file mode 100644
index 0000000..bf6ebfe
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ActivityUtil.java
@@ -0,0 +1,79 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.ActivityManager;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+
+
+import com.tengshisoft.chatmodule.activity.InvitedPointCallActivity;
+import com.tengshisoft.chatmodule.activity.SponsorMeetingActivity;
+
+import java.util.List;
+
+import androidx.annotation.RequiresApi;
+
+public final class ActivityUtil {
+
+ private static final String TAG = "ActivityUtil";
+
+ // 构造函数私有防止不必要的初始化
+ private ActivityUtil() {
+ throw new UnsupportedOperationException("you can not instantiate me...");
+ }
+
+ public static void startActivity(Context context, Intent intent) {
+ try {
+ LogUtil.zzz("startActivity");
+ intent.setPackage(context.getPackageName());
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ LogUtil.zzz("startActivityActivityNotFoundException");
+ e.printStackTrace();
+ }
+ }
+
+ public static void isRunningForegroundToApp(Context context, final Class Class) {
+ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ // 利用系统方法获取当前Task堆栈, 数目可按实际情况来规划,这里只是演示
+ List taskInfoList = activityManager.getRunningTasks(20);
+ for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {
+ // 遍历找到本应用的 task,并将它切换到前台
+ if (taskInfo.baseActivity.getPackageName().equals(context.getPackageName())) {
+ Log.d(TAG, "timerTask pid " + taskInfo.id);
+ Log.d(TAG, "timerTask processName " + taskInfo.topActivity.getPackageName());
+ Log.d(TAG, "timerTask getPackageName " + context.getPackageName());
+ activityManager.moveTaskToFront(taskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
+ Intent intent = new Intent(context, Class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ context.startActivity(intent);
+ break;
+ }
+ }
+ }
+
+ /**
+ * 保证接听语音会议界面和视频会议界面任务栈一直在最顶层
+ *
+ * @param context
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static void moveToFront(Context context) {
+ if (context instanceof InvitedPointCallActivity || context instanceof SponsorMeetingActivity) {
+ return;
+ }
+ // honeycomb
+ ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List recentTasks = manager.getRunningTasks(Integer.MAX_VALUE);
+ for (int i = 0; i < recentTasks.size(); i++) {
+ // bring to front
+ if (recentTasks.get(i).baseActivity.getClassName().equals(InvitedPointCallActivity.class.getName())
+ || recentTasks.get(i).baseActivity.getClassName().equals(SponsorMeetingActivity.class.getName())) {
+ manager.moveTaskToFront(recentTasks.get(i).id, ActivityManager.MOVE_TASK_WITH_HOME);
+ }
+ }
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/AppUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/AppUtil.java
new file mode 100644
index 0000000..d26fd17
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/AppUtil.java
@@ -0,0 +1,141 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.os.Build;
+import android.view.WindowManager;
+
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.util.List;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.content.ContextCompat;
+
+/**
+ * @Time: 2021/6/10
+ * @Author: isoftstone
+ * @Description:App相关工具类
+ */
+public class AppUtil {
+
+
+ private AppUtil() {
+ }
+
+ /**
+ * 判断蓝牙耳机是否连接
+ *
+ * @return 蓝牙耳机连接状态
+ */
+ public static boolean isBluetoothEarphoneAvailable() {
+ BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (mBluetoothAdapter == null) {
+ return false;
+ } else if (mBluetoothAdapter.isEnabled()) {
+ // 可操控蓝牙设备,如带播放暂停功能的蓝牙耳机
+ int headset = mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET);
+ // 查看是否蓝牙是否连接到耳机设备的一种,以此来判断是否处于连接状态还是打开并没有连接的状态
+ return headset == BluetoothProfile.STATE_CONNECTED || headset
+ == BluetoothProfile.STATE_CONNECTING;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 判断手机是否连接耳机(包含有线和蓝牙耳机)
+ *
+ * @return 耳机连接状态
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static boolean checkIsWired() {
+ AudioManager audioManager = (AudioManager) BaseAppContext.getInstance().
+ getSystemService(Context.AUDIO_SERVICE);
+ AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ for (AudioDeviceInfo device : devices) {
+ int deviceType = device.getType();
+ if (deviceType == AudioDeviceInfo.TYPE_WIRED_HEADSET
+ || deviceType == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
+ || deviceType == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
+ || deviceType == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判断手机是否插入有线耳机
+ *
+ * @return 有线耳机连接状态
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static boolean checkWiredHeadsetOn() {
+ AudioManager audioManager = (AudioManager) BaseAppContext.getInstance().
+ getSystemService(Context.AUDIO_SERVICE);
+ AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ for (AudioDeviceInfo device : devices) {
+ int deviceType = device.getType();
+ if (deviceType == AudioDeviceInfo.TYPE_WIRED_HEADSET
+ || deviceType == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ *TODO 重启应用,刷新语言缓存
+ *
+ * @param context 上下文
+ */
+ public static void restartApp(Context context) {
+// Intent intent = new Intent(context, MainActivityV2.class);
+// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+// ContextCompat.startActivity(context, intent, null);
+ }
+
+ /**
+ * 全屏并且隐藏状态栏
+ */
+ public static void hideStatusBar(Activity activity) {
+ WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
+ attrs.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ activity.getWindow().setAttributes(attrs);
+ }
+
+ /**
+ * 判断某activity是否处于栈顶
+ *
+ * @return true在栈顶 false不在栈顶
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static boolean isActivityTop(Class cls, Context context) {
+ if (cls == null) {
+ return false;
+ }
+ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List tasks = activityManager.getAppTasks();
+ if (tasks != null && tasks.size() > 0) {
+ return (tasks.get(0).getTaskInfo().topActivity.getClassName().equals(cls.getName()));
+ }
+ return true;
+ }
+
+ public static boolean isInt(String str) {
+ try {
+ Integer.valueOf(str);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/BandWidthUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/BandWidthUtils.java
new file mode 100644
index 0000000..23917e9
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/BandWidthUtils.java
@@ -0,0 +1,154 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+/**
+ * @Time: 2021/7/1
+ * @Author: isoftstone
+ * @Description: 带宽工具类
+ */
+public class BandWidthUtils {
+ private static final String TAG = "BandWidthUtils";
+ /**
+ * 画中画分辨率
+ */
+ private static final float P_720 = 720f;
+ private static final float P_360 = 360f;
+ private static final float P_180 = 180f;
+ private static final float P_90 = 90f;
+
+ /**
+ * 宽带比
+ */
+ private static final float BW_0 = 0f;
+ private static final float BW_1 = 1f;
+ private static final float BW_512 = 512f;
+ private static final float BW_1500 = 1500f;
+
+ /**
+ * 屏幕宽高比
+ */
+ private static final float SCREEN_W_H_05 = 0.5f;
+ private static final float SCREEN_W_H_05625 = 0.5625f;
+ /**
+ * 默认视频窗口数量
+ */
+ private static final float VIDEO_COUNT_DEFAULT = 2;
+
+ public BandWidthUtils() {
+ }
+
+ /**
+ * 计算画中画分辨率
+ *
+ * @param videoCount
+ * @param bandWidth
+ * @return
+ */
+ public static int[] getPipVideoSize(int videoCount, int bandWidth) {
+ int[] videoSize = new int[3];
+ float with;
+ float tempBandWith = bandWidth * BW_1;
+ if (tempBandWith > BW_0) {
+ if (tempBandWith > BW_1500) {
+ with = P_720;
+ } else {
+ with = P_360;
+ }
+ } else {
+ with = P_720 / videoCount;
+ LogUtils.d("getPipVideoSize()", "meeting bandwidth is empty");
+ }
+ videoSize[1] = (int) with;
+ videoSize[0] = (int) (with / SCREEN_W_H_05625);
+ videoSize[2] = (int) (tempBandWith / videoCount + SCREEN_W_H_05);
+ return videoSize;
+ }
+
+ /**
+ * 计算画廊分辨率
+ *
+ * @param videoCount
+ * @param bandWidth
+ * @param isSvcH265
+ * @return
+ */
+ public static int[] getGalleryVideoSize(int videoCount, int bandWidth, boolean isSvcH265) {
+ int[] videoSize = new int[3];
+ float with;
+ float tempBandWith = bandWidth * BW_1;
+ if (tempBandWith > BW_0) {
+ if (videoCount == 1) {
+ if (tempBandWith > BW_1500) {
+ with = P_720;
+ } else {
+ with = P_360;
+ }
+ } else {
+ if (tempBandWith > BW_1500) {
+ if (videoCount >= VIDEO_COUNT_DEFAULT) {
+ with = P_360;
+ } else {
+ with = P_720;
+ }
+ } else if (tempBandWith > BW_512) {
+ if (videoCount >= VIDEO_COUNT_DEFAULT) {
+ with = P_180;
+ } else {
+ with = P_360;
+ }
+ } else {
+ if (videoCount >= VIDEO_COUNT_DEFAULT) {
+ with = isSvcH265 ? P_180 : P_90;
+ } else {
+ with = P_180;
+ }
+ }
+ }
+ } else {
+ with = P_720 / videoCount;
+ LogUtils.d("getGalleryVideoSize()", "meeting bandwidth is empty");
+ }
+ videoSize[1] = (int) with;
+ videoSize[0] = (int) (with / SCREEN_W_H_05625);
+ videoSize[2] = (int) (tempBandWith / videoCount + SCREEN_W_H_05);
+ return videoSize;
+ }
+
+ /**
+ * 计算悬浮窗分辨率 之前取的90P, 180P画面太模糊了
+ *
+ * @param bandWidth
+ * @return
+ */
+ public static int[] getMiniVideoSize(int bandWidth) {
+ int[] videoSize = new int[3];
+ float with;
+ float tempBandWith = bandWidth * BW_1;
+ with = P_360;
+ videoSize[1] = (int) (with / SCREEN_W_H_05625);
+ videoSize[0] = (int) with;
+ videoSize[2] = (int) (tempBandWith / 1 + SCREEN_W_H_05);
+ return videoSize;
+ }
+
+
+ /**
+ * 计算辅流分辨率 当页面在辅流页面时选看一个最小分辨率来解决辅流页面耗费
+ *
+ * @param bandWidth
+ * @param isSvcH265
+ * @return
+ */
+ public static int[] geAuxDataVideoSize(int bandWidth, boolean isSvcH265) {
+ int[] videoSize = new int[3];
+ float with;
+ float tempBandWith = bandWidth * BW_1;
+ with = isSvcH265 ? P_180 : P_90;
+ videoSize[1] = (int) (with / SCREEN_W_H_05625);
+ videoSize[0] = (int) with;
+ videoSize[2] = (int) (tempBandWith / 1 + SCREEN_W_H_05);
+ return videoSize;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Closeables.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Closeables.java
new file mode 100644
index 0000000..ade019a
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Closeables.java
@@ -0,0 +1,27 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+
+public class Closeables
+{
+ public static void closeCloseable(Closeable closeable)
+ {
+ if (closeable == null)
+ {
+ return;
+ }
+
+ try
+ {
+ closeable.close();
+ }
+ catch (IOException e)
+ {
+ Log.e(LogUtil.CLOUNDLINK, e.getMessage());
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/CloudLinkPermission.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/CloudLinkPermission.java
new file mode 100644
index 0000000..ae379ae
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/CloudLinkPermission.java
@@ -0,0 +1,52 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.Activity;
+
+import com.tengshisoft.chatmodule.hwclud.api.IAgree;
+import com.tengshisoft.chatmodule.hwclud.api.IPermission;
+import com.tengshisoft.chatmodule.hwclud.api.IPermissionFactory;
+import com.tengshisoft.chatmodule.hwclud.api.IRefuse;
+
+import androidx.annotation.NonNull;
+
+/**
+ * @author Yason
+ *
+ * 权限申请代理类
+ * 持有权限申请{@link IPermission} 对象,通过操作接口,让接口的实现类去实现具体业务。
+ * 此类方法功能可参考{@link IPermission} 类注释
+ * */
+public class CloudLinkPermission {
+
+ /**
+ * 权限申请接口对象,面向接口编程
+ * */
+ private IPermission permission;
+
+ public void reCheck() {
+ permission.reCheck();
+ }
+
+ /**
+ * @param clz clz must extends IPermissionFactory {@link IPermissionFactory}
+ * */
+ public boolean checkPermission(Activity context, IAgree iAgree, IRefuse iRefuse, Class>... clz) {
+ try {
+ if (clz != null && clz.length == 1) {
+ IPermissionFactory factory = (IPermissionFactory) clz[0].newInstance();
+ permission = factory.permission();
+ } else {
+ IPermissionFactory factory = new MultiFactory();
+ permission = factory.multiPermission(clz);
+ }
+ } catch (IllegalAccessException | InstantiationException e) {
+ e.printStackTrace();
+ return false;
+ }
+ return permission.checkPermission(context, iAgree, iRefuse);
+ }
+
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ permission.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ConfigAppUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ConfigAppUtil.java
new file mode 100644
index 0000000..8263193
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ConfigAppUtil.java
@@ -0,0 +1,15 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+public class ConfigAppUtil {
+
+
+ private static boolean isAuxServiceStopped = true;
+
+ public static boolean isIsAuxServiceStopped() {
+ return isAuxServiceStopped;
+ }
+
+ public static void setIsAuxServiceStopped(boolean isAuxServiceStopped) {
+ ConfigAppUtil.isAuxServiceStopped = isAuxServiceStopped;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Constant.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Constant.java
new file mode 100644
index 0000000..669098d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Constant.java
@@ -0,0 +1,451 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+
+public class Constant {
+ /**
+ * 主页面跳转
+ */
+ public static final int FRAGMENT_MEETING = 0;
+ public static final int FRAGMENT_CONTACTS = 1;
+ public static final int FRAGMENT_MINE = 2;
+ /**
+ * 会议设置 1
+ */
+ public static final int MEETING_SETTING_1 = 1;
+ public static final int MEETING_SETTING_2 = 2;
+ public static final int MEETING_SETTING_3 = 3;
+ /**
+ * 服务器设置 1
+ */
+ public static final int SERVER_SETTING_1 = 1;
+ /**
+ * 加入会议 1登录加入会议
+ */
+ public static final int JOIN_MEETING_1 = 1;
+
+ /**
+ * 跳转本地通讯录,显示sheet页面
+ */
+ public static final int CONTACT_ADAPTER_LOCAL = 1;
+
+ /**
+ * 企业通讯录
+ */
+ public static final int CONTACT_ADAPTER_COMPANY = 2;
+
+ public static final int CONTACT_ADAPTER_MAIN = 3;
+
+ public static final int INVITE_CONTACT = 4;
+
+ public static final int CONTACT_ADAPTER_ADD = 5;
+
+ public static final int INVITE_COMPANY_CONTACT = 6;
+
+ public static final int CONTACT_RECENT_CALLS = 7;
+
+ public static final String CONTACT_ADAPTER_ADD_content = "ldapContactsInfo";
+
+ public static final int VOICE_CONF_COMING = 1;
+
+ /**
+ * 音频会议进行中
+ */
+ public static final int VOICE_CONF_ING = 2;
+
+ /**
+ * 视频会议来电
+ */
+ public static final int CONF_VIDEO_COMING = 4;
+
+ //点对点语音主叫
+ public static final int POINT_CALL = 5;
+ //点对点语音被叫
+ public static final int POINT_CALLED = 6;
+ //点对点视频已连接
+ public static final int POINT_CONNECTED = 7;
+ //点对点视频主叫
+ public static final int POINT_VIDEO = 8;
+ //点对点视频被叫
+ public static final int POINT_VIDEOED = 9;
+
+ public static final int VOICE_ACCESS_CONF_ING = 10;
+
+ public static final int PIC_ADD = 20;
+
+ public static final int PIC_DELETE = 21;
+
+ //加入语音会议
+ public static final int VOICE_LIST_JOIN = 11;
+
+ /**
+ * 授权联系人被拒
+ */
+ public static final int MEETING_TYPE = 105;
+ public static final int UPDATE_VOICE = 10;
+ public static final String CONF_LIST_DATA = "conf_list_data";
+ public static final int UPDATE_CONF_CONNECT = 12;
+ public static final int CALL_UPDATE_VOICE = 13;
+ public static final int UPDATE_CALL_CONNECT = 14;
+ public static final String CALLED_NAME = "called_name";
+ public static final int CALL_VOICE_GOING = 15;
+ public static final int CALL_VIDEO_GOING = 16;
+ public static final String DIAL = "dial";
+ public static final int VIDEO_CLOSE = 17;
+ public static final String VIDEO_CALL_NUM = "video_call_num";
+ public static final int BOOK_CONF_LIST = 18;
+ public static final int ACTION_VIDEO_TO_AUDIO = 19;
+ public static final int CALL_UPGRADE_ACTION = 20;
+ public static final int OPEN_VIDEO = 21;
+ public static final int REFUSE_OPEN_VIDEO = 23;
+
+ /**
+ * 单向直播
+ */
+ public static final int AUDIT_DIR = 101;
+
+
+ public static final int RECEIVED_MOBILE_CALL = 200;
+
+ public static final int END_MOBILE_CALL = 201;
+
+ /**
+ * 修改VMR密码错误码
+ */
+ public static final int UPDATE_VMR_PWD_ERROR_CODE2 = 67109102;
+
+ /**
+ * 发起或预约会议修改VMR密码错误码
+ */
+ public static final int UPDATE_VMR_PWD_ERROR_CODE1 = 67109101;
+
+ /**
+ * 发起或预约会议失败错误码
+ */
+ public static final int BOOK_MEETING_ERROR_CODE = 67109096;
+
+ public static final String SHOW_NO_NET_DIALOG_INFO = "showNoNetDialogInfo";
+ public static final String BOOK_CONF_LIST_DATA = "book_conf_list_data";
+ public static final String INVITE_CONF_LIST_DATA = "invite_conf_list_data";
+ public static final String VOICE_CALL_NUM = "voice_call_num";
+ public static final String CONF_IS_COMING = "conf_is_coming";
+ public static final int UPDATE_VIDEO_STATUS = 22;
+ public static final String CONF_IS_CONNECT = "conf_is_connect";
+ public static final String LOGIN_SERVER_SIP_URL = "login_server_sip_url";
+ public static final String UPD_TLS = "upd_tls";
+ public static final String SIPNUMBER = "sipnumber";
+ public static final String BFCP_TRANSPORT_MODE = "bfcpTransportMode";
+ public static final String IS_OPEN_GM = "isOpenGm";
+ public static final String IS_AGREE_RISK_GUEST_PWD_RANDOM_WARNING = "isAgreeRiskGuestPwdRandomWarning";
+ public static final String IS_AGREE_RISK_PERSONAL_ID_WARNING = "isAgreeRiskPersonalId";
+ public static final String IS_AGREE_RISK_CHAIRMAN_PWD_WARNING = "isAgreeRiskChairmanPwd";
+ public static final String IS_AGREE_RISK_GUEST_PWD_WARNING = "isAgreeRiskGuestPwd";
+ public static final int AUX_DATA = 25;
+ public static final String VMR_TITLE = "vir_title";
+ public static final String VMR_ACCESSNUMBER = "vmr_acceddnumber";
+ public static final String VMR_CHAIRMANPWD = "vmr_chairmanpwd";
+ public static final String VMR_GUESTPWD = "vmr_guestpwd";
+ public static final String VMR_CONF_ID = "vmr_conf_id";
+ public static final String VMR_SIP_NUMBER = "vmr_sip_number";
+ public static final String IS_FIRST_IN = "is_first_in";
+ public static final String IS_FROM_SPALSH = "is_from_spalsh";
+ public static final String ALL_TEXT = "all_text";
+ public static final String WATCH_MEMBER = "watch_member"; //选看成员
+ public static final String IS_WATCH_MEMBER = "is_watch_member"; //是否选看
+ public static final String IS_BROAD_MEMBER = "is_broad_member"; //是否有广播
+ public static final String IS_SVC_MEETING = "is_svc_meeting"; //是否是SVC会议
+ public static final String IS_CLOSE_AUX = "is_close_aux";//开关控制共享的时候进自己应用关不关辅流
+ public static final String IS_AUX = "isAuxData"; //是否有辅流
+ public static final String IS_FROM_ATTENDEELIST = "is_from_attendeelist"; //是否有辅流
+ public static final String ID = "id";
+ public static final String MINE_USER_NAME = "mine_user_name";
+ public static final String HOWL_AUTO_MUTE = "howl_auto_mute";
+ public static final String CHOOSE_ARRAY = "choose_array";
+ public static final String CHOOSE_POSITION = "choose_position";
+ public static final String CHOOSE_TITLE = "choose_title";
+ public static final String INITIATE_VMR = "initiate_vmr";
+ public static final int CHOOSE_CODE1 = 10001;
+ public static final int CHOOSE_CODE2 = 10002;
+ public static final int CHOOSE_CODE_ZONE = 10003;
+ public static final String ACTION_HEADSET_PLUG = "action_headset_plug";
+ //手动打开或者隐藏悬浮窗
+ public static final String SPONSOR_MEETING_WATCH_MEMBER = "sponsor_meeting_watch_member";//选看成功
+ /**
+ * 广播
+ */
+ public static final String SPONSOR_MEETING_BROAD_MEMBER = "sponsor_meeting_broad_member";//广播成功
+ public static final String SPONSOR_MEETING_BROAD_MEMBER_CANCEL = "sponsor_meeting_broad_member_cancel";//广播取消
+ /**
+ * 点名
+ */
+ public static final String SPONSOR_MEETING_ROLL_MEMBER = "sponsor_meeting_roll_member";//点名成功
+ public static final String SPONSOR_MEETING_REFLUSHED = "sponsor_meeting_reflushed";//与会者列表被动刷新的时候,SVC大画面 选看/点名/广播 所展示内容
+ public static final String MEETING_DESTORY = "meeting_destory"; //会议销毁
+ /**
+ * 是否关闭摄像头
+ */
+ public static final String IS_CLOSE_VIDEO = "is_close_video"; // 是否关闭摄像头
+ public static final String IS_MINIMIZE_CLICK = "is_minimize_click";
+ public static final int REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = 102;
+ public static final String LDAP_CONTACTS_INFO = "ldapContact";
+ public static final String IS_COLLECTION_CONTACT = "MailContact";
+ public static final String IS_LOCAL_ADD_CONTACT = "LocalContact";
+ public static final String CONTACTS_TYPE = "contacts_type";
+ public static final String IS_VIBRATION = "is_vibration";
+ public static final String IS_RECEIVING_RING = "is_receiving_ring";
+ public static final String AUX = "aux";
+ public static final boolean END_CALL = true;
+
+ public static String LOGIN_ACCOUNT = "login_account";
+ public static String ANONYMOUS = "anonymous";
+ public static String SERVICE_TYPE = "service_type";
+ public static String USER_TYPE = "user_type";
+ public static String AUTH_TYPE = "auth_type";
+ public static String IS_LOGOUT = "is_logout";
+ public static String IS_NO_STREAM_DURATION = "is_no_steam_duration";
+ public static String DEVICE_ID = "device_id";
+ public static boolean IS_BACK_MINIMIZE = false;
+ public static boolean IS_SHOW_PARTICIPANTS = false;
+
+ public static String DEFINITION_SETTING = "definition_setting";
+ public static int WATCH_ATTENDEE_REQUEST_CODE = 1004;
+
+
+ // 与会者列表操作事件广播,用来通知会议界面
+ public static String WATCH_MYSELF_CAMERA_STATE = "watch_myself_camera_state";
+ /**
+ * 点对点
+ */
+ public static final int POP_CALL_VIDEO = 0x0101;
+ /**
+ * 主席
+ */
+ public static final int POP_IS_CHAIRMAN = 0x0111;
+ /**
+ * 非主席
+ */
+ public static final int POP_IS_NOT_CHAIRMAN = 0x1000;
+ /**
+ * 主席密码
+ */
+ public static final String MEETING_CHAIRMAN = "meeting_chairman";
+
+ public static final String SCREEN_OFF = "screen_off";
+
+ public static final int NAME_CHARACTER_LENGTH_192 = 192;
+
+ /**
+ * 登录结果枚举类
+ */
+ public enum LoginUIEvent {
+
+ VOIP_LOGIN_SUCCESS(),
+
+ LOGIN_FAILED(),
+
+ AUTH_FAILED(),
+
+ FIREWALL_DETECT_FAILED(),
+
+ BUILD_STG_FAILED(),
+
+ LOGOUT(),
+
+ FIRST_CHANGE_PWD(),
+
+ CHANGE_PWD()
+ }
+
+ /**
+ * 服务器登录地址
+ */
+ public static final String LOGIN_SERVER_ADDRESS = "login_server_address";
+
+ /**
+ * 服务器端口
+ */
+ public static final String LOGIN_SERVER_PORT = "login_server_port";
+
+ /**
+ * Https Port
+ */
+ public static final String LOGIN_SERVER_HTTPSPORT = "login_server_httpsport";
+
+ /**
+ * 服务器地址是否变更
+ */
+ public static final String LOGIN_SERVER_CHANGE = "login_server_change";
+
+ /**
+ * 判断是否匿名链接入会
+ */
+ public static final String JOIN_MEETING_NOLOGIN = "join_meeting_nologin";
+ /**
+ * 判断是否处于匿名链接入会中,此时不允许用户点击登录
+ */
+ public static final String JOIN_MEETING_NOLOGIN_ING = "join_meeting_nologin_ing";
+
+ /**
+ * 判断3.0日志是否正在上传状态(true正在上传,false非上传状态)
+ */
+ public static final String FEEDBACK_UPDATE_3 = "feedback_update_3";
+
+ public static final String LOGIN_SERVER_SRTP_MODE = "login_server_srtp_mode";
+
+ public static final String IS_VIDEO_CONF = "is_video_conf";
+ public static final String IS_AUTO_LOGIN = "is_auto_login";
+
+ public static final String CAMERAM_STATE = "camera_state";
+
+ public static final String CAMERA_INDEX = "camera_index";
+
+ public static final String CONF_ID = "conf_id";
+
+ public static final String IS_AUTO_JUMP = "isAutoJump";
+
+ public static final String MY_CONF_INFO = "my_conf_info";
+
+ public static String TEMP_CONF_INFO = "";
+
+ public static final String CONF_INFO = "conf_info";
+
+ public static final String IS_VMR_2_ID = "is_vmr_2.0_id";
+
+ public static final String IS_VMR_3_ID = "is_vmr_3.0_id";
+
+ public static final String CONF_TEMPORARY = "conf_temporary";
+
+ public static final String CALL_INFO = "call_info";
+
+ public static final String CALL_VIDEO_TO_VOICE = "call_video_to_voice";
+
+ public static final String LOGIN_SERVER_PROXY_SWITCH_STATE = "login_server_proxy_switch_state";
+
+ public static final String LOGIN_SERVER_SECRET_SWITCH_STATE = "login_server_secret_switch_state";
+
+ public static final String LOGIN_SERVER_TLS_MODE_SWITCH_STATE = "login_server_TLS_mode_switch_state";
+
+ public static final String SERVER_PORT = "5061";
+
+ /**
+ * 来宾密码开关标志
+ */
+ public static final String IS_GUEST_SWITCH_OPEN = "is_guest_switch_open";
+
+ /**
+ * 主席密码开关标志
+ */
+ public static final String IS_CHAIRMAN_SWITCH_OPEN = "is_chairman_switch_open";
+
+ /**
+ * 判断是否是Vmr标志
+ */
+ public static final String IS_VMR = "is_vmr";
+ /**
+ * 判断是否修改密码
+ */
+ public static final String CHANGE_PWD = "change_pwd";
+ /**
+ * 判断联系人图标索引
+ */
+ public static final String CONTACT_INDEX = "contact_index";
+
+ /**
+ * 判断联系人背景颜色所用
+ */
+ public static final String CONTACT_BG_COLOR_INDEX = "contact_bg_color_index";
+
+ /**
+ * 密码置空字符串
+ */
+ public static final String PASSWORD_NULL_STRING = "";
+
+ /**
+ * 空格字符串
+ */
+ public static final String SPACE_STRING = " ";
+ /**
+ * 广播action
+ */
+
+ public static final String BROADCAST_ACTION_STOP_SHARE = "action_stop_share";
+
+ /**
+ * 进入语音会议页面的type
+ * 1:语音会议来电通知
+ * 2.语音会议进入页面
+ * 3.点对点呼叫进入
+ * 4.视频会议来电通知
+ */
+ public static final String VOICE_JOIN_TYPE = "VOICE_JOIN_TYPE";
+
+ /**
+ * 与会者枚举类
+ */
+ public enum ParticipantEvent {
+
+ REQUEST_CHAIRMAN_SUCCESS(),
+
+ REQUEST_CHAIRMAN_FAILED(),
+
+ RELEASE_CHAIRMAN_FAILED(),
+
+ RELEASE_CHAIRMAN_SUCCESS(),
+ }
+
+
+ /**
+ * 网络异常 提示最大值 [0,5] 0 :最差,5:最好
+ */
+ public static final int NET_LEVEL_MIN_TOAST = 3;
+ /**
+ * 网络 toast 间隔时间 单位: 毫秒
+ */
+ public static final int NET_LEVEL_MIN_TOAST_INTERVAL = 1 * 30 * 1000;
+
+ /**
+ * 申请会议主席 result
+ */
+ public static final String REQUEST_CHAIRMAN_SUCCESS = "result_action_request_chairman_success";
+
+ /**
+ * 记录2.0发起会议自己输入的主席密码,用来2.0申请主席使用
+ */
+ public static final String CHAIRMANPWD_SMC_2 = "chairmanpwd_smc_2";
+
+ /**
+ * 判断2.0是否是自己创建的会议
+ */
+ public static final String CREATE_MEETING_SMC_2 = "create_meeting_smc_2";
+
+ /**
+ * 关闭无码流弹框
+ */
+ public static final String NO_DURATION_DIALOG_CLOSE = "noduration_dialog_close";
+
+ public static boolean DownLoadDialogIsShow = false;
+
+ public static final int MEETING_HIGH_SETTING_CODE = 10005;
+
+ public static final String MEETING_HIGH_SETTING_VIDEO = "isVideo";
+
+ public static final String MEETING_HIGH_SETTING_AUDIO = "isAudio";
+
+ public static final String MEETING_HIGH_SETTING_SUBTITLE = "isSubtitle";
+
+ public static final String MEETING_HIGH_SETTING_SUBTITLE_LANGUAGE = "subtitle_language";
+
+ public static final String MEETING_HIGH_SETTING_TYPE_VIDEO = "isVideoConf";
+
+ public static final String MEETING_CAPTION_ENABLE = "isSubtitleEnable";
+
+ public static final String MEETING_CONF_CAPTION_ENABLE = "isConfSubtitleEnable";
+
+ public static final String MEETING_CONF_CAPTION_SHOWING = "isSubtitleShowing";
+
+ public static final SubtitleLanguage[] LANGUAGE_LIST_DATA = {
+ new SubtitleLanguage(1, "中文"),
+ new SubtitleLanguage(0, "English")};
+
+ public static final String MEETING_CONF_CONTROL_ENABLE = "isConfControlEnable";
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DateUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DateUtil.java
new file mode 100644
index 0000000..7a75e13
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DateUtil.java
@@ -0,0 +1,254 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import android.os.Build;
+import android.text.TextUtils;
+
+
+import com.tengshisoft.chatmodule.R;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.util.Date;
+import java.util.TimeZone;
+
+import androidx.annotation.RequiresApi;
+
+public class DateUtil {
+ private static final DateUtil INSTANCE = new DateUtil();
+
+ /**
+ * time patten(yyyy-MM-dd HH:mm)
+ */
+
+ public static final String FMT_YMDHMS = "yyyy-MM-dd HH:mm:ss";
+ public static final String FMT_YMDHM = "yyyy-MM-dd HH:mm";
+ public static final String FMT_YMDHM_2 = "yyyy/MM/dd HH:mm";
+ public static final String FMT_YMDHM_3 = "HH:mm:ss";
+ public static final String FMT_YMDHM_4 = "MM/dd HH:mm";
+ public static final String UTC = "UTC";
+ public static final String GMT = "GMT+8";
+
+ private DateUtil() {
+ }
+
+ public static DateUtil getInstance() {
+ return INSTANCE;
+ }
+
+ public static String localTimeUtc(String srcTime) {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ SimpleDateFormat dspFmt = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ String utcTime;
+ Date resultDate;
+ long resultTime = 0;
+
+ TimeZone timeZone = TimeZone.getDefault();
+
+ if ((srcTime == null) || srcTime.equals("")) {
+ return null;
+ } else {
+ try {
+ sdf.setTimeZone(timeZone);
+ resultDate = sdf.parse(srcTime);
+
+ resultTime = resultDate.getTime();
+ } catch (Exception e) {
+ resultTime = System.currentTimeMillis();
+ dspFmt.setTimeZone(TimeZone.getDefault());
+ utcTime = dspFmt.format(resultTime);
+ return utcTime;
+ }
+ }
+
+ dspFmt.setTimeZone(TimeZone.getTimeZone("GMT00:00"));
+ utcTime = dspFmt.format(resultTime);
+
+ return utcTime;
+ }
+
+ /**
+ * Convert utc time to local time
+ *
+ * @param utcTime
+ * @param utcPatten
+ * @param localPatten
+ * @return Local time
+ */
+ public static String utcToLocalDate(String utcTime, String utcPatten, String localPatten) {
+ Date utcDate = parseDateStr(utcTime, UTC, utcPatten);
+ return generateFormat(utcDate, localPatten);
+ }
+
+ /**
+ * parseDateStr
+ *
+ * @param time
+ * @param dateId
+ * @param patten
+ * @return
+ */
+ public static Date parseDateStr(String time, String dateId, String patten) {
+ SimpleDateFormat utcFormat = new SimpleDateFormat(patten);
+ utcFormat.setTimeZone(TimeZone.getTimeZone(dateId));
+ Date utcDate;
+ try {
+ utcDate = utcFormat.parse(time);
+ } catch (ParseException e) {
+ return new Date();
+ }
+ return utcDate;
+ }
+
+ private static String generateFormat(Date date, String patten) {
+ SimpleDateFormat localFormat = new SimpleDateFormat(patten);
+ localFormat.setTimeZone(TimeZone.getDefault());
+
+ return localFormat.format(date.getTime());
+ }
+
+ public static String fromLongToDate(String format, Long time) {
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
+ sdf.setTimeZone(TimeZone.getTimeZone("GMT00:00"));
+ Date date = new Date(time);
+ return sdf.format(date);
+ }
+
+ public static String fromLongToDate1(String format, Long time) {
+ SimpleDateFormat sdf = new SimpleDateFormat(format);
+ sdf.setTimeZone(TimeZone.getDefault());
+ Date date = new Date(time);
+ return sdf.format(date);
+ }
+
+ /**
+ * 得到时间 , hh:mm
+ */
+ public static String getMeetingTimeHHMM(String time) {
+ if (true) {
+ return time.substring(11, 16);
+ } else {
+ return DateUtil.utcToLocalDate(time, DateUtil.FMT_YMDHM, DateUtil.FMT_YMDHM).substring(11);
+ }
+ }
+
+ /**
+ * 得到时间 , MM-DD
+ */
+ public static String getMeetingTimeMMDD(String time) {
+ if (true) {
+ return time.substring(0, 10);
+ } else {
+ return DateUtil.utcToLocalDate(time, DateUtil.FMT_YMDHM, DateUtil.FMT_YMDHM).substring(0, 10);
+ }
+ }
+
+ /**
+ * yyyy-MM-dd HH:mm:ss 转为 long 型时间戳
+ */
+ public static Date fromToLong(String time) {
+ if (true) {
+ SimpleDateFormat utcFormat = new SimpleDateFormat(FMT_YMDHMS);
+ Date utcDate;
+ try {
+ utcDate = utcFormat.parse(time);
+ } catch (ParseException e) {
+ return new Date();
+ }
+ return utcDate;
+ } else {
+ return parseDateStr(time, DateUtil.UTC, DateUtil.FMT_YMDHM);
+ }
+ }
+
+ /**
+ * yyyy-MM-dd HH:mm:ss 转为 yyyy/MM/dd HH:mm
+ */
+ public static String fromDetailTime(String time) {
+ if (true) {
+ SimpleDateFormat utcFormat = new SimpleDateFormat(FMT_YMDHMS);
+ Date utcDate;
+ try {
+ utcDate = utcFormat.parse(time);
+ SimpleDateFormat format = new SimpleDateFormat(FMT_YMDHM_2);
+ return format.format(utcDate);
+ } catch (ParseException e) {
+ return "";
+ }
+ } else {
+ return utcToLocalDate(time, DateUtil.FMT_YMDHM, DateUtil.FMT_YMDHM_2);
+ }
+ }
+
+ /**
+ * 格式化会议时间 10月8日 14:30-17:30
+ */
+ public static String fromMeetingTime(String startTime, String endTime) {
+ String meetingTime = "";
+ if (TextUtils.isEmpty(startTime)) {
+ return meetingTime;
+ }
+ try {
+ meetingTime += startTime.substring(5, 7) + BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_month) + startTime.substring(8, 10) +
+ BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_day) + " " + startTime.substring(11, 16);
+ if (TextUtils.isEmpty(endTime)) {
+ meetingTime += "-" + BaseAppContext.getInstance().getString(R.string.cloudLink_continuingMeetings);
+ }
+ //取消结束时间,先注释,勿删
+// else {
+// meetingTime += "-" + endTime.substring(11, 16);
+// }
+//
+// if (!TextUtils.isEmpty(startTime) && !TextUtils.isEmpty(endTime)) {
+// if (startTime.substring(0, 10).equals(DateUtil.getMeetingTimeMMDD(endTime))) {
+// } else {
+// int year = Integer.valueOf(endTime.substring(0, 4));
+// int month = Integer.valueOf(endTime.substring(5, 7));
+// int day = Integer.valueOf(endTime.substring(8, 10));
+// LocalDate from_end = LocalDate.of(year, month, day);
+// LocalDate from_start=LocalDate.of(Integer.valueOf(startTime.substring(0, 4)),Integer.valueOf(startTime.substring(5, 7)),Integer.valueOf(startTime.substring(8, 10)));
+// long num = from_end.toEpochDay() - from_start.toEpochDay();
+// meetingTime += " +" + num;
+// }
+// }
+ return meetingTime;
+ } catch (Exception e) {
+ LogUtil.zzz("error", "会议时间格式异常");
+ return meetingTime;
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ public static String formatShareTime(String startTime, String endTime) {
+ String meetingTime = "";
+ if (TextUtils.isEmpty(startTime)) {
+ return meetingTime;
+ }
+ try {
+ meetingTime += startTime.substring(0, 4) + "/" + startTime.substring(5, 7) + "/" + startTime.substring(8, 10) + " \n" + startTime.substring(11, 16);
+ if (TextUtils.isEmpty(endTime)) {
+ meetingTime += "-" + BaseAppContext.getInstance().getString(R.string.cloudLink_continuingMeetings);
+ } else {
+ meetingTime += "-" + endTime.substring(11, 16);
+ }
+
+ if (!TextUtils.isEmpty(startTime) && !TextUtils.isEmpty(endTime)) {
+ if (startTime.substring(0, 10).equals(DateUtil.getMeetingTimeMMDD(endTime))) {
+ } else {
+ int year = Integer.valueOf(endTime.substring(0, 4));
+ int month = Integer.valueOf(endTime.substring(5, 7));
+ int day = Integer.valueOf(endTime.substring(8, 10));
+ LocalDate from = LocalDate.of(year, month, day);
+ long num = from.toEpochDay() - LocalDate.now().toEpochDay();
+ meetingTime += " +" + num;
+ }
+ }
+ return meetingTime;
+ } catch (Exception e) {
+ LogUtil.zzz("error", "会议时间格式异常");
+ return meetingTime;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DateUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DateUtils.java
new file mode 100644
index 0000000..30bb4be
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DateUtils.java
@@ -0,0 +1,249 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.Context;
+
+import com.huawei.ecterminalsdk.base.TsdkTimeZoneInfo;
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+/**
+ * @Time: 2021/6/8
+ * @Author: isoftstone
+ * @Description: 时间工具类
+ */
+public class DateUtils {
+ /**
+ * 日期格式
+ */
+ public static final String YYYY_MM_DD_HH_MM = "yyyy-MM-dd HH:mm";
+ public static final String YYYY_MM_DD = "yyyy-MM-dd";
+ /**
+ * 一刻钟
+ */
+ public static final int ONE_QUARTER = 15;
+ /**
+ * 两刻钟
+ */
+ private static final int TWO_QUARTER = 30;
+ /**
+ * 三刻钟
+ */
+ private static final int THREE_QUARTER = 45;
+ /**
+ * 00 分钟数大于45,默认显示00
+ */
+ public static final String DEFAULT_QUARTER = "00";
+ /**
+ * 一个小时
+ */
+ public static final int ONE_HOUR_VALUE = 1000 * 60 * 60;
+ /**
+ * 时区相差值
+ */
+ public static final int TIME_ZONE_VALUE = 10;
+ /**
+ * 时区
+ */
+ public static final String UTC = "UTC";
+
+ /**
+ * 60 表示一分钟
+ */
+ public static final int ONE_MINUTE = 60;
+
+ /**
+ * 时间点转化日历
+ *
+ * @return Calendar 日历
+ */
+ public static Calendar getCalendarStart(String date) {
+ Calendar startDate = Calendar.getInstance();
+ SimpleDateFormat sdf2 = new SimpleDateFormat(YYYY_MM_DD_HH_MM);
+ try {
+ Date dates = sdf2.parse(date);
+ startDate.setTime(dates);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return startDate;
+ }
+
+ /**
+ * 将时间字符串转化时间
+ *
+ * @return Calendar 日历
+ */
+ public static Date getDate(String date) {
+ Date dates = new Date();
+ SimpleDateFormat sdf2 = new SimpleDateFormat(YYYY_MM_DD_HH_MM);
+ try {
+ dates = sdf2.parse(date);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return dates;
+ }
+
+ /**
+ * 根据下标获取刻钟
+ *
+ * @param tempIndex 下标
+ * @return 刻钟
+ */
+ public static String getIndexQuarter(int tempIndex) {
+ String tempMinute;
+ switch (tempIndex) {
+ case 1:
+ tempMinute = String.valueOf(ONE_QUARTER);
+ break;
+ case 2:
+ tempMinute = String.valueOf(TWO_QUARTER);
+ break;
+ case 3:
+ tempMinute = String.valueOf(THREE_QUARTER);
+ break;
+ default:
+ tempMinute = DEFAULT_QUARTER;
+ break;
+ }
+ return tempMinute;
+ }
+
+ /**
+ * 讲分钟转化刻钟
+ *
+ * @param minute 分钟数
+ * @return 刻钟
+ */
+ public static String minuteToQuarter(int minute) {
+ String mQuarter = DEFAULT_QUARTER;
+ if (minute < ONE_QUARTER) {
+ mQuarter = String.valueOf(ONE_QUARTER);
+ } else if (minute < TWO_QUARTER) {
+ mQuarter = String.valueOf(TWO_QUARTER);
+ } else if (minute < THREE_QUARTER) {
+ mQuarter = String.valueOf(THREE_QUARTER);
+ }
+ return mQuarter;
+ }
+
+ /**
+ * 获取最近时区时间
+ *
+ * @param context 上下文
+ * @return 时区对象
+ */
+ public static TsdkTimeZoneInfo getTimeZoneInfo(Context context) {
+ if (MeetingMgrV2.getInstance().getTsdkTimeZoneInfoList() != null) {
+ List tzs = MeetingMgrV2.getInstance().getTsdkTimeZoneInfoList().getTimeZoneInfoList();
+ TsdkTimeZoneInfo mTimeZone = null;
+ if (!ListTools.empty(tzs)) {
+ try {
+ for (TsdkTimeZoneInfo tz : tzs) {
+ String name;
+ int hour = PhoneUtil.getTimeZoneOffset() / ONE_HOUR_VALUE;
+ if (hour > 0 && hour < TIME_ZONE_VALUE) {
+ name = "(UTC+0" + hour;
+ } else if (hour >= TIME_ZONE_VALUE) {
+ name = "(UTC+" + hour;
+ } else if (hour > -TIME_ZONE_VALUE && hour < 0) {
+ name = "(UTC-0" + (-hour);
+ } else if (hour <= -TIME_ZONE_VALUE) {
+ name = "(UTC-" + (-hour);
+ } else {
+ name = "(UTC)";
+ }
+ if (tz.getTimeZoneDesc().contains(name)) {
+ mTimeZone = tz;
+ break;
+ }
+ }
+ } catch (Exception e) {
+ LogUtil.zzz(context.getClass().getName(), context.getString(R.string.cloudLink_resolution_error));
+ }
+ }
+ return mTimeZone;
+ }
+ return null;
+ }
+
+ /**
+ * 获取最近时区时间
+ *
+ * @param context 上下文
+ * @return 时区对象
+ */
+ public static TsdkTimeZoneInfo getTimeZonePhone(Context context) {
+ if (MeetingMgrV2.getInstance().getTsdkTimeZoneInfoList() != null) {
+ List tzs = MeetingMgrV2.getInstance().getTsdkTimeZoneInfoList().getTimeZoneInfoList();
+ TsdkTimeZoneInfo mTimeZone = null;
+ if (!ListTools.empty(tzs)) {
+ try {
+ for (TsdkTimeZoneInfo tz : tzs) {
+ if (PhoneUtil.getTimeZone().equals(tz.getTimeZoneName())) {
+ mTimeZone = tz;
+ }
+ }
+ } catch (Exception e) {
+ LogUtil.zzz(context.getClass().getName(), context.getString(R.string.cloudLink_resolution_error));
+ }
+ }
+ return mTimeZone;
+ }
+ return null;
+ }
+
+ /**
+ * 时间点根据时区转化相应日期格式的日期
+ *
+ * @param time 时间点
+ * @param dateId 时区
+ * @param patten 日期格式
+ * @return Date
+ */
+ public static Date parseDateStr(String time, String dateId, String patten) {
+ SimpleDateFormat utcFormat = new SimpleDateFormat(patten);
+ utcFormat.setTimeZone(TimeZone.getTimeZone(dateId));
+ Date utcDate;
+ try {
+ utcDate = utcFormat.parse(time);
+ } catch (ParseException e) {
+ return new Date();
+ }
+ return utcDate;
+ }
+
+ /**
+ * 将小于10的时间点,转化0+时间点显示
+ *
+ * @param times 时间点
+ * @return Date
+ */
+ public static String unitFormat(int times) {
+ String retStr = null;
+ if (times >= 0 && times < TIME_ZONE_VALUE) {
+ retStr = "0" + times;
+ } else {
+ retStr = "" + times;
+ }
+ return retStr;
+ }
+
+ /**
+ * 获取当前日期
+ *
+ * @return 年-月-日
+ */
+ public static String getToday() {
+ SimpleDateFormat dateFormat = new SimpleDateFormat(YYYY_MM_DD);
+ String today = dateFormat.format(new Date());
+ return today;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DeviceManager.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DeviceManager.java
new file mode 100644
index 0000000..c2bca17
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/DeviceManager.java
@@ -0,0 +1,575 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.WindowManager;
+
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * This class is about device manager util.
+ * 设备管理 util类
+ */
+public final class DeviceManager {
+ private static final String TAG = "DeviceManager";
+
+ /**
+ * This is a constructor of DeviceManager class.
+ * 构造方法
+ */
+ private DeviceManager() {
+ }
+
+ // save screen size
+ private static final float mDensity;
+
+ private static final int mScreenWidth;
+
+ private static final int mScreenHeight;
+
+
+ // true闭音 false开音, 记录会中来电前麦克风状态
+ public static boolean isSilentState = false;
+
+ static {
+ Resources resources = Resources.getSystem();
+ DisplayMetrics metrics = resources.getDisplayMetrics();
+
+ mDensity = metrics.density;
+ mScreenWidth = metrics.widthPixels;
+ mScreenHeight = metrics.heightPixels;
+ }
+
+ public static float getmDensity() {
+ return mDensity;
+ }
+
+ public static int getmScreenWidth() {
+ return mScreenWidth;
+ }
+
+ public static int getmScreenHeight() {
+ return mScreenHeight;
+ }
+
+ /**
+ * This method is used to is ip address boolean.
+ * 是否为本地ip地址
+ *
+ * @param iPAddress Indicates the p address
+ * 本地ip地址
+ * @return boolean If yes return TRUE, otherwise return false
+ * 是返回TRUE,否则返回false
+ */
+ public static boolean isIPAddress(String iPAddress) {
+ Pattern p = null;
+ if (iPAddress.contains(":")) {
+ p = Pattern.compile("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}"
+ + "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]):[0-9]{2,5}$");
+ } else {
+ p = Pattern.compile("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}"
+ + "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$");
+ }
+
+ return p.matcher(iPAddress).matches();
+ }
+
+ /**
+ * 判断是否是ipv4地址;解决UADP包循环依赖
+ *
+ * @param ipAddress 匹配的ip地址,不能为空,否则会空指针
+ * @return true ipv4地址,否则false
+ */
+ public static boolean isIPV4Address(String ipAddress) {
+ Pattern p = Pattern.compile("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.)"
+ + "{3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$");
+ return p.matcher(ipAddress).matches();
+ }
+
+ /**
+ * This method is used to is ipv4 address.
+ * 是否为ipv4 地址
+ *
+ * @param ipAddr Indicates ip address
+ * ip地址
+ * @return boolean If yes return TRUE, otherwise return false
+ * 是返回TRUE,否则返回false
+ */
+ private static boolean isIPV4Addr(String ipAddr) {
+ Pattern p = Pattern.compile("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\\.){3}"
+ + "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$");
+ return p.matcher(ipAddr).matches();
+ }
+
+ /**
+ * This method is used to get vpn local ip.
+ * 获取本地VPN ip地址
+ *
+ * @return String Return the vpn local ip
+ * 返回本地VPN ip地址
+ */
+ public static String getVpnLocalIp() {
+ String ip = "";
+ try {
+ List networkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces());
+ for (NetworkInterface networkInterface : networkInterfaces) {
+ String displayName = networkInterface.getDisplayName();
+ List addresses = networkInterface.getInterfaceAddresses();
+ if (null == displayName || displayName.equals("")) {
+ Log.e(TAG, "the displayName is null");
+ return null;
+ }
+ if (displayName.equals("ppp0") && addresses.size() > 0) {
+ ip = addresses.get(0).getAddress().getHostAddress();
+ Log.d(TAG, "ip = " + ip);
+ if (!TextUtils.isEmpty(ip)) {
+ break;
+ }
+ }
+ }
+ return ip;
+ } catch (Exception e) {
+ LogUtil.e(TAG, e.getMessage());
+ }
+ return null;
+ }
+
+ /**
+ * This method is used to get ip address.
+ * 获取本地ip地址
+ *
+ * @return String Return ip address
+ * 返回本地ip地址
+ */
+ public static String getLocalIp() {
+ String ip = "";
+ try {
+ Enumeration networkInfo = NetworkInterface
+ .getNetworkInterfaces();
+ NetworkInterface intf = null;
+ Enumeration intfAddress = null;
+ InetAddress inetAddress = null;
+ if (networkInfo == null) {
+ LogUtil.d("getLocalIp",
+ "get LocalIp address Error , return null value ");
+ return "";
+ }
+ for (Enumeration en = networkInfo; en
+ .hasMoreElements(); ) {
+ intf = en.nextElement();
+ intfAddress = intf.getInetAddresses();
+ for (Enumeration enumIpAddr = intfAddress; enumIpAddr
+ .hasMoreElements(); ) {
+ inetAddress = enumIpAddr.nextElement();
+ if (!inetAddress.isLoopbackAddress()) {
+ ip = inetAddress.getHostAddress();
+ if (isIPV4Addr(ip)) {
+// LogUtil.zzz("getLocalIp", "ip is " + ip);
+ return ip;
+ }
+ }
+ }
+ }
+ } catch (SocketException e) {
+ LogUtil.e(TAG, e.getMessage());
+ }
+ return ip;
+ }
+
+
+ /**
+ * This method is used to get the ip address.
+ * 获取本地ip地址
+ */
+ public static String getLocalIpAddress(boolean isVPN) {
+ return DeviceManager.getIpAddress(isVPN);
+ }
+
+ public static String getLocalIpV6() {
+ try {
+ for (Enumeration en = NetworkInterface
+ .getNetworkInterfaces(); en.hasMoreElements(); ) {
+ NetworkInterface intf = en.nextElement();
+ for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+ InetAddress inetAddress = enumIpAddr.nextElement();
+ if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet6Address) {
+ return inetAddress.getHostAddress();
+ }
+ }
+ }
+ } catch (Exception ex) {
+ Log.e("IP Address", ex.toString());
+ }
+ return null;
+ }
+
+ public static String getLocalIpAddressIPV6() {
+
+// String hostIp6 = getLocalIpV6();
+// //过滤找到真实的ipv6地址
+// if (hostIp6 != null && hostIp6.contains("%")) {
+// String[] split = hostIp6.split("%");
+// String s1 = split[0];
+// if (s1 != null && s1.contains(":")) {
+// String[] split1 = s1.split(":");
+// if (split1.length == 6||split1.length==8) {
+// if (split1[0].contains("fe") || split1[0].contains("fc")) {
+// return "0.0.0.0";
+// } else {
+// return s1;
+// }
+// }
+// }
+// }
+// return "0.0.0.0";
+
+
+ try {
+ for (Enumeration en = NetworkInterface
+ .getNetworkInterfaces(); en.hasMoreElements(); ) {
+ NetworkInterface intf = en.nextElement();
+ for (Enumeration enumIpAddr = intf
+ .getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
+ InetAddress inetAddress = enumIpAddr.nextElement();
+ if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {
+ return inetAddress.getHostAddress().toString();
+ }
+ }
+ }
+ } catch (SocketException ex) {
+ Log.e("", ex.toString());
+ }
+ return "0.0.0.0";
+ }
+
+ /**
+ * This method is used to is PadScreen
+ *
+ * @return boolean
+ */
+ public static boolean isPhone() {
+ DisplayMetrics metric = new DisplayMetrics();
+ WindowManager wm = (WindowManager) BaseAppContext.getInstance().getSystemService(Context.WINDOW_SERVICE);
+ wm.getDefaultDisplay().getMetrics(metric);
+ int width = metric.widthPixels;
+ int height = metric.heightPixels;
+ float xdpi = metric.xdpi;
+ float ydpi = metric.ydpi;
+ float density = metric.density;
+ double size = Math.sqrt(Math.pow(width / xdpi, 2) + Math.pow(height / ydpi, 2));
+ LogUtil.zzz(TAG, "screen size: " + size);
+ if (size > 6.600000 && Math.max(width, height) / density > 900) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * 获取当前的IP地址,该接口不涉及Svn相关的Ip;
+ *
+ * @return 当前IP
+ * @see
+ */
+ public static String getIpAddress(boolean enableVpn) {
+ LogUtil.zzz(TAG, "Local Address : VPN enable is " + enableVpn);
+
+ // 检查是否有vpn连接,如果是vpn连接的话返回vpn的ip地址
+ String vpnIp = getVpnIp();
+ if (enableVpn && !TextUtils.isEmpty(vpnIp)) {
+ LogUtil.zzz(TAG, "Local Address : return VPN " + ipToStars(vpnIp));
+ return vpnIp;
+ }
+
+ // 检查是否有wifi连接,如果是wifi连接的话返回wifi的ip地址
+ String wifiIp = getWifiIp();
+ if (!TextUtils.isEmpty(wifiIp)) {
+ if (enableVpn || !wifiIp.equals(vpnIp)) {
+ LogUtil.zzz(TAG, "Local Address : return WIFI " + ipToStars(wifiIp));
+ return wifiIp;
+ }
+ }
+
+ // 检查是否有网络连接,如果有则返回对应IP。
+ List netIps = getNetAddress();
+ for (String tempNetIp : netIps) {
+ if (!enableVpn && vpnIp.equals(tempNetIp)) {
+ continue;
+ }
+
+ LogUtil.zzz(TAG, "Local Address : return NetIp " + ipToStars(tempNetIp));
+ return tempNetIp;
+ }
+
+ if (!TextUtils.isEmpty(vpnIp)) {
+ LogUtil.zzz(TAG, "Local Address : return VPN " + ipToStars(vpnIp));
+ return vpnIp;
+ } else if (!TextUtils.isEmpty(wifiIp)) {
+ LogUtil.zzz(TAG, "Local Address : return WIFI " + ipToStars(wifiIp));
+ return wifiIp;
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * 在VPN连接的情况下,返回VPN的IP
+ *
+ * @return VPN IP
+ */
+ private static String getVpnIp() {
+ LogUtil.zzz(TAG, "Local Address : VPN begin");
+ // 在VPN连接的情况下,返回VPN的IP
+ NetworkInterface vpn = null;
+ String vpnIp;
+
+ try {
+ vpn = NetworkInterface.getByName("tun0");
+
+ if (vpn == null) {
+ vpn = NetworkInterface.getByName("ppp0");
+ }
+ } catch (SocketException e) {
+ LogUtil.e(TAG, e.getMessage());
+ }
+
+ vpnIp = getValidIpAddress(vpn);
+
+ if (null == vpnIp) {
+ vpnIp = "";
+ }
+
+ LogUtil.zzz(TAG, "Local Address : VPN is " + ipToStars(vpnIp));
+ return vpnIp;
+ }
+
+ /**
+ * 将int类型转换为String类型的字符串
+ *
+ * @param i ip地址
+ * @return
+ */
+ public static String intToIp(int i) {
+ return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF) + "." + ((i >> 24) & 0xFF);
+ }
+
+ /**
+ * 检查是否有wifi连接,如果是wifi连接的话返回wifi的ip地址
+ *
+ * @return wifi ip
+ */
+ private static String getWifiIp() {
+ @SuppressLint("WifiManagerLeak") Object obj = BaseAppContext.getInstance()
+ .getSystemService(Context.WIFI_SERVICE);
+ if (!(obj instanceof WifiManager)) {
+ return "";
+ }
+
+ LogUtil.zzz(TAG, "Local Address : WIFI begin");
+ WifiInfo wifiInfo = ((WifiManager) obj).getConnectionInfo();
+
+ String wifiIp;
+ if (wifiInfo != null) {
+ int ipAddress = wifiInfo.getIpAddress();
+ wifiIp = intToIp(ipAddress);
+
+ if (0 != ipAddress) {
+ LogUtil.zzz(TAG, "Local Address : WIFI is " + ipToStars(wifiIp));
+ return wifiIp;
+ }
+ }
+
+ LogUtil.zzz(TAG, "Local Address : WIFI is ");
+ return "";
+ }
+
+ private static List getNetAddress() {
+ LogUtil.zzz(TAG, "Local Address : NetAddress begin");
+ List allIpList = new ArrayList<>();
+
+ Enumeration networkInfo = null;
+
+ try {
+ networkInfo = NetworkInterface.getNetworkInterfaces();
+ } catch (SocketException e) {
+ LogUtil.e(TAG, e.getMessage());
+ }
+
+ if (networkInfo == null) {
+ LogUtil.e(TAG, "Local Address : NetAddress address Error");
+ return allIpList;
+ }
+
+ NetworkInterface intf;
+ List tempIp;
+ for (Enumeration en = networkInfo; en.hasMoreElements(); ) {
+ intf = en.nextElement();
+ tempIp = getValidIpAddressList(intf);
+ if (!tempIp.isEmpty()) {
+ allIpList.addAll(tempIp);
+ }
+ }
+ List ipStarList = new ArrayList<>();
+ for (String ip : allIpList) {
+ ipStarList.add(ipToStars(ip));
+ }
+ LogUtil.zzz(TAG, "Local Address : NetAddress is " + ipStarList);
+ return allIpList;
+ }
+
+ /**
+ * 本地ip地址匿名化格式 ipv4星化中间2个字节 ipv6星化中间11个字节
+ *
+ * @param ip 原始ip
+ * @return 格式化后的ip
+ */
+ public static String ipToStars(String ip) {
+ if (TextUtils.isEmpty(ip)) {
+ return ip;
+ }
+ int index;
+ if (isIPV4Address(ip)) {
+ index = ip.indexOf(".");
+ return index == -1 ? ip : ip.substring(0, ip.indexOf(".")) + "******" + ip.substring(ip.lastIndexOf(".") + 1);
+ } else {
+ index = ip.indexOf(":");
+ return index == -1 ? ip : ip.substring(0, ip.indexOf(":")) + "******************" + ip.substring(ip.lastIndexOf(":") + 1);
+ }
+ }
+
+ public static String getValidIpAddress(NetworkInterface networkInterface) {
+ if (networkInterface == null) {
+ return "";
+ }
+
+ Enumeration addressEnum;
+ InetAddress inetAddress;
+
+ String ip;
+
+ try {
+ // up表示网卡已经启用
+ if (!networkInterface.isUp()) {
+ return "";
+ }
+
+ addressEnum = networkInterface.getInetAddresses();
+
+ for (Enumeration value = addressEnum; value.hasMoreElements(); ) {
+ inetAddress = value.nextElement();
+
+ if (inetAddress.isLoopbackAddress()) {
+ continue;
+ }
+
+ ip = inetAddress.getHostAddress();
+
+ if (!isIPV4Address(ip)) {
+ continue;
+ }
+
+ if ("10.0.2.15".equals(ip)) {
+ LogUtil.zzz(TAG, "Local Address : error ip#" + ipToStars(ip));
+ } else {
+ LogUtil.zzz(TAG, "Local Address : name#" + inetAddress.getHostName() + ",ip#" + ipToStars(ip));
+ return ip;
+ }
+ }
+ } catch (SocketException e) {
+ LogUtil.e(TAG, e.getMessage());
+ }
+
+ return "";
+ }
+
+ private static List getValidIpAddressList(NetworkInterface networkInterface) {
+ List ipList = new ArrayList<>();
+ if (networkInterface == null) {
+ return ipList;
+ }
+
+ Enumeration addressEnum;
+ InetAddress inetAddress;
+
+ try {
+ // up表示网卡已经启用
+ if (!networkInterface.isUp()) {
+ return ipList;
+ }
+
+ addressEnum = networkInterface.getInetAddresses();
+
+ String tempIp;
+ for (Enumeration value = addressEnum; value.hasMoreElements(); ) {
+ inetAddress = value.nextElement();
+
+ if (inetAddress.isLoopbackAddress()) {
+ continue;
+ }
+
+ tempIp = inetAddress.getHostAddress();
+
+ if (!isIPV4Address(tempIp)) {
+ continue;
+ }
+
+ if ("10.0.2.15".equals(tempIp)) {
+ LogUtil.d(TAG, "Local Address : getValidIp error ip#" + ipToStars(tempIp));
+ } else {
+ LogUtil.d(TAG, "Local Address : getValidIp name#" + networkInterface.getDisplayName() + ",ip#" + ipToStars(tempIp));
+ ipList.add(tempIp);
+ }
+ }
+ } catch (SocketException e) {
+ LogUtil.e(TAG, e.getMessage());
+ }
+
+ return ipList;
+ }
+
+
+ /**
+ * This method is used to is network available boolean.
+ * 网络是否可用
+ *
+ * @param context Indicates the context
+ * 上下文
+ * @return boolean If yes return TRUE, otherwise return false
+ * 是返回TRUE,否则返回false
+ */
+ public static boolean isNetworkAvailable(Context context) {
+ ConnectivityManager cm = (ConnectivityManager) context.
+ getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ NetworkInfo[] info = cm.getAllNetworkInfo();
+ if (info != null) {
+ for (int i = 0; i < info.length; ++i) {
+ if (info[i].getState() == NetworkInfo.State.CONNECTED) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/FileUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/FileUtil.java
new file mode 100644
index 0000000..2d759c2
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/FileUtil.java
@@ -0,0 +1,838 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.Context;
+import android.os.Environment;
+import android.text.TextUtils;
+
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+
+/**
+ * This class is about file Tool class.
+ */
+public final class FileUtil {
+
+ public static final String TAG = "FileUtil";
+
+ private FileUtil() {
+ }
+
+ public static final int SIZETYPE_B = 1;//获取文件大小单位为B的double值
+ public static final int SIZETYPE_KB = 2;//获取文件大小单位为KB的double值
+ public static final int SIZETYPE_MB = 3;//获取文件大小单位为MB的double值
+ public static final int SIZETYPE_GB = 4;//获取文件大小单位为GB的double值
+
+ /**
+ * copy file
+ *
+ * @param input FileInputStream
+ * @param trgPath The path to the target file to be copied
+ */
+ public static boolean copyFile(InputStream input, String trgPath) {
+ boolean sdCardExist = Environment.getExternalStorageState()
+ .equals(Environment.MEDIA_MOUNTED);
+ if (!sdCardExist) {
+ LogUtil.e(LogUtil.CLOUNDLINK, "sdcard is not exist");
+ return false;
+ }
+
+ if (null == input || TextUtils.isEmpty(trgPath)) {
+ LogUtil.e(LogUtil.CLOUNDLINK, "stream or The target path is null!");
+ return false;
+ }
+
+ BufferedInputStream inBuffStream = null;
+ FileOutputStream output = null;
+ BufferedOutputStream outBuffStream = null;
+
+ File trgFile = new File(trgPath);
+ try {
+ if (!trgFile.exists()) {
+ boolean isCreateSuccess = trgFile.createNewFile();
+ if (!isCreateSuccess) {
+ return false;
+ }
+ }
+ inBuffStream = new BufferedInputStream(input);
+ output = new FileOutputStream(trgFile);
+ outBuffStream = new BufferedOutputStream(output);
+ byte[] buffer = new byte[2 * 1024 * 1024];
+ while (true) {
+ int inBuflen = inBuffStream.read(buffer);
+ if (-1 == inBuflen) {
+ outBuffStream.flush();
+ break;
+ } else {
+ outBuffStream.write(buffer, 0, inBuflen);
+ }
+ }
+
+ return true;
+ } catch (FileNotFoundException e) {
+ LogUtil.e(LogUtil.CLOUNDLINK, e.getMessage());
+ return false;
+ } catch (IOException e) {
+ LogUtil.e(LogUtil.CLOUNDLINK, e.getMessage());
+ return false;
+ } finally {
+ Closeables.closeCloseable(outBuffStream);
+ Closeables.closeCloseable(output);
+ Closeables.closeCloseable(inBuffStream);
+ Closeables.closeCloseable(input);
+ }
+ }
+
+ /**
+ * is sdcard exist
+ *
+ * @return boolean
+ */
+ public static boolean isSdCardExist() {
+ boolean sdCardExist = Environment.getExternalStorageState()
+ .equals(Environment.MEDIA_MOUNTED);
+ if (sdCardExist) {
+ return true;
+ }
+ LogUtil.e(LogUtil.CLOUNDLINK, "sdcard is not exist");
+ return false;
+ }
+
+ public static void deleteFile(File file) {
+ deleteFile(file, null);
+ }
+
+ public static void deleteFile(File file, File[] exceptFiles) {
+ if (file == null || !file.exists()) {
+ return;
+ }
+
+ if (file.isFile() && !file.isHidden()) {
+ boolean success = file.delete();
+
+ if (!success) {
+ LogUtil.e(LogUtil.CLOUNDLINK, "delete file error ");
+ }
+
+ return;
+ }
+
+ if (file.isDirectory() && !isContainFile(file, exceptFiles)) {
+ File[] files = file.listFiles();
+ if (null != files && 0 != files.length) {
+ for (File f : files) {
+ deleteFile(f, exceptFiles);
+ }
+ }
+
+ if (!file.delete()) {
+ LogUtil.e(LogUtil.CLOUNDLINK, "delete file error ");
+ }
+ }
+ }
+
+ /**
+ * Unit conversion
+ *
+ * @param size
+ * @return String
+ */
+ public static String makeUpSizeShow(double size) {
+ double unit = 1024.0;
+ String sizeUnit = "B";
+ // to KB
+ if (unit < size) {
+ sizeUnit = "KB";
+ size = size / unit;
+ }
+ // to M
+ if (unit < size) {
+ sizeUnit = "M";
+ size = size / unit;
+ }
+ // to .00
+ DecimalFormat df = new DecimalFormat(".00");
+ return df.format(size) + sizeUnit;
+ }
+
+ private static boolean isContainFile(File file, File[] files) {
+ if (file == null || files == null || files.length == 0) {
+ return false;
+ }
+
+ for (File f : files) {
+ if (file.equals(f)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * 系统打开。
+ *
+ * @param gfFilePath
+ * @return
+ */
+ public static int openBySystem(Context context, String gfFilePath) {
+
+ return OpenResult.OPEN_BY_THIRDPARTY_FAIL;
+
+
+ }
+
+ private interface OpenResult {
+ int OPEN_SUCCESS = 0;
+ int OPEN_BY_THIRDPARTY_FAIL = 1;
+ }
+
+ /**
+ * 创建ZIP文件
+ *
+ * @param sourcePath 文件或文件夹路径
+ * @param zipPath 生成的zip文件存在路径(包括文件名)
+ */
+ public static void createZip(String sourcePath, String zipPath) {
+ FileOutputStream fos = null;
+ ZipOutputStream zos = null;
+ try {
+ fos = new FileOutputStream(zipPath);
+ zos = new ZipOutputStream(fos);
+ //createXmlFile(sourcePath,"293.xml");
+ writeZip(new File(sourcePath), "", zos);
+ } catch (FileNotFoundException e) {
+ } finally {
+ try {
+ if (zos != null) {
+ zos.close();
+ }
+ } catch (IOException e) {
+ }
+
+ }
+ }
+
+ private static void writeZip(File file, String parentPath, ZipOutputStream zos) {
+ if (file.exists()) {
+ if (file.isDirectory()) {//处理文件夹
+ parentPath += file.getName() + File.separator;
+ File[] files = file.listFiles();
+ if (files.length != 0) {
+ for (File f : files) {
+ writeZip(f, parentPath, zos);
+ }
+ } else { //空目录则创建当前目录
+ try {
+ zos.putNextEntry(new ZipEntry(parentPath));
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ } else {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ ZipEntry ze = new ZipEntry(parentPath + file.getName());
+ zos.putNextEntry(ze);
+ byte[] content = new byte[1024];
+ int len;
+ while ((len = fis.read(content)) != -1) {
+ zos.write(content, 0, len);
+ zos.flush();
+ }
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ } finally {
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+ }
+
+
+ public static String getAssetsCacheFile(Context context, String fileName) {
+ File cacheFile = new File(context.getCacheDir(), fileName);
+ try {
+ InputStream inputStream = context.getAssets().open(fileName);
+ try {
+ FileOutputStream outputStream = new FileOutputStream(cacheFile);
+ try {
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = inputStream.read(buf)) > 0) {
+ outputStream.write(buf, 0, len);
+ }
+ } finally {
+ outputStream.close();
+ }
+ } finally {
+ inputStream.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return cacheFile.getAbsolutePath();
+ }
+
+
+ /**
+ * 复制单个文件
+ *
+ * @param oldPath$Name String 原文件路径+文件名 如:data/user/0/com.test/files/abc.txt
+ * @param newPath$Name String 复制后路径+文件名 如:data/user/0/com.test/cache/abc.txt
+ * @return true
if and only if the file was copied;
+ * false
otherwise
+ */
+ public static boolean copyFile(String oldPath$Name, String newPath$Name) {
+ try {
+ File oldFile = new File(oldPath$Name);
+ if (!oldFile.exists()) {
+ return false;
+ } else if (!oldFile.isFile()) {
+ return false;
+ } else if (!oldFile.canRead()) {
+ return false;
+ }
+
+ FileInputStream fileInputStream = new FileInputStream(oldPath$Name);
+ FileOutputStream fileOutputStream = new FileOutputStream(newPath$Name);
+ byte[] buffer = new byte[1024];
+ int byteRead;
+ while (-1 != (byteRead = fileInputStream.read(buffer))) {
+ fileOutputStream.write(buffer, 0, byteRead);
+ }
+ fileInputStream.close();
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ /**
+ * 复制文件夹及其中的文件
+ *
+ * @param oldPath String 原文件夹路径 如:data/user/0/com.test/files
+ * @param newPath String 复制后的路径 如:data/user/0/com.test/cache
+ * @return true
if and only if the directory and files were copied;
+ * false
otherwise
+ */
+ public static boolean copyFolder(String oldPath, String newPath, List strings) {
+ try {
+ File newFile = new File(newPath);
+ if (!newFile.exists()) {
+ if (!newFile.mkdirs()) {
+ return false;
+ }
+ }
+ File oldFile = new File(oldPath);
+ String[] files = oldFile.list();
+ File temp;
+ for (String file : files) {
+ if (oldPath.endsWith(File.separator)) {
+ temp = new File(oldPath + file);
+ } else {
+ temp = new File(oldPath + File.separator + file);
+ }
+ if (temp.isDirectory()) { //如果是子文件夹
+ copyFolder(oldPath + "/" + file, newPath + "/" + file, strings);
+ } else if (!temp.exists()) {
+ return false;
+ } else if (!temp.isFile()) {
+ return false;
+ } else if (!temp.canRead()) {
+ return false;
+ } else {
+ if (file.contains(".zip") && !containStr(strings, file)) {
+ continue;
+ }
+ FileInputStream fileInputStream = new FileInputStream(temp);
+ FileOutputStream fileOutputStream = new FileOutputStream(newPath + "/" + temp.getName());
+ byte[] buffer = new byte[1024];
+ int byteRead;
+ while ((byteRead = fileInputStream.read(buffer)) != -1) {
+ fileOutputStream.write(buffer, 0, byteRead);
+ }
+ fileInputStream.close();
+ fileOutputStream.flush();
+ fileOutputStream.close();
+ }
+ }
+ return true;
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ private static boolean containStr(List strings, String file) {
+ for (int i = 0; i < strings.size(); i++) {
+ if (strings.get(i).length() > 7 && file.contains(strings.get(i))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void updataFiles() throws ParseException {
+ String today = TimeUtils.getToday2(); //当前时间
+ getFilesAllName(BaseAppContext.getInstance().getFilesDir() + "/HWCloudLink/Log", today);
+ }
+
+ private static void getFilesAllName(String path, String today) throws ParseException {
+ File file = new File(path);
+ File[] files = file.listFiles();
+ if (files == null) {
+ LogUtil.zzz("空目录");
+ return;
+ }
+
+ for (int i = 0; i < files.length; i++) {
+ if (!isRqFormat(files[i].getAbsolutePath().split("Log/")[1])) {
+ deleteDirectory(files[i].getAbsolutePath());
+ continue;
+ }
+ if (TimeUtils.isWhichday(files[i].getAbsolutePath().split("Log/")[1], today) < 0) {
+ deleteDirectory(files[i].getAbsolutePath());
+ LogUtil.zzz("af:" + files[i].getAbsolutePath());
+ }
+ }
+ }
+
+ /***
+ * 判断字符串是否是yyyyMMdd格式
+ * @param mes 字符串
+ * @return boolean 是否是日期格式
+ */
+ public static boolean isRqFormat(String mes) {
+ String format = "([0-9]{4})-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])";
+ Pattern pattern = Pattern.compile(format);
+ Matcher matcher = pattern.matcher(mes);
+ if (matcher.matches()) {
+ pattern = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})");
+ matcher = pattern.matcher(mes);
+ if (matcher.matches()) {
+ int y = Integer.valueOf(matcher.group(1));
+ int m = Integer.valueOf(matcher.group(2));
+ int d = Integer.valueOf(matcher.group(3));
+ if (d > 28) {
+ Calendar c = Calendar.getInstance();
+ c.set(y, m - 1, 1);
+ //每个月的最大天数
+ int lastDay = c.getActualMaximum(Calendar.DAY_OF_MONTH);
+ return (lastDay >= d);
+ }
+ }
+ return true;
+ }
+ return false;
+
+ }
+
+
+ //清空文件夹
+
+ /**
+ * 删除目录及目录下的文件
+ */
+ public static boolean deleteDirectory(String dir) {
+ // 如果dir不以文件分隔符结尾,自动添加文件分隔符
+ if (!dir.endsWith(File.separator))
+ dir = dir + File.separator;
+ File dirFile = new File(dir);
+ // 如果dir对应的文件不存在,或者不是一个目录,则退出
+ if ((!dirFile.exists()) || (!dirFile.isDirectory())) {
+ System.out.println("删除目录失败:" + dir + "不存在!");
+ return false;
+ }
+ boolean flag = true;
+ // 删除文件夹中的所有文件包括子目录
+ File[] files = dirFile.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ // 删除子文件
+ if (files[i].isFile()) {
+ flag = deleteFile(files[i].getAbsolutePath());
+ if (!flag)
+ break;
+ }
+ // 删除子目录
+ else if (files[i].isDirectory()) {
+ flag = deleteDirectory(files[i]
+ .getAbsolutePath());
+ if (!flag)
+ break;
+ }
+ }
+ if (!flag) {
+ System.out.println("删除目录失败!");
+ return false;
+ }
+ // 删除当前目录
+ if (dirFile.delete()) {
+ System.out.println("删除目录" + dir + "成功!");
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * 删除单个文件
+ *
+ * @param fileName 要删除的文件的文件名
+ * @return 单个文件删除成功返回true,否则返回false
+ */
+ public static boolean deleteFile(String fileName) {
+ File file = new File(fileName);
+ // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
+ if (file.exists() && file.isFile()) {
+ if (file.delete()) {
+ System.out.println("删除单个文件" + fileName + "成功!");
+ return true;
+ } else {
+ System.out.println("删除单个文件" + fileName + "失败!");
+ return false;
+ }
+ } else {
+ System.out.println("删除单个文件失败:" + fileName + "不存在!");
+ return false;
+ }
+ }
+
+ public static void deleteFolderFile(String filePath, String str1, String str2, String str3) {
+ try {
+ File file = new File(filePath);//获取SD卡指定路径
+ File[] files = file.listFiles();//获取SD卡指定路径下的文件或者文件夹
+ for (int i = 0; i < files.length; i++) {
+ //删除不是选择的3天内的
+ if (files[i].getName().equals(str1) || files[i].getName().equals(str2) || files[i].getName().equals(str3)) {
+
+ } else {
+ deleteDirectory(files[i].getPath());
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void writeData(String file_path, String file_name, String contact) {
+ String filePath = file_path;
+ String fileName = file_name;
+ writeTxtToFile(contact, filePath, fileName);
+ }
+
+ // 将字符串写入到文本文件中
+ private static void writeTxtToFile(String strcontent, String filePath, String fileName) {
+ //生成文件夹之后,再生成文件,不然会出错
+ makeFilePath(filePath, fileName);
+
+ String strFilePath = filePath + fileName;
+ // 每次写入时,都换行写
+ String strContent = strcontent + "\r\n";
+ try {
+ File file = new File(strFilePath);
+ if (!file.exists()) {
+ file.getParentFile().mkdirs();
+ file.createNewFile();
+ }
+ RandomAccessFile raf = new RandomAccessFile(file, "rwd");
+ raf.seek(file.length());
+ raf.write(strContent.getBytes());
+ raf.close();
+ } catch (Exception e) {
+ }
+ }
+
+ //生成文件
+ private static File makeFilePath(String filePath, String fileName) {
+ File file = null;
+ makeRootDirectory(filePath);
+ try {
+ file = new File(filePath + fileName);
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return file;
+ }
+
+ //生成文件夹
+
+ private static void makeRootDirectory(String filePath) {
+ File file = null;
+ try {
+ file = new File(filePath);
+ if (!file.exists()) {
+ file.mkdir();
+ }
+ } catch (Exception e) {
+ }
+ }
+
+ public static String stringFilter(String str) throws PatternSyntaxException {
+ // 只允许字母、数字和汉字 _ -
+ String regEx = "[^a-zA-Z0-9_\\w-\u4E00-\u9FA5]";
+ Pattern p = Pattern.compile(regEx);
+ Matcher m = p.matcher(str);
+ return m.replaceAll("").trim();
+ }
+
+ /**
+ * 判断字符串占了多少字节
+ *
+ * @param value
+ * @return
+ */
+ public static int chineseLength(String value) {
+ int valueLength = 0;
+ String chinese = "[\u0391-\uFFE5]";/*这是汉语的这则表达式*/
+ for (int i = 0; i < value.length(); i++) {
+ /* 获取一个字符 */
+ String temp = value.substring(i, i + 1);
+ /* 判断是否为中文字符 */
+ if (temp.matches(chinese)) {
+ /* 中文字符长度为2 */
+ valueLength += 3;
+ } else {
+ /* 其他字符长度为1 */
+ valueLength += 1;
+ }
+ }
+ return valueLength;
+ }
+
+ /**
+ * 判断字符串是否超过64位,并在第几位超过的
+ *
+ * @param value
+ * @return
+ */
+ public static int chineseLength(String value, int length) {
+ int valueLength = 0;
+ String chinese = "[\u0391-\uFFE5]";/*这是汉语的这则表达式*/
+ for (int i = 0; i < value.length(); i++) {
+ /* 获取一个字符 */
+ String temp = value.substring(i, i + 1);
+ /* 判断是否为中文字符 */
+ if (temp.matches(chinese)) {
+ /* 中文字符长度为2 */
+ valueLength += 3;
+ } else {
+ /* 其他字符长度为1 */
+ valueLength += 1;
+ }
+ if (valueLength > length) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * 获取文件指定文件的指定单位的大小
+ *
+ * @param filePath 文件路径
+ * @param sizeType 获取大小的类型1为B、2为KB、3为MB、4为GB
+ * @return double值的大小
+ */
+ public static double getFileOrFilesSize(String filePath, int sizeType) {
+ File file = new File(filePath);
+ long blockSize = 0;
+ try {
+ if (file.isDirectory()) {
+ blockSize = getFileSizes(file);
+ } else {
+ blockSize = getFileSize(file);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return FormetFileSize(blockSize, sizeType);
+ }
+
+ /**
+ * 获取指定文件大小
+ *
+ * @param file
+ * @return
+ * @throws Exception
+ */
+ private static long getFileSize(File file) throws Exception {
+ long size = 0;
+ if (file.exists()) {
+ FileInputStream fis = null;
+ fis = new FileInputStream(file);
+ size = fis.available();
+ } else {
+ file.createNewFile();
+ }
+ return size;
+ }
+
+ /**
+ * 获取指定文件夹
+ *
+ * @param f
+ * @return
+ * @throws Exception
+ */
+ private static long getFileSizes(File f) throws Exception {
+ long size = 0;
+ File[] flist = f.listFiles();
+ for (int i = 0; i < flist.length; i++) {
+ if (flist[i].isDirectory()) {
+ size = size + getFileSizes(flist[i]);
+ } else {
+ size = size + getFileSize(flist[i]);
+ }
+ }
+ return size;
+ }
+
+ /**
+ * 转换文件大小
+ *
+ * @param fileS
+ * @return
+ */
+ private static String FormetFileSize(long fileS) {
+ DecimalFormat df = new DecimalFormat("#.00");
+ String fileSizeString = "";
+ String wrongSize = "0B";
+ if (fileS == 0) {
+ return wrongSize;
+ }
+ if (fileS < 1024) {
+ fileSizeString = df.format((double) fileS) + "B";
+ } else if (fileS < 1048576) {
+ fileSizeString = df.format((double) fileS / 1024) + "KB";
+ } else if (fileS < 1073741824) {
+ fileSizeString = df.format((double) fileS / 1048576) + "MB";
+ } else {
+ fileSizeString = df.format((double) fileS / 1073741824) + "GB";
+ }
+ return fileSizeString;
+ }
+
+ /**
+ * 转换文件大小,指定转换的类型
+ *
+ * @param fileS
+ * @param sizeType
+ * @return
+ */
+ private static double FormetFileSize(long fileS, int sizeType) {
+ DecimalFormat df = new DecimalFormat("#.00");
+ double fileSizeLong = 0;
+ switch (sizeType) {
+ case SIZETYPE_B:
+ fileSizeLong = Double.valueOf(df.format((double) fileS));
+ break;
+ case SIZETYPE_KB:
+ fileSizeLong = Double.valueOf(df.format((double) fileS / 1024));
+ break;
+ case SIZETYPE_MB:
+ fileSizeLong = Double.valueOf(df.format((double) fileS / 1048576));
+ break;
+ case SIZETYPE_GB:
+ fileSizeLong = Double.valueOf(df.format((double) fileS / 1073741824));
+ break;
+ default:
+ break;
+ }
+ return fileSizeLong;
+ }
+
+
+ /**
+ * 将asset文件写入缓存
+ */
+ public static boolean copyAssetToFiles(String dirsName, String fileName) {
+ try {
+ File cacheDir = new File(BaseAppContext.getInstance().getFilesDir().getAbsolutePath() + "/" + dirsName);
+ if (!cacheDir.exists()) {
+ cacheDir.mkdirs();
+ }
+ File outFile = new File(cacheDir, fileName);
+ if (!outFile.exists()) {
+ boolean res = outFile.createNewFile();
+ if (!res) {
+ return false;
+ }
+ } else {
+ if (outFile.length() > 10) {//表示已经写入一次
+ return true;
+ }
+ }
+ InputStream is = BaseAppContext.getInstance().getAssets().open(dirsName + "/" + fileName);
+ FileOutputStream fos = new FileOutputStream(outFile);
+ byte[] buffer = new byte[1024];
+ int byteCount;
+ while ((byteCount = is.read(buffer)) != -1) {
+ fos.write(buffer, 0, byteCount);
+ }
+ fos.flush();
+ is.close();
+ fos.close();
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return false;
+ }
+
+ /**
+ * 是否存在su命令,并且有执行权限
+ *
+ * @return 存在su命令,并且有执行权限返回true
+ */
+ public static boolean isSuEnable() {
+ File file = null;
+ String[] paths = {"/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin/", "/vendor/bin/", "/su/bin/"};
+ try {
+ for (String path : paths) {
+ file = new File(path + "su");
+ if (file.exists() && file.canExecute()) {
+ LogUtil.i(TAG, "find su in : " + path);
+ return true;
+ }
+ }
+ } catch (Exception x) {
+ x.printStackTrace();
+ }
+ return false;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/FirstLetterUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/FirstLetterUtil.java
new file mode 100644
index 0000000..f598093
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/FirstLetterUtil.java
@@ -0,0 +1,185 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import android.text.TextUtils;
+
+import com.github.promeg.pinyinhelper.Pinyin;
+
+/**
+ * 获取首字母
+ */
+public class FirstLetterUtil {
+ private FirstLetterUtil() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ private static int BEGIN = 45217;
+ private static int END = 63486;
+ // 按照声母表示,这个表是在GB2312中的出现的第一个汉字,也就是说“啊”是代表首字母a的第一个汉字。
+ // i, u, v都不做声母, 自定规则跟随前面的字母
+ private static char[] chartable = {'啊', '芭', '擦', '搭', '蛾', '发', '噶', '哈',
+ '哈', '击', '喀', '垃', '妈', '拿', '哦', '啪', '期', '然', '撒', '塌', '塌',
+ '塌', '挖', '昔', '压', '匝',};
+ // 二十六个字母区间对应二十七个端点
+ // GB2312码汉字区间十进制表示
+ private static int[] table = new int[27];
+ // 对应首字母区间表
+ private static char[] initialtable = {'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'h', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
+ 't', 't', 'w', 'x', 'y', 'z',};
+
+ // 初始化
+ static {
+ for (int i = 0; i < 26; i++) {
+ table[i] = gbValue(chartable[i]);// 得到GB2312码的首字母区间端点表,十进制。
+ }
+ table[26] = END;// 区间表结尾
+ }
+
+ /**
+ * 根据一个包含汉字的字符串返回一个汉字拼音首字母的字符串 最重要的一个方法,思路如下:一个个字符读入、判断、输出
+ */
+ public static String getFirstLetter1(String sourceStr) {
+ if (TextUtils.isEmpty(sourceStr)) {
+ return "#";
+ }
+ String str = sourceStr.toUpperCase();
+ char ch = Char2Initial(str.charAt(0));
+ if (isLetter(ch)) {
+ return String.valueOf(ch).toUpperCase();
+ }
+ return "#";
+ }
+
+ /**
+ * 返回是否是字母
+ */
+ private static boolean isLetter(char ch) {
+ if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 返回是否是字母
+ */
+ private static boolean isLetterNum(char ch) {
+ if ((ch >= '0' && ch <= '9')) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 汉字转拼音
+ */
+ public static String getPingYin(String inputString) {
+ if (TextUtils.isEmpty(inputString)) {
+ return "#";
+ }
+ String s = Pinyin.toPinyin(inputString, "");
+
+ return s.toLowerCase();
+ }
+
+ /**
+ * 获取指定串的首字母
+ */
+ public static String getAllFirstLetter(String sourceStr) {
+ String result = "";
+ String str = sourceStr.toUpperCase();
+ int StrLength = str.length();
+ int i;
+ try {
+ for (i = 0; i < StrLength; i++) {
+ result += Char2Initial(str.charAt(i));
+ }
+ } catch (Exception e) {
+ result = "";
+ }
+ return result;
+ }
+
+ public static String getFirstLetter(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return "#";
+ }
+ char ch = str.charAt(0);
+ if (ch >= 'a' && ch <= 'z') {
+ return (char) (ch - 'a' + 'A') + "";
+ }
+ if (ch >= 'A' && ch <= 'Z') {
+ return ch + "";
+ }
+ if (ch >= '0' && ch <= '9') {
+ return ch + "";
+ }
+ try {
+ String s = Pinyin.toPinyin(str, "");
+ return s.charAt(0) + "";
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "#";
+ }
+
+ /**
+ * 输入字符,得到他的声母,英文字母返回对应的大写字母,其他非简体汉字返回 '0'
+ */
+ private static char Char2Initial(char ch) {
+ // 对英文字母的处理:小写字母转换为大写,大写的直接返回
+ if (ch >= 'a' && ch <= 'z') {
+ return ch;
+ }
+ if (ch >= 'A' && ch <= 'Z') {
+ return ch;
+ }
+ if (ch >= '0' && ch <= '9') {
+ return ch;
+ }
+ // 对非英文字母的处理:转化为首字母,然后判断是否在码表范围内,
+ // 若不是,则直接返回。
+ // 若是,则在码表内的进行判断。
+ int gb = gbValue(ch);// 汉字转换首字母
+
+ if ((gb < BEGIN) || (gb > END))// 在码表区间之前,直接返回
+ {
+ return ch;
+ }
+
+ int i;
+ for (i = 0; i < 26; i++) {// 判断匹配码表区间,匹配到就break,判断区间形如“[,)”
+ if ((gb >= table[i]) && (gb < table[i + 1])) {
+ break;
+ }
+ }
+
+ if (gb == END) {// 补上GB2312区间最右端
+ i = 25;
+ }
+ return initialtable[i]; // 在码表区间中,返回首字母
+ }
+
+ /**
+ * 取出汉字的编码 cn 汉字
+ */
+ private static int gbValue(char ch) {// 将一个汉字(GB2312)转换为十进制表示。
+ String str = new String();
+ str += ch;
+ try {
+ byte[] bytes = str.getBytes("GB2312");
+ if (bytes.length < 2) {
+ return 0;
+ }
+ return (bytes[0] << 8 & 0xff00) + (bytes[1] & 0xff);
+ } catch (Exception e) {
+ return 0;
+ }
+ }
+
+ public static String getAllPinYin(String var) {
+ String s = Pinyin.toPinyin(var, "");
+ return s;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/GMUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/GMUtil.java
new file mode 100644
index 0000000..887756b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/GMUtil.java
@@ -0,0 +1,39 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.Context;
+
+import com.huawei.ecterminalsdk.base.TsdkTlsInParam;
+import com.huawei.ecterminalsdk.base.TsdkTlsSmParam;
+import com.tengshisoft.chatmodule.hwclud.serivce.ServiceManger;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+public class GMUtil {
+ /**
+ * 设置国密证书
+ */
+ public static void setSecretCertificate(Context context, boolean isChecked) {
+ int openSmVerfity = isChecked ? 1 : 0;
+ // TLS参数设置
+ String inDirPath = context.getFilesDir() + "/" + BaseAppContext.inDir;
+ String smDirPath = context.getFilesDir() + "/" + BaseAppContext.smDir;
+ String rootPath = context.getFilesDir() + "/root";
+ String smRootPath = context.getFilesDir() + "/smRoot";
+ TsdkTlsInParam inTlsParam = new TsdkTlsInParam();
+ inTlsParam.setCaCertPath(rootPath + "/"); // TLS根证书路径
+ inTlsParam.setClientCertPath(inDirPath + "/equip.pem"); // TLS服务端证书地址
+ inTlsParam.setClientKeyPath(inDirPath + "/equip_key.pem"); // TLS服务端私钥证书地址
+ inTlsParam.setClientPrivkeyPwd("Change_Me"); // TLS服务端私钥加密口令
+ // 国密参数设置
+ TsdkTlsSmParam smTlsParam = new TsdkTlsSmParam();
+ smTlsParam.setSmGmcaDirPath(smRootPath + "/");//
+ smTlsParam.setSmSignCertPath(smDirPath + "/equip_cloudlink.pem");//签名设备证书路径
+ smTlsParam.setSmSignKeyCertPath(smDirPath + "/equip_key_cloudlink.pem");//签名设备证书私钥文件路径
+ smTlsParam.setSmSignKeyFilePsw("Change_Me");//签名设备证书私钥文件的加密口令
+ smTlsParam.setSmEncCertPath(smDirPath + "/equip_cloudlink_enc.pem");//加密设备证书路径
+ smTlsParam.setSmEncKeyCertPath(smDirPath + "/equip_key_cloudlink_enc.pem");//加密设备证书私钥文件路径
+ smTlsParam.setSmEncKeyFilePsw("Change_Me");//加密设备证书私钥文件的加密口令
+
+ // 调用TLS接口
+ ServiceManger.getServiceMgr().tlsParam(openSmVerfity, inTlsParam, smTlsParam);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/HuaweiUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/HuaweiUtils.java
new file mode 100644
index 0000000..0357504
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/HuaweiUtils.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
+ */
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Build;
+import android.util.Log;
+
+
+import com.hjq.toast.ToastUtils;
+
+import java.lang.reflect.Method;
+
+public class HuaweiUtils {
+ private static final String TAG = "HuaweiUtils";
+
+ /**
+ * 检测 Huawei 悬浮窗权限
+ */
+ public static boolean checkFloatWindowPermission(Context context) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
+ }
+ return true;
+ }
+
+ /**
+ * 去华为权限申请页面
+ */
+ public static void applyPermission(Context context) {
+ try {
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+// ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理
+// ComponentName comp = new ComponentName("com.huawei.systemmanager",
+// "com.huawei.permissionmanager.ui.SingleAppActivity");//华为权限管理,跳转到指定app的权限管理位置需要华为接口权限,未解决
+ ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面
+ intent.setComponent(comp);
+ if (RomUtils.getEmuiVersion() == 3.1) {
+ //emui 3.1 的适配
+ context.startActivity(intent);
+ } else {
+ //emui 3.0 的适配
+ comp = new ComponentName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity");//悬浮窗管理页面
+ intent.setComponent(comp);
+ context.startActivity(intent);
+ }
+ } catch (SecurityException e) {
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+// ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理
+ ComponentName comp = new ComponentName("com.huawei.systemmanager",
+ "com.huawei.permissionmanager.ui.MainActivity");//华为权限管理,跳转到本app的权限管理页面,这个需要华为接口权限,未解决
+// ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面
+ intent.setComponent(comp);
+ context.startActivity(intent);
+ Log.e(TAG, Log.getStackTraceString(e));
+ } catch (ActivityNotFoundException e) {
+ /**
+ * 手机管家版本较低 HUAWEI SC-UL10
+ */
+// Toast.makeText(MainActivity.this, "act找不到", Toast.LENGTH_LONG).show();
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ ComponentName comp = new ComponentName("com.Android.settings", "com.android.settings.permission.TabItem");//权限管理页面 android4.4
+// ComponentName comp = new ComponentName("com.android.settings","com.android.settings.permission.single_app_activity");//此处可跳转到指定app对应的权限管理页面,但是需要相关权限,未解决
+ intent.setComponent(comp);
+ context.startActivity(intent);
+ Log.e(TAG, Log.getStackTraceString(e));
+ } catch (Exception e) {
+ //抛出异常时提示信息
+ ToastUtils.show("进入设置页面失败,请手动设置");
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+
+ //com.android.packageinstaller/.permission.ui.ManagePermissionsActivity
+ public static void toPermisstionSetting(Context context) {
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setAction("android.intent.action.MANAGE_APP_PERMISSIONS");
+ ComponentName comp = new ComponentName("com.android.packageinstaller", "com.android.packageinstaller.permission.ui.ManagePermissionsActivity");
+ intent.setComponent(comp);
+ context.startActivity(intent);
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static boolean checkOp(Context context, int op) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ try {
+ Class clazz = AppOpsManager.class;
+ Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
+ return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ } else {
+ Log.e(TAG, "Below API 19 cannot invoke!");
+ }
+ return false;
+ }
+}
+
+
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ImpPermission.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ImpPermission.java
new file mode 100644
index 0000000..de8771d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ImpPermission.java
@@ -0,0 +1,91 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+
+import com.tengshisoft.chatmodule.hwclud.api.IAgree;
+import com.tengshisoft.chatmodule.hwclud.api.IPermission;
+import com.tengshisoft.chatmodule.hwclud.api.IRefuse;
+import com.tenlionsoft.baselib.constant.PermissionConstants;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+public abstract class ImpPermission implements IPermission {
+
+ protected int code;
+ protected IAgree iAgree;
+ protected IRefuse iRefuse;
+ protected WeakReference context;
+ protected List requestPermission;
+
+ @Override
+ public boolean reCheck() {
+ if (context == null || context.get() == null) {
+ return false;
+ }
+ for (String permission : requestPermission) {
+ if (ContextCompat.checkSelfPermission(context.get(), permission) != PackageManager.PERMISSION_GRANTED) {
+ ActivityCompat.requestPermissions(context.get(), requestPermission.toArray(new String[0]), code);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean checkPermission(Activity context, @Nullable IAgree iAgree) {
+ this.iAgree = iAgree;
+ this.context = new WeakReference<>(context);
+ return reCheck();
+ }
+
+ @Override
+ public boolean checkPermission(Activity context, @Nullable IAgree iAgree, @Nullable IRefuse iRefuse) {
+ this.iRefuse = iRefuse;
+ return checkPermission(context, iAgree);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode == code) {
+ boolean hasPermission;
+ boolean notAgainAsk = false;
+ List refusePermissions = null;
+ for (int i = 0; i < permissions.length; i++) {
+ hasPermission = grantResults[i] == PackageManager.PERMISSION_GRANTED;
+ if (!hasPermission) {
+ if (permissions[i].equals(PermissionConstants.READ_PHONE_STATE)) {
+ requestPermission.remove(PermissionConstants.READ_PHONE_STATE);
+ continue;
+ }
+ if (refusePermissions == null) {
+ refusePermissions = new ArrayList<>();
+ }
+ String permissionRes = PermissionConstants.getPermissionRes(context.get(), permissions[i]);
+ if (refusePermissions.indexOf(permissionRes) < 0) {
+ refusePermissions.add(permissionRes);
+ }
+ if (!ActivityCompat.shouldShowRequestPermissionRationale(context.get(), permissions[i])) {
+ notAgainAsk = true;
+ }
+ }
+ }
+ if (refusePermissions != null && refusePermissions.size() > 0) {
+ if (iRefuse != null) {
+ iRefuse.refuse(refusePermissions, notAgainAsk);
+ }
+ return;
+ }
+ if (iAgree != null) {
+ iAgree.agree();
+ }
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ImportFileUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ImportFileUtil.java
new file mode 100644
index 0000000..4d19600
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ImportFileUtil.java
@@ -0,0 +1,84 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.os.Environment;
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @Time: 2021/6/10
+ * @Author: isoftstone
+ * @Description:拷贝引入资源文件工具类
+ */
+public class ImportFileUtil {
+
+ private ImportFileUtil() {
+ }
+
+ /**
+ * 引入资源文件
+ */
+ public static void importFile() {
+ if (FileUtil.isSdCardExist()) {
+ try {
+ // importMediaFile
+ String mediaPath = Environment.getExternalStorageDirectory() + File.separator +
+ ConstantsV2.RINGING_FILE;
+ InputStream mediaInputStream = BaseAppContext.getInstance().getAssets()
+ .open(ConstantsV2.RINGING_FILE);
+ FileUtil.copyFile(mediaInputStream, mediaPath);
+ String ringBackPath = Environment.getExternalStorageDirectory() + File.separator +
+ ConstantsV2.RING_BACK_FILE;
+ InputStream ringBackInputStream = BaseAppContext.getInstance().getAssets()
+ .open(ConstantsV2.RING_BACK_FILE);
+ FileUtil.copyFile(ringBackInputStream, ringBackPath);
+ // importBmpFile
+ InputStream bmpFileStream = BaseAppContext.getInstance().getAssets().open(ConstantsV2.BMP_FILE);
+ FileUtil.copyFile(bmpFileStream, ConstantsV2.BMP_FILE_PATH);
+ // importAnnotFile
+ String bmpPath = Environment.getExternalStorageDirectory() + File.separator +
+ ConstantsV2.ANNOT_FILE;
+ File file = new File(bmpPath);
+ if (!file.exists()) {
+ file.mkdir();
+ }
+ String[] bmpNames = new String[]{"check.bmp", "xcheck.bmp", "lpointer.bmp",
+ "rpointer.bmp", "upointer.bmp", "dpointer.bmp", "lp.bmp"};
+ for (int i = 0; i < bmpNames.length; i++) {
+ String path = bmpPath + File.separator + bmpNames[i];
+ InputStream bmpInputStream = BaseAppContext.getInstance().getAssets().open(bmpNames[i]);
+ FileUtil.copyFile(bmpInputStream, path);
+ }
+ } catch (IOException e) {
+ LogUtil.e(LogUtil.CLOUNDLINK, e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * 是否存在su命令并且有执行权限
+ *
+ * @param targetTag 目标类tag
+ * @return 存在su命令并且有执行权限返回true
+ */
+ public static boolean isSuEnable(String targetTag) {
+ String[] paths = {"/system/bin/", "/system/xbin/", "/system/sbin/",
+ "/sbin/", "/vendor/bin/", "/su/bin/"};
+ try {
+ for (String path : paths) {
+ File file = new File(path + "su");
+ if (file.exists() && file.canExecute()) {
+ LogUtil.zzz(targetTag, "find su in : " + path);
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/JoinMeetingHistoryUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/JoinMeetingHistoryUtils.java
new file mode 100644
index 0000000..df4dbba
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/JoinMeetingHistoryUtils.java
@@ -0,0 +1,152 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+
+import java.util.ArrayList;
+
+/**
+ * @author Jelly
+ * @version 1.0
+ * @e-mail denggd0519@thundersoft.com
+ * @date 2020/12/15 14:28
+ * @desc 加入会议历史工具操作类
+ */
+public class JoinMeetingHistoryUtils {
+ /**
+ * 添加数据
+ *
+ * @param context
+ * @param contentValues
+ * @return
+ */
+ public static long addJoinMeetingHistory(Context context, ContentValues contentValues) {
+ return 10;
+// DatabaseHelper databaseHelper = new DatabaseHelper(context, DatabaseHelper.DB_NAME,
+// null, DatabaseHelper.DB_VERSION);
+// SQLiteDatabase db = databaseHelper.getWritableDatabase();
+// long flag;
+// flag = db.insert(JoinMeetingHistoryTable.TABLE_NAME, null, contentValues);
+// db.close();
+// return flag;
+ }
+
+ /**
+ * 查询所有历史记录
+ *
+ * @param context
+ * @param version
+ * @return
+ */
+ public static ArrayList getAllMeetingHistory(Context context, String version) {
+ return null;
+// Log.e("getAllMeetingHistory", "version = " + version);
+// ArrayList contacts = new ArrayList<>();
+// DatabaseHelper databaseHelper = new DatabaseHelper(context, DatabaseHelper.DB_NAME,
+// null, DatabaseHelper.DB_VERSION);
+// SQLiteDatabase db = databaseHelper.getWritableDatabase();
+// String ucacc = LoginMangerV2.getInstance().getAccount();
+// String selection = JoinMeetingHistoryTable.ACCOUNT + "= ? and " + JoinMeetingHistoryTable.VERSION + "= ?";
+// String[] selectionArgs = new String[]{ucacc, version};
+//
+// Cursor cursor = db.query(JoinMeetingHistoryTable.TABLE_NAME, null, selection,
+// selectionArgs, null, null,
+// JoinMeetingHistoryTable.ADD_TIME + " desc limit 5");
+//
+// MeetingHistory temp;
+// while (cursor.moveToNext()) {
+// // 新建一个联系人实例
+// temp = new MeetingHistory();
+// temp.setAccount(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.ACCOUNT)));
+// temp.setVersion(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.VERSION)));
+// temp.setTitle(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.TITLE)));
+// temp.setAccountnumber(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.ACCOUNT_NUMBER)));
+// temp.setMeetingpassword(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.MEETING_PSW)));
+// temp.setOther(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.ADD_TIME)));
+// temp.setOther(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.OTHER)));
+// contacts.add(temp);
+// }
+// cursor.close();
+// return contacts;
+ }
+
+ /**
+ * 查询某一条会议历史记录
+ *
+ * @param context
+ * @param version
+ * @param accountnumber
+ * @return
+ */
+ public static ArrayList getItemMeetingHistory(Context context, String version,
+ String accountnumber) {
+ return null;
+// ArrayList contacts = new ArrayList<>();
+// DatabaseHelper databaseHelper = new DatabaseHelper(context, DatabaseHelper.DB_NAME,
+// null, DatabaseHelper.DB_VERSION);
+// SQLiteDatabase db = databaseHelper.getWritableDatabase();
+// String ucacc = LoginMangerV2.getInstance().getAccount();
+// String selection = JoinMeetingHistoryTable.ACCOUNT + "= ? and " +
+// JoinMeetingHistoryTable.VERSION + "= ? and " +
+// JoinMeetingHistoryTable.ACCOUNT_NUMBER + "= ?";
+// String[] selectionArgs = new String[]{ucacc, version, accountnumber};
+//
+// Cursor cursor = db.query(JoinMeetingHistoryTable.TABLE_NAME, null, selection,
+// selectionArgs, null, null, JoinMeetingHistoryTable.ID);
+//
+// MeetingHistory temp;
+// while (cursor.moveToNext()) {
+// // 新建一个联系人实例
+// temp = new MeetingHistory();
+// temp.setAccount(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.ACCOUNT)));
+// temp.setVersion(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.VERSION)));
+// temp.setTitle(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.TITLE)));
+// temp.setAccountnumber(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.ACCOUNT_NUMBER)));
+// temp.setMeetingpassword(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.MEETING_PSW)));
+// temp.setOther(cursor.getString(cursor.getColumnIndex(JoinMeetingHistoryTable.OTHER)));
+// contacts.add(temp);
+// }
+// cursor.close();
+// return contacts;
+ }
+
+ /**
+ * 删除所有历史记录 2.0 和3.0分开
+ *
+ * @param context
+ * @param version
+ */
+ public static void clearAllMeetingHistory(Context context, String version) {
+// DatabaseHelper databaseHelper = new DatabaseHelper(context, DatabaseHelper.DB_NAME,
+// null, DatabaseHelper.DB_VERSION);
+// SQLiteDatabase db = databaseHelper.getWritableDatabase();
+// String ucacc = LoginMangerV2.getInstance().getAccount();
+// String whereClause = JoinMeetingHistoryTable.ACCOUNT + "=? and " +
+// JoinMeetingHistoryTable.VERSION + "= ?";
+// String[] whereArgs = new String[]{ucacc, version};
+// db.delete(JoinMeetingHistoryTable.TABLE_NAME, whereClause, whereArgs);
+// db.close();
+ }
+
+ /**
+ * 删除某一条历史记录
+ *
+ * @param context
+ * @param accountnumber
+ */
+ public static void deleteItemMeetingHistory(Context context, String accountnumber) {
+// DatabaseHelper databaseHelper = new DatabaseHelper(context, DatabaseHelper.DB_NAME,
+// null, DatabaseHelper.DB_VERSION);
+// SQLiteDatabase db = databaseHelper.getWritableDatabase();
+// String ucacc = LoginMangerV2.getInstance().getAccount();
+// String whereClause = JoinMeetingHistoryTable.ACCOUNT + "=? and " +
+// JoinMeetingHistoryTable.ACCOUNT_NUMBER + "= ?";
+// String[] whereArgs = new String[]{ucacc, accountnumber};
+// db.delete(JoinMeetingHistoryTable.TABLE_NAME, whereClause, whereArgs);
+// db.close();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ListTools.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ListTools.java
new file mode 100644
index 0000000..b5db10b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ListTools.java
@@ -0,0 +1,65 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import java.util.List;
+
+/**
+ * @Time: 2021/7/14
+ * @Author: isoftstone
+ * @Description: 集合判断工具
+ */
+public class ListTools {
+ private ListTools() {
+ }
+
+ /**
+ * 判断集合是否为空
+ *
+ * @param list 集合
+ * @return true 为空 false 不为空
+ */
+ public static boolean empty(List list) {
+ return list == null;
+ }
+
+ /**
+ * 判断集合是否为空
+ *
+ * @param list 集合
+ * @return true 为空 false 不为空
+ */
+ public static boolean isEmpty(List list) {
+ return list == null || list.isEmpty();
+ }
+
+ /**
+ * 判断集合是否大于长度
+ *
+ * @param list 集合
+ * @param size 长度
+ * @return true 为空 false 不为空
+ */
+ public static boolean isThan(List list, int size) {
+ return list != null && list.size() > size;
+ }
+
+ /**
+ * 判断集合是否小于长度
+ *
+ * @param list 集合
+ * @param size 长度
+ * @return true 为空 false 不为空
+ */
+ public static boolean isLess(List list, int size) {
+ return list == null || list.size() < size;
+ }
+
+ /**
+ * 判断对象属性是否为空
+ *
+ * @param mAttr 对象属性
+ * @return
+ */
+ public static boolean isAttrEmpty(String mAttr) {
+ return mAttr == null || mAttr.isEmpty();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LocContext.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LocContext.java
new file mode 100644
index 0000000..472c843
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LocContext.java
@@ -0,0 +1,68 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+
+import java.io.File;
+
+/**
+ * This class is about context.
+ * 上下文数据类
+ */
+public class LocContext
+{
+ private static Context context;
+
+ public static void init(Context context)
+ {
+ LocContext.context = context;
+ }
+
+ public static Context getContext()
+ {
+ return context;
+ }
+
+ public static ContentResolver getContentResolver()
+ {
+ return context.getContentResolver();
+ }
+
+ public static Resources getResources()
+ {
+ return context.getResources();
+ }
+
+ public static String getString(int resId)
+ {
+ return context.getString(resId);
+ }
+
+ public static String getString(int resId, Object... formatArgs)
+ {
+ return context.getString(resId, formatArgs);
+ }
+
+ public static File getFilesDir()
+ {
+ return context.getFilesDir();
+ }
+
+ public static String getPackageName()
+ {
+ return context.getPackageName();
+ }
+
+ public static int getColor(int res)
+ {
+ return getResources().getColor(res);
+ }
+
+ // 这个接口放在这里欠考虑
+ public static DisplayMetrics getDisplayMetrics()
+ {
+ return getResources().getDisplayMetrics();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LogTag.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LogTag.java
new file mode 100644
index 0000000..350fe10
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LogTag.java
@@ -0,0 +1,55 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+/**
+ * This class is about log tag.
+ * 日志标签类
+ */
+public class LogTag {
+
+ String getExtraInfo() {
+ StringBuilder stringBuilder = new StringBuilder();
+ int i = getStackTraceElementIndex();
+ StackTraceElement ste = Thread.currentThread().getStackTrace()[i];
+ stringBuilder.append(getClassName(ste.getClassName())).append(".");
+ stringBuilder.append(ste.getMethodName()).append("(");
+ stringBuilder.append(ste.getFileName()).append(":");
+ stringBuilder.append(ste.getLineNumber()).append(")");
+ return stringBuilder.toString();
+ }
+
+ public String getSimpleExtraInfo() {
+ StringBuilder stringBuilder = new StringBuilder();
+ int i = getStackTraceElementIndex();
+ StackTraceElement ste = Thread.currentThread().getStackTrace()[i];
+ stringBuilder.append("(");
+ stringBuilder.append(ste.getFileName()).append(":");
+ stringBuilder.append(ste.getLineNumber()).append(")");
+ return stringBuilder.toString();
+ }
+
+ private static String getClassName(String className) {
+ int pos = className.lastIndexOf(46);
+ if (pos == -1) {
+ return className;
+ } else {
+ className = className.substring(pos + 1);
+ pos = className.lastIndexOf(36);
+ return pos == -1 ? className : className.substring(pos + 1);
+ }
+ }
+
+ private int getStackTraceElementIndex() {
+ StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+
+ int i;
+ for (i = 0; i < stack.length; ++i) {
+ String cls = stack[i].getClassName();
+ if (cls.equals(this.getClass().getName())) {
+ i += 2;
+ break;
+ }
+ }
+
+ return i;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LogUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LogUtil.java
new file mode 100644
index 0000000..cf3b402
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/LogUtil.java
@@ -0,0 +1,573 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.os.Environment;
+import android.util.Log;
+
+import com.google.gson.Gson;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * This class is about The type Tup log util.
+ * Record tup log,Written in the SD card file.
+ * LogUtil日志类
+ * 记录tup日志, 写入 SD 卡文件
+ */
+public final class LogUtil {
+
+ //日志超时删除天数
+ public static final int LogTimeOut = 3;
+ public static final String CHARSET_UTF_8 = "UTF-8";
+ public static final String CLOUNDLINK = "HWCloudLink";
+ public static final String CLOUNDLINK_LOG_FILE_NAME = "HWCloudLink.log";
+ private static final String TAG = "CloudLink";
+ private static String format = "yyyy-MM-dd HH:mm:ss.SSS";
+ public static double logFileSize = 1024.00 * 1024.00 * 5;
+ private static String logPath = CLOUNDLINK + "/App";
+ public static long LogFileMaxSize = 1024 * 1024 * 20;
+ private static boolean isLog = true;
+ private static String FileLogTitle = "[app]" + " [" + CLOUNDLINK + "] ";
+ private static final int SIZE = 6;
+ private static final String SYMBOL = "*";
+ private static final LogTag defaultInstance = new LogTag();
+ private static Gson gson = new Gson();
+
+ private LogUtil() {
+ }
+
+ static long fileSize = 0;
+
+ public static void initLogFiles() {
+ File fileDir = new File(BaseAppContext.getInstance().getFilesDir() + "/" + CLOUNDLINK + "/App/" + TimeUtils.getToday2() + "/");
+ if (!fileDir.exists()) {
+ fileDir.mkdirs();
+ }
+ new LogUtil().changePrmeris();
+// File ecSDKDemo = new File(Environment.getExternalStorageDirectory() + "/ECSDKDemo");
+// if (!ecSDKDemo.exists()) {
+// ecSDKDemo.mkdirs();
+// }
+ checkLogDirs(fileDir);
+ if (fileSize > LogFileMaxSize) {
+ delLogDirs(fileDir);
+ }
+ }
+
+ private void changePrmeris() {
+ String permission = "700";
+ String path = BaseAppContext.getInstance().getFilesDir().getAbsolutePath();
+ try {
+ String command = "chmod " + permission + " " + path;
+ Runtime runtime = Runtime.getRuntime();
+ Process exec = runtime.exec(command);
+ int i = exec.waitFor();
+ if (i == 0) {
+ Log.e("chmod", "chmod: Success");
+ } else {
+ Log.e("chmod", "chmod: failed");
+ }
+ } catch (Exception e) {
+ Log.e("chmod", "chmod: Exception" + e.getMessage());
+ }
+ }
+
+ /**
+ * 删除
+ */
+ private static void delLogDirs(File file) {
+ if (file != null) {
+ if (file.isFile()) {
+ file.delete();
+ } else {
+ //文件夹
+ File[] fileList = file.listFiles();
+ for (File f :
+ fileList) {
+ delLogDirs(f);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * 计算大小和删除超时的
+ */
+ private static void checkLogDirs(File file) {
+ if (file != null) {
+ if (file.isFile()) {
+ boolean isTimeOut = (System.currentTimeMillis() - file.lastModified()) < (1000 * 60 * 60 * 24 * LogTimeOut);
+ if (isTimeOut) {
+ fileSize += file.length();
+ } else {
+ file.delete();
+ }
+
+ } else {
+ //文件夹
+ File[] fileList = file.listFiles();
+ if (fileList != null) {
+ for (File f :
+ fileList) {
+ checkLogDirs(f);
+ }
+ }
+ }
+ }
+
+ }
+
+
+ public static LogTag getInstance() {
+ return defaultInstance;
+ }
+
+
+ /**
+ * This method is used to log debug.
+ * debug日志级别
+ *
+ * @param msg the msg
+ * 消息内容
+ */
+ public static void d(String msg) {
+ if (isLog) {
+ writeLog("debug" + "-" + msg);
+ Log.d(TAG, " " + msg);
+ }
+ }
+
+ /**
+ * This method is used to log debug.
+ * debug日志级别
+ *
+ * @param tag the tag
+ * 日志标签
+ * @param msg the msg
+ * 消息内容
+ */
+ public static void d(String tag, String msg) {
+ if (isLog) {
+ Log.d(TAG, tag + " " + msg);
+ writeLog("debug" + "-" + getTagName(tag) + " : " + msg);
+ }
+ }
+
+ /**
+ * This method is used to log info.
+ * info日志级别
+ *
+ * @param tag the tag
+ * 日志标签
+ * @param msg the msg
+ * 消息内容
+ */
+ public static void i(String tag, String msg) {
+ if (isLog) {
+ Log.i(TAG, tag + ":" + getInstance().getExtraInfo() + " " + msg);
+ showLogAndSaveFile(msg);
+ }
+ }
+
+ /**
+ * This method is used to log error.
+ * error日志级别
+ *
+ * @param tag the tag
+ * 日志标签
+ * @param msg the msg
+ * 消息内容
+ */
+ public static void e(String tag, String msg) {
+ if (isLog) {
+ Log.e(TAG, tag + " " + msg);
+ writeLog("error" + "-" + getTagName(tag) + " : " + msg);
+ }
+
+ }
+
+ private static String getTagName(String tag) {
+ return tag == null ? CLOUNDLINK : tag;
+ }
+
+ private static void writeLog(String logText) {
+ if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+ return;
+ }
+ if (logText.length() > logFileSize) {
+ return;
+ }
+
+ String nowTimeStr = String.format("[%s]", new SimpleDateFormat(format).format(new Date()));
+ String toLogStr = nowTimeStr + " " + logText;
+ toLogStr += "\r\n";
+
+ FileOutputStream fileOutputStream = null;
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ String formatStr = dateFormat.format(new Date());
+ String logFile;
+ if (BaseAppContext.getInstance() == null) {
+ return;
+ } else {
+ logFile = BaseAppContext.getInstance().getFilesDir() + "/" + CLOUNDLINK + "/App/" + formatStr + "";
+ }
+ String filename = formatStr + "_" + CLOUNDLINK_LOG_FILE_NAME;
+ try {
+
+ File fileOld = new File(logFile + "/" + filename);
+ if ((float) ((fileOld.length() + logText.length()) / 1024.00) > logFileSize) {
+ File bakFile = new File(fileOld.getPath() + ".bak");
+ if (bakFile.exists()) {
+ if (bakFile.delete()) {
+ Log.d("Write Log", "delete " + bakFile.getName());
+ }
+ }
+ if (fileOld.renameTo(bakFile)) {
+ Log.d("Write Log", fileOld.getName() + " rename to " + bakFile.getName());
+ }
+ }
+
+ File file = new File(logFile);
+ if (!file.exists()) {
+ if (file.mkdir()) {
+ Log.d("Write Log", "create " + file.getName());
+ }
+ }
+
+ File filepath = new File(logFile + "/" + filename);
+ if (!filepath.exists()) {
+ if (filepath.createNewFile()) {
+ Log.d("Write Log", "create " + filepath.getName());
+ }
+ }
+ fileOutputStream = new FileOutputStream(filepath, true);
+
+ byte[] buffer = toLogStr.getBytes(StandardCharsets.UTF_8);
+
+ fileOutputStream.write(buffer);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, e.getMessage());
+ } catch (IOException e) {
+ Log.e(TAG, e.getMessage());
+ } finally {
+ if (fileOutputStream != null) {
+ try {
+ fileOutputStream.close();
+ } catch (IOException e) {
+ Log.e(TAG, LogUtil.encodeForLog(e.getMessage()));
+ }
+ }
+ }
+ }
+
+ public static String encodeForLog(Object obj) {
+ if (obj == null) {
+ return "null";
+ }
+ String msg = obj.toString();
+ int length = msg.length();
+ StringBuilder sb = new StringBuilder(length);
+ for (int i = 0; i < length; i++) {
+ char ch = msg.charAt(i);
+ // Replace \ r \ n with ' _ '
+ if (ch == '\r' || ch == '\n') {
+ ch = '_';
+ }
+ sb.append(Character.valueOf(ch));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 自用
+ */
+ public static void zzz(String s) {
+ if (isLog) {
+ String logInfo = s + " " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * 自用
+ */
+ public static void zzz(String mName, String s) {
+ if (isLog) {
+ String logInfo = "[" + mName + "] " + s + " " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * 用户操作
+ */
+ public static void userAction(String s) {
+ if (isLog) {
+ String logInfo = "[用户操作] [" + s + "] " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * UI操作
+ */
+ public static void UIAction(String s) {
+ if (isLog) {
+ String logInfo = "[界面绘制] [" + s + "] " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * 调试
+ */
+ public static void debug(String s) {
+ if (isLog) {
+ String logInfo = "[debug] [" + s + "] " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * 自用
+ */
+ public static void zzz(String mName, Object... objects) {
+ if (isLog) {
+ StringBuilder info = new StringBuilder();
+ for (Object obj :
+ objects) {
+ info.append(toJson(obj));
+ }
+ String logInfo = "[" + mName + "] " + info.toString() + " " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+
+ /**
+ * 自用
+ */
+ public static void zzz(String mName, String tips, Object... objects) {
+ if (isLog) {
+ StringBuilder info = new StringBuilder();
+ for (Object obj :
+ objects) {
+ if (info.length() != 0) {
+ info.append(",");
+ }
+ if (obj instanceof String) {
+ info.append(obj);
+ } else {
+ info.append(toJson(obj));
+ }
+ }
+ String logInfo = "[" + mName + "] [" + tips + "] " + info.toString() + " " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * 自用,只记录日志,不控制台打印
+ */
+ public static void zzzOnlySaveFile(String mName, String tips, Object... objects) {
+ if (isLog) {
+ StringBuilder info = new StringBuilder();
+ for (Object obj :
+ objects) {
+ info.append(toJson(obj));
+ }
+ writeLog("[app]" + " [" + getTagName(CLOUNDLINK) + "] [" + mName + "] [" + tips + "] " + info.toString() + getInstance().getExtraInfo());
+ }
+ }
+
+ /**
+ * 自用,只控制台打印,不记录日志
+ */
+ public static void zzzOnlyShowLogcat(String mName, String tips, Object... objects) {
+ if (isLog) {
+ StringBuilder info = new StringBuilder();
+ for (Object obj :
+ objects) {
+ info.append(toJson(obj));
+ }
+ Log.i("qqq", "[" + mName + "] [" + tips + "] " + info.toString() + " " + getInstance().getSimpleExtraInfo());
+ }
+ }
+
+ /**
+ * 自用
+ */
+ public static void zzz(String mName, String tips, int result) {
+ if (isLog) {
+ String logInfo = "[" + mName + "] [" + tips + "] result=" + result + " " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+ /**
+ * 调用SDK的API
+ */
+ public static void API(String apiName, int result) {
+ if (isLog) {
+ String logInfo = "[SDK-API] [" + apiName + "] result=" + result + " " + getInstance().getSimpleExtraInfo();
+ showLogAndSaveFile(logInfo);
+ }
+ }
+
+
+ /**
+ * 打印并且保存到文件
+ */
+ public static void showLogAndSaveFile(String logInfo) {
+ if (isLog) {
+ Log.i("qqq", logInfo);
+ SingleLineUtil.getInstance().getSingle().execute(() -> writeLog(FileLogTitle + logInfo));
+ }
+ }
+
+ /**
+ * 文本间隔
+ */
+ public static String StringInterval(String value) {
+ return "[" + value + "]";
+ }
+
+ /**
+ * 敏感信息脱敏
+ */
+ public static String commonDisplay(String value) {
+ if (null == value || "".equals(value)) {
+ return "";
+ }
+ StringBuilder stringBuilder = new StringBuilder();
+ if (value.length() == 1) {
+ stringBuilder.append(value);
+ } else if (value.length() == 2) {
+ stringBuilder.append(value.charAt(0));
+ stringBuilder.append("*");
+ } else {
+ stringBuilder.append(value.charAt(0));
+ stringBuilder.append("***");
+ stringBuilder.append(value.charAt(value.length() - 1));
+ }
+
+ return stringBuilder.toString();
+
+// if (null == value || "".equals(value)) {
+// return "";
+// }
+// int len = value.length();
+// int pamaone = len / 2;
+// int pamatwo = pamaone - 1;
+// int pamathree = len % 2;
+// StringBuilder stringBuilder = new StringBuilder();
+// if (len <= 2) {
+// if (pamathree == 1) {
+// return SYMBOL;
+// }
+// stringBuilder.append(SYMBOL);
+// stringBuilder.append(value.charAt(len - 1));
+// } else {
+// if (pamatwo <= 0) {
+// stringBuilder.append(value.substring(0, 1));
+// stringBuilder.append(SYMBOL);
+// stringBuilder.append(value.substring(len - 1, len));
+//
+// } else if (pamatwo >= SIZE / 2 && SIZE + 1 != len) {
+// int pamafive = (len - SIZE) / 2;
+// stringBuilder.append(value.substring(0, pamafive));
+// for (int i = 0; i < SIZE; i++) {
+// stringBuilder.append(SYMBOL);
+// }
+// stringBuilder.append(value.substring(len - (pamafive + 1), len));
+// } else {
+// int pamafour = len - 2;
+// stringBuilder.append(value.substring(0, 1));
+// for (int i = 0; i < pamafour; i++) {
+// stringBuilder.append(SYMBOL);
+// }
+// stringBuilder.append(value.substring(len - 1, len));
+// }
+// }
+// return stringBuilder.toString();
+ }
+
+ /**
+ * 得到一个新的对象,用来修改敏感数据
+ */
+ public static T getNewObject(Object obj, Class c) {
+ String json = toJson(obj);
+ return toBean(json, c);
+ }
+
+ /**
+ * toString 方法
+ */
+ public static String toString(Object obj) {
+ StringBuilder sb = new StringBuilder();
+ Class> aClass = obj.getClass();
+ Field[] fields = aClass.getDeclaredFields();
+ sb.append("{");
+ for (int i = 0; i < fields.length; i++) {
+ Field f = fields[i];
+ f.setAccessible(true);
+ if (i != 0) {
+ sb.append(",");
+ }
+ try {
+ sb.append("\"").append(f.getName()).append("\"=\"").append(f.get(obj)).append("\"");
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ static long tempTime = 0;
+
+ /**
+ * 计时方法
+ */
+ public static void startTime(String... Strings) {
+ tempTime = System.currentTimeMillis();
+ StringBuilder str = new StringBuilder();
+ str.append(StringInterval("工具类 "));
+ str.append(StringInterval("计时开始 "));
+ for (String s :
+ Strings) {
+ str.append(StringInterval(s + " "));
+ }
+ str.append(getInstance().getSimpleExtraInfo());
+ showLogAndSaveFile(str.toString());
+ }
+
+ public static void endTime(String... Strings) {
+ StringBuilder str = new StringBuilder();
+ str.append(StringInterval("工具类 "));
+ str.append(StringInterval("计时结束 "));
+ for (String s :
+ Strings) {
+ str.append(StringInterval(s + " "));
+ }
+ str.append("耗时:").append(System.currentTimeMillis() - tempTime).append("ms");
+ str.append(getInstance().getSimpleExtraInfo());
+ showLogAndSaveFile(str.toString());
+ }
+
+ public static String toJson(Object obj) {
+ return gson.toJson(obj);
+ }
+
+ public static T toBean(String json, Class c) {
+ return gson.fromJson(json, c);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MYSPUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MYSPUtils.java
new file mode 100644
index 0000000..31d851f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MYSPUtils.java
@@ -0,0 +1,263 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Base64;
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.StreamCorruptedException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+public class MYSPUtils {
+ /**
+ * 保存在手机里面的文件名
+ */
+ public static final String FILE_NAME = "S_P";
+ // user id
+ public static final String USER_ID = "userId";
+ //
+ public static final String IS_FIRST_IN = "ISFIRSTIN";
+ // 所有的的 种类
+ public static String ALL_CATEGORYS = "ALL_CATEGORYS";
+
+
+ /**
+ * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
+ *
+ * @param context
+ * @param key
+ * @param object
+ */
+ static Context context = BaseAppContext.getInstance();
+
+ public static void put(String key, Object object) {
+
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+
+ if (object instanceof String) {
+ editor.putString(key, (String) object);
+ } else if (object instanceof Integer) {
+ editor.putInt(key, (Integer) object);
+ } else if (object instanceof Boolean) {
+ editor.putBoolean(key, (Boolean) object);
+ } else if (object instanceof Float) {
+ editor.putFloat(key, (Float) object);
+ } else if (object instanceof Long) {
+ editor.putLong(key, (Long) object);
+ } else {
+ editor.putString(key, object.toString());
+ }
+
+ SharedPreferencesCompat.apply(editor);
+ }
+
+ /**
+ * 保存数据的方法,同步写入方法,需拿到保存后状态进行进一步操作
+ *
+ * @param key
+ * @param object
+ */
+ public static boolean putAndCommit(String key, Object object) {
+
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+
+ if (object instanceof String) {
+ editor.putString(key, (String) object);
+ } else if (object instanceof Integer) {
+ editor.putInt(key, (Integer) object);
+ } else if (object instanceof Boolean) {
+ editor.putBoolean(key, (Boolean) object);
+ } else if (object instanceof Float) {
+ editor.putFloat(key, (Float) object);
+ } else if (object instanceof Long) {
+ editor.putLong(key, (Long) object);
+ } else {
+ editor.putString(key, object.toString());
+ }
+
+ return editor.commit();
+ }
+
+ /**
+ * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
+ *
+ * @param key
+ * @param defaultObject
+ * @retur n
+ */
+ public static Object get(String key, Object defaultObject) {
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+
+ if (defaultObject instanceof String) {
+ return sp.getString(key, (String) defaultObject);
+ } else if (defaultObject instanceof Integer) {
+ return sp.getInt(key, (Integer) defaultObject);
+ } else if (defaultObject instanceof Boolean) {
+ return sp.getBoolean(key, (Boolean) defaultObject);
+ } else if (defaultObject instanceof Float) {
+ return sp.getFloat(key, (Float) defaultObject);
+ } else if (defaultObject instanceof Long) {
+ return sp.getLong(key, (Long) defaultObject);
+ }
+
+ return null;
+ }
+
+ /**
+ * 移除某个key值已经对应的值
+ */
+ public static void remove(String key) {
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.remove(key);
+ SharedPreferencesCompat.apply(editor);
+ }
+
+ /**
+ * 清除所有数据
+ */
+ public static void clear() {
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.clear();
+ SharedPreferencesCompat.apply(editor);
+ }
+
+
+ public static void setObject(String key, Object object) {
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = null;
+ try {
+ out = new ObjectOutputStream(baos);
+ out.writeObject(object);
+ String objectVal = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putString(key, objectVal);
+ editor.commit();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (baos != null) {
+ baos.close();
+ }
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T getObject(String key, Class clazz) {
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+
+ if (sp.contains(key)) {
+ String objectVal = sp.getString(key, null);
+ byte[] buffer = Base64.decode(objectVal, Base64.DEFAULT);
+ ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
+ ObjectInputStream ois = null;
+ try {
+ ois = new ObjectInputStream(bais);
+ T t = (T) ois.readObject();
+ return t;
+ } catch (StreamCorruptedException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ if (bais != null) {
+ bais.close();
+ }
+ if (ois != null) {
+ ois.close();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * 返回所有的键值对
+ *
+ * @return
+ */
+ public static Map getAll() {
+ SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
+ Context.MODE_PRIVATE);
+ return sp.getAll();
+ }
+
+
+ /**
+ * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
+ *
+ * @author zhy
+ */
+ private static class SharedPreferencesCompat {
+ private static final Method sApplyMethod = findApplyMethod();
+
+ /**
+ * 反射查找apply的方法
+ *
+ * @return
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static Method findApplyMethod() {
+ try {
+ Class clz = SharedPreferences.Editor.class;
+ return clz.getMethod("apply");
+ } catch (NoSuchMethodException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * 如果找到则使用apply执行,否则使用commit
+ *
+ * @param editor
+ */
+ public static void apply(SharedPreferences.Editor editor) {
+ try {
+ if (sApplyMethod != null) {
+ sApplyMethod.invoke(editor);
+ return;
+ }
+ } catch (IllegalArgumentException e) {
+ } catch (IllegalAccessException e) {
+ } catch (InvocationTargetException e) {
+ }
+ editor.commit();
+ }
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MathUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MathUtil.java
new file mode 100644
index 0000000..5116c0e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MathUtil.java
@@ -0,0 +1,34 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/12 11:49
+ */
+public class MathUtil {
+
+ /**
+ * 10进制转16进制
+ */
+ public static String TenToHex(int numb) {
+ return Integer.toHexString(numb);
+ }
+
+ /**
+ * 得到6位随机整数
+ */
+ public static String getRandom6Int() {
+ StringBuilder num = new StringBuilder();
+ for (int i = 0; i < 6; i++) {
+ int n = (int) (Math.random() * 10);
+ num.append(n);
+ }
+ return num.toString();
+ }
+
+ /**
+ * 得到1位随机整数
+ */
+ public static int getRandomOne() {
+ return (int) (Math.random() * 10);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MediaUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MediaUtil.java
new file mode 100644
index 0000000..9f2902e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MediaUtil.java
@@ -0,0 +1,217 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Vibrator;
+import android.util.Log;
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import androidx.annotation.RequiresApi;
+
+import static android.content.Context.VIBRATOR_SERVICE;
+
+/**
+ * 媒体播放工具
+ *
+ * @author Jackson
+ */
+public class MediaUtil {
+
+ private static final String TAG = "MediaUtil";
+
+ private MediaPlayer player;
+ private EventListener eventListener;
+ private Vibrator vibrator;
+ private AudioManager mAudioManager;
+ /**
+ * 震动标记位
+ */
+ private boolean isVibrating = true;
+
+ private boolean isReceivingRing = true;
+
+ private MediaUtil() {
+ player = new MediaPlayer();
+ mAudioManager = (AudioManager) BaseAppContext.getInstance().getSystemService(Context.AUDIO_SERVICE);
+ vibrator = (Vibrator) BaseAppContext.getInstance().getSystemService(VIBRATOR_SERVICE);
+ }
+
+ AudioManager.OnAudioFocusChangeListener afChangeListener = new AudioManager.OnAudioFocusChangeListener() {
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ // Pause playback
+ LogUtils.d(TAG, " AudioManager.AUDIOFOCUS_LOSS_TRANSIENT");
+ } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ // Resume playback
+ LogUtils.d(TAG, " AudioManager.AUDIOFOCUS_GAIN");
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
+ // mAm.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
+ LogUtils.d(TAG, " AudioManager.AUDIOFOCUS_LOSS");
+ mAudioManager.abandonAudioFocus(afChangeListener);
+ // Stop playback
+ }
+ }
+ };
+
+ private void resume() {
+ if (player != null) {
+ player.start();
+ }
+ }
+
+ private void pause() {
+ if (player != null && player.isPlaying()) {
+ player.pause();
+ }
+ }
+
+ MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer player) {
+ if (!player.isLooping()) {
+ mAudioManager.abandonAudioFocus(afChangeListener);
+ }
+ }
+ };
+
+ public boolean requestFocus() {
+ // Request audio focus for playback
+ int result = mAudioManager.requestAudioFocus(afChangeListener,
+ // Use the music stream.
+ AudioManager.STREAM_VOICE_CALL,
+ // Request permanent focus.
+ AudioManager.AUDIOFOCUS_GAIN);
+ return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+ }
+
+ public void abandonAudioFocus() {
+ mAudioManager.abandonAudioFocus(afChangeListener);
+ }
+
+
+ private volatile static MediaUtil INSTANCE;
+
+ public static MediaUtil getInstance() {
+ if (null == INSTANCE) {
+ synchronized (MediaUtil.class) {
+ if (null == INSTANCE) {
+ INSTANCE = new MediaUtil();
+ }
+ }
+ }
+ return INSTANCE;
+ }
+
+ public void play(String fileName) {
+ long[] pattern = {1000, 1000, 1000, 1000};
+ if (isVibrating()) {
+ vibrator.vibrate(pattern, 0);
+ }
+ AudioAttributes abs = new AudioAttributes.Builder()
+ .setLegacyStreamType(AudioManager.STREAM_RING)
+ .build();
+ try {
+ if (isReceivingRing()) {
+ if (eventListener != null) {
+ eventListener.onStop();
+ }
+ player.reset();
+ player.setAudioAttributes(abs);
+ player.setDataSource(fileName);
+ player.setLooping(true);
+ player.prepare();
+ player.start();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "play error:" + e);
+ }
+ }
+
+ public MediaPlayer getPlayer() {
+ return player;
+ }
+
+
+ public void setEventListener(final EventListener eventListener) {
+ if (player != null) {
+ player.setOnCompletionListener(mp -> eventListener.onStop());
+ }
+ this.eventListener = eventListener;
+ }
+
+
+ public void stop() {
+ if (player != null && player.isPlaying()) {
+ player.stop();
+ }
+ if (isVibrating()) {
+ setVibrator(false);
+ vibrator.cancel();
+ }
+ }
+
+ public long getDuration(String path) {
+ player = MediaPlayer.create(BaseAppContext.getInstance(), Uri.parse(path));
+ return player.getDuration();
+ }
+
+ /**
+ * 根据系统的情景模式
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public void showSystemDefaultRing() {
+ // 获取手机情景模式
+ AudioManager audioManager = (AudioManager) BaseAppContext.getInstance().getSystemService(Context.AUDIO_SERVICE);
+ int ringerMode = audioManager.getRingerMode();
+ switch (ringerMode) {
+ case AudioManager.RINGER_MODE_NORMAL:
+ // 标准模式(铃声和震动)
+ setVibrator(EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + Constant.IS_VIBRATION, true));
+ setReceivingRing(EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + Constant.IS_RECEIVING_RING, true));
+ break;
+ case AudioManager.RINGER_MODE_SILENT:
+ // 静音模式
+ setVibrator(false);
+ setReceivingRing(false);
+ break;
+ case AudioManager.RINGER_MODE_VIBRATE:
+ // 震动模式【开启震动不要忘记系统震动权限】
+ setVibrator(EncryptedSPTool.getBoolean(EncryptedSPTool.getString(Constant.LOGIN_ACCOUNT) + Constant.IS_VIBRATION, true));
+ setReceivingRing(false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public boolean isReceivingRing() {
+ return isReceivingRing;
+ }
+
+ public void setReceivingRing(boolean receivingRing) {
+ isReceivingRing = receivingRing;
+ }
+
+ public boolean isVibrating() {
+ return isVibrating;
+ }
+
+ public void setVibrator(boolean vibrator) {
+ isVibrating = vibrator;
+ }
+
+ /**
+ * 播放器事件监听
+ */
+ public interface EventListener {
+ void onStop();
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MeizuUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MeizuUtils.java
new file mode 100644
index 0000000..cac2bc1
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MeizuUtils.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
+ */
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Build;
+import android.util.Log;
+
+
+import java.lang.reflect.Method;
+
+public class MeizuUtils {
+ private static final String TAG = "MeizuUtils";
+
+ /**
+ * 检测 meizu 悬浮窗权限
+ */
+ public static boolean checkFloatWindowPermission(Context context) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
+ }
+ return true;
+ }
+
+ /**
+ * 去魅族权限申请页面
+ */
+ public static void applyPermission(Context context) {
+ try {
+ Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
+// intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity");//remove this line code for fix flyme6.3
+ intent.setPackage(context.getPackageName());
+ intent.putExtra("packageName", context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+ } catch (Exception e) {
+ try {
+ Log.e(TAG, "获取悬浮窗权限, 打开AppSecActivity失败, " + Log.getStackTraceString(e));
+ // 最新的魅族flyme 6.2.5 用上述方法获取权限失败, 不过又可以用下述方法获取权限了
+ RomUtils.commonROMPermissionApplyInternal(context);
+ } catch (Exception eFinal) {
+ Log.e(TAG, "获取悬浮窗权限失败, 通用获取方法失败, " + Log.getStackTraceString(eFinal));
+ }
+ }
+
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static boolean checkOp(Context context, int op) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ try {
+ Class clazz = AppOpsManager.class;
+ Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
+ return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ } else {
+ Log.e(TAG, "Below API 19 cannot invoke!");
+ }
+ return false;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MiuiUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MiuiUtils.java
new file mode 100644
index 0000000..cb305e3
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MiuiUtils.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
+ */
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.provider.Settings;
+import android.util.Log;
+
+
+import java.lang.reflect.Method;
+
+public class MiuiUtils {
+ private static final String TAG = "MiuiUtils";
+
+ /**
+ * 获取小米 rom 版本号,获取失败返回 -1
+ *
+ * @return miui rom version code, if fail , return -1
+ */
+ public static int getMiuiVersion() {
+ String version = RomUtils.getSystemProperty("ro.miui.ui.version.name");
+ if (version != null) {
+ try {
+ return Integer.parseInt(version.substring(1));
+ } catch (Exception e) {
+ Log.e(TAG, "get miui version code error, version : " + version);
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * 检测 miui 悬浮窗权限
+ */
+ public static boolean checkFloatWindowPermission(Context context) {
+ final int version = Build.VERSION.SDK_INT;
+
+ if (version >= 19) {
+ return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
+ } else {
+// if ((context.getApplicationInfo().flags & 1 << 27) == 1) {
+// return true;
+// } else {
+// return false;
+// }
+ return true;
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static boolean checkOp(Context context, int op) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ try {
+ Class clazz = AppOpsManager.class;
+ Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
+ return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ } else {
+ Log.e(TAG, "Below API 19 cannot invoke!");
+ }
+ return false;
+ }
+
+ /**
+ * 小米 ROM 权限申请
+ */
+ public static void applyMiuiPermission(Context context) {
+ int versionCode = getMiuiVersion();
+ if (versionCode == 5) {
+ goToMiuiPermissionActivity_V5(context);
+ } else if (versionCode == 6) {
+ goToMiuiPermissionActivity_V6(context);
+ } else if (versionCode == 7) {
+ goToMiuiPermissionActivity_V7(context);
+ } else if (versionCode == 8) {
+ goToMiuiPermissionActivity_V8(context);
+ } else {
+ Log.e(TAG, "this is a special MIUI rom version, its version code " + versionCode);
+ }
+ }
+
+ private static boolean isIntentAvailable(Intent intent, Context context) {
+ if (intent == null) {
+ return false;
+ }
+ return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ }
+
+ /**
+ * 小米 V5 版本 ROM权限申请
+ */
+ public static void goToMiuiPermissionActivity_V5(Context context) {
+ Intent intent = null;
+ String packageName = context.getPackageName();
+ intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", packageName, null);
+ intent.setData(uri);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ Log.e(TAG, "intent is not available!");
+ }
+
+ //设置页面在应用详情页面
+// Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+// PackageInfo pInfo = null;
+// try {
+// pInfo = context.getPackageManager().getPackageInfo
+// (HostInterfaceManager.getHostInterface().getApp().getPackageName(), 0);
+// } catch (PackageManager.NameNotFoundException e) {
+// AVLogUtils.e(TAG, e.getMessage());
+// }
+// intent.setClassName("com.android.settings", "com.miui.securitycenter.permission.AppPermissionsEditor");
+// intent.putExtra("extra_package_uid", pInfo.applicationInfo.uid);
+// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+// if (isIntentAvailable(intent, context)) {
+// context.startActivity(intent);
+// } else {
+// AVLogUtils.e(TAG, "Intent is not available!");
+// }
+ }
+
+ /**
+ * 小米 V6 版本 ROM权限申请
+ */
+ public static void goToMiuiPermissionActivity_V6(Context context) {
+ Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
+ intent.putExtra("extra_pkgname", context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ Log.e(TAG, "Intent is not available!");
+ }
+ }
+
+ /**
+ * 小米 V7 版本 ROM权限申请
+ */
+ public static void goToMiuiPermissionActivity_V7(Context context) {
+ Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
+ intent.putExtra("extra_pkgname", context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ Log.e(TAG, "Intent is not available!");
+ }
+ }
+
+ /**
+ * 小米 V8 版本 ROM权限申请
+ */
+ public static void goToMiuiPermissionActivity_V8(Context context) {
+ Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
+// intent.setPackage("com.miui.securitycenter");
+ intent.putExtra("extra_pkgname", context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ intent.setPackage("com.miui.securitycenter");
+ intent.putExtra("extra_pkgname", context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ Log.e(TAG, "Intent is not available!");
+ }
+ }
+ }
+
+ public static void toPermisstionSetting(Context context) throws NoSuchFieldException, IllegalAccessException {
+ int rom = getMiuiVersion();
+ Intent intent = null;
+ if (5 == rom) {
+ Uri packageURI = Uri.parse("package:" + context.getApplicationInfo().packageName);
+ intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, packageURI);
+ } else if (rom == 6 || rom == 7) {
+ intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
+ intent.putExtra("extra_pkgname", context.getPackageName());
+ } else if (rom == 8 || rom == 9) {
+ intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
+ intent.putExtra("extra_pkgname", context.getPackageName());
+ } else {
+ RomUtils.commonROMPermissionApplyInternal(context);
+ return;
+ }
+ intent.setPackage(context.getPackageName());
+ context.startActivity(intent);
+ }
+
+ /**
+ * 跳转到miui的权限管理页面
+ */
+ public static void gotoMiuiPermission(Context context) {
+ Intent i = new Intent("miui.intent.action.APP_PERM_EDITOR");
+ ComponentName componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
+ i.setComponent(componentName);
+ i.putExtra("extra_pkgname", context.getPackageName());
+ try {
+ context.startActivity(i);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MultiFactory.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MultiFactory.java
new file mode 100644
index 0000000..ed0c871
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MultiFactory.java
@@ -0,0 +1,17 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import com.tengshisoft.chatmodule.hwclud.api.IPermission;
+import com.tengshisoft.chatmodule.hwclud.api.IPermissionFactory;
+
+public class MultiFactory implements IPermissionFactory {
+
+ @Override
+ public IPermission permission() {
+ return new MultiPermission();
+ }
+
+ @Override
+ public IPermission multiPermission(Class>... factories) {
+ return new MultiPermission(factories);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MultiPermission.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MultiPermission.java
new file mode 100644
index 0000000..38b0969
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/MultiPermission.java
@@ -0,0 +1,29 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import com.tengshisoft.chatmodule.hwclud.utils.mic.RecordAudioFactory;
+import com.tengshisoft.chatmodule.hwclud.utils.phone_state.PhoneStateFactory;
+import com.tenlionsoft.baselib.constant.PermissionConstants;
+
+import java.util.ArrayList;
+
+import androidx.camera.core.impl.CameraFactory;
+
+public class MultiPermission extends ImpPermission {
+
+ public MultiPermission(Class>... factories) {
+ code = PermissionConstants.REQUEST_CODE_MULTI;
+ requestPermission = new ArrayList<>();
+ for (Class> clazz : factories)
+ if (clazz == StorageFactory.class) {
+ requestPermission.add(PermissionConstants.READ_EXTERNAL_STORAGE);
+ requestPermission.add(PermissionConstants.WRITE_EXTERNAL_STORAGE);
+ } else if (clazz == RecordAudioFactory.class) {
+ requestPermission.add(PermissionConstants.RECORD_AUDIO);
+ } else if (clazz == CameraFactory.class) {
+ requestPermission.add(PermissionConstants.CAMERA);
+ } else if (clazz == PhoneStateFactory.class) {
+ requestPermission.add(PermissionConstants.READ_PHONE_STATE);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/NetUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/NetUtil.java
new file mode 100644
index 0000000..69d6f62
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/NetUtil.java
@@ -0,0 +1,549 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.huawei.ecterminalsdk.models.conference.TsdkConferenceManager;
+import com.huawei.ecterminalsdk.models.maintain.TsdkMaintainManager;
+import com.tengshisoft.chatmodule.hwclud.api.CallOnResponse;
+import com.tengshisoft.chatmodule.hwclud.api.CancelConfResultCallBack;
+import com.tengshisoft.chatmodule.hwclud.api.CheckVersionCallBack;
+import com.tengshisoft.chatmodule.hwclud.api.LogUpload;
+import com.tengshisoft.chatmodule.hwclud.api.LogoutCallBack;
+import com.tengshisoft.chatmodule.hwclud.api.SearchTimeZoneListCallBack;
+import com.tengshisoft.chatmodule.hwclud.api.SearchUsernameFormID;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.LdapFrontstageMgr;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.lang.reflect.Method;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/19 17:12
+ */
+public class NetUtil {
+ public static CancelConfResultCallBack cancelConfResultCallBack;
+ public static SearchUsernameFormID searchUsernameFormId;
+ public static SearchTimeZoneListCallBack searchTimeZoneListCallBack;
+ public static CheckVersionCallBack checkVersionCallBack;
+ public static LogoutCallBack logoutCallBack;
+ public static LogUpload logUpload;
+ public static CallOnResponse response;
+
+ // 没有连接
+ public static final int NETWORN_NONE = 0;
+
+ // wifi连接
+ public static final int NETWORN_WIFI = 1;
+
+ // 手机网络数据连接
+ public static final int NETWORN_2G = 2;
+
+ public static final int NETWORN_3G = 3;
+
+ public static final int NETWORN_4G = 4;
+
+ public static final int NETWORN_MOBILE = 5;
+
+ public static final int NETWORK_TYPE_NR = 20;
+
+ public static final int SDK_VERSION_Q = 29;
+
+ private static NetUtil netUtil;
+
+ public static int currentNetType = 0;
+
+ private Context context;
+
+ private String ip = "";
+
+ private boolean isConnected = false;
+
+ private NetUtil(Context context) {
+ this.context = context;
+ }
+
+ public static NetUtil getInstance(Context context) {
+ if (netUtil == null) {
+ synchronized (NetUtil.class) {
+ if (netUtil == null) {
+ netUtil = new NetUtil(context);
+ }
+ }
+ }
+ return netUtil;
+ }
+
+ /**
+ * 查询时区列表
+ */
+ public static void searchTimeZoneList() {
+ try {
+ int result = TsdkConferenceManager.getObject().getTimeZoneList(getLocLanguage());
+ LogUtil.zzz("getTimeZoneList", "Call query time zone interface", result);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ //TODO 多语言
+ static int getLocLanguage() {
+ return 0;
+ }
+
+
+ public static void startCallOn(String num, boolean isVideo, String displayName, CallOnResponse response) {
+ NetUtil.response = response;
+ if (NetUtil.response != null) {
+ int i = CallMgrV2.getInstance().startCall(num, isVideo, displayName);
+ if (i == 0) {
+ response.onFailed(i, "呼叫失败");
+ }
+ }
+ }
+
+ /**
+ * 取消会议
+ */
+ public static void cancelConf(String confId, CancelConfResultCallBack cancelConfResultCallBack) {
+ NetUtil.cancelConfResultCallBack = cancelConfResultCallBack;
+ if (NetUtil.cancelConfResultCallBack != null) {
+ if (null != TsdkManager.getInstance()) {
+ int result = TsdkManager.getInstance().getConferenceManager().cancelConference(confId);
+ if (result != 0) {
+ cancelConfResultCallBack.fail(result, "");
+ }
+ }
+ }
+ }
+
+ /**
+ * 日志上传
+ */
+ public static void logUp(String confId, LogUpload logUpload) {
+ NetUtil.logUpload = logUpload;
+ if (NetUtil.logUpload != null) {
+ // 上传日志的接口调用
+ int result = TsdkMaintainManager.getObject().logUpload(confId);
+ if (result != 0) {
+ logUpload.fail(result, "");
+ }
+ }
+ }
+
+ /**
+ * 通过id匹配通讯录姓名
+ *
+ * @param usercode
+ * @param searchUsernameFormId
+ */
+ public static void searchNameFormID(String usercode, SearchUsernameFormID searchUsernameFormId) {
+ NetUtil.searchUsernameFormId = searchUsernameFormId;
+ if (NetUtil.searchUsernameFormId != null) {
+ int result = LdapFrontstageMgr.getInstance().searchLdapContacts(usercode, "", "ou", 0, 0, 0, new ArrayList<>());
+ if (result != 0) {
+ searchUsernameFormId.fail(result, "");
+ }
+ }
+ }
+
+ /**
+ * 检测新版本
+ */
+ public static void checkVersion(CheckVersionCallBack checkVersionCallBack) {
+// int result = -1;
+// NetUtil.checkVersionCallBack = checkVersionCallBack;
+// if (NetUtil.checkVersionCallBack != null) {
+// String serverAddr;
+// // 服务器信息获取
+// serverAddr = EncryptedSPTool.getString(UIUtil.getContext(), Constant.LOGIN_SERVER_ADDRESS);
+// if (!TextUtils.isEmpty(serverAddr)) {
+// // 获取软终端下载版本信息的信息接口调用
+// TsdkServerCfg serverCfg = new TsdkServerCfg();
+// serverCfg.setAcServerAddr(serverAddr);
+// serverCfg.setUlServerPort(0);
+// LogUtil.zzz("升级接口参数", LogUtil.commonDisplay(serverAddr));
+// try {
+// result = TsdkMaintainManager.getObject().getSoftTerminalDownloadInfo(serverCfg);
+// } catch (Exception e) {
+// Log.e("TAG", "checkVersion: " + e.getMessage());
+// }
+// LogUtil.zzz("getSoftTerminalDownloadInfo", "调用版本检测接口", result);
+// if (result != 0) {
+// checkVersionCallBack.fail(result, "check Error");
+// }
+// }
+// }
+ }
+
+ /**
+ * 退出登录
+ */
+ public static void AppLogout(LogoutCallBack logoutCallBack) {
+ NetUtil.logoutCallBack = logoutCallBack;
+ if (NetUtil.logoutCallBack != null) {
+ if (null != TsdkManager.getInstance()) {
+ int result = TsdkManager.getInstance().getLoginManager().logout();
+ LogUtil.zzz("logout", "退出登录", result);
+ if (result != 0) {
+ logoutCallBack.fail(result, "check Error");
+ }
+ } else {
+ logoutCallBack.fail(-1, "check Error");
+ }
+ }
+
+ }
+
+
+ /**
+ * 判断是否是流量
+ */
+ public static boolean is4G() {
+ return checkNetworkConnection() == 2;
+ }
+
+ /**
+ * 判断网络类型
+ * 0 :不可用
+ * 1 :wifi
+ * 2 :移动流量
+ */
+ private static int checkNetworkConnection() {
+ // Whether there is a Wi-Fi connection.
+ boolean wifiConnected = false;
+ // Whether there is a mobile connection.
+ boolean mobileConnected = false;
+ // BEGIN_INCLUDE(connect)
+ ConnectivityManager connMgr =
+ (ConnectivityManager) BaseAppContext.getInstance().getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeInfo = connMgr.getActiveNetworkInfo();
+ if (activeInfo != null && activeInfo.isConnected()) {
+ wifiConnected = activeInfo.getType() == ConnectivityManager.TYPE_WIFI;
+ mobileConnected = activeInfo.getType() == ConnectivityManager.TYPE_MOBILE;
+ if (wifiConnected) {
+ return 1;
+ } else if (mobileConnected) {
+ return 2;
+ }
+ }
+ return 0;
+ }
+
+
+ public static void logSuccess(String name) {
+ LogUtil.zzz(name, "成功");
+ }
+
+ public static void logFail(String name, int result, String msg) {
+ LogUtil.zzz(name, "失败", result);
+ }
+
+ /**
+ * 网络状态改变记录网络信息
+ */
+ public void recordNetworkStateChanges() {
+ int networkState = getNetworkState();
+ LogUtil.zzz("网络变化", "上次: " + getNetWorkType(currentNetType) +
+ " 当前: " + getNetWorkType(networkState));
+ currentNetType = networkState;
+ }
+
+ /**
+ * 登录时记录网络信息
+ */
+ public void loginRecordNetworkStatus() {
+ int networkState = getNetworkState();
+ currentNetType = networkState;
+ LogUtil.zzz("登录", "网络类型", getNetWorkType(networkState));
+ }
+
+ private int getNetworkState() {
+ ConnectivityManager connManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ if (connManager == null) {
+ return NETWORN_NONE;
+ }
+
+ NetworkInfo activeNetInfo = connManager.getActiveNetworkInfo();
+
+ // Wifi网络Info
+ NetworkInfo wifiInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+
+ // 手机网络Info
+ NetworkInfo networkInfo = connManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
+
+
+ // 记录Ip地址
+ getCurrentIp(wifiInfo, networkInfo);
+
+ // 记录网络连接状态变化
+ if (recordNetworkConnecte(activeNetInfo) == NETWORN_NONE) {
+ return NETWORN_NONE;
+ }
+
+ // 记录wifi网络信息
+ if (isWifiConnect(wifiInfo)) {
+ return NETWORN_WIFI;
+ }
+
+ // 记录移动网络信息
+ int mobileNetworkState = getMobileNetworkState(networkInfo, activeNetInfo);
+ if (mobileNetworkState != NETWORN_NONE) {
+ return mobileNetworkState;
+ }
+ return NETWORN_NONE;
+ }
+
+ private int recordNetworkConnecte(NetworkInfo activeNetInfo) {
+ if (activeNetInfo == null || !activeNetInfo.isAvailable() || !activeNetInfo.isConnected()) {
+ boolean lastConnected = isConnected;
+ isConnected = false;
+ LogUtil.zzz("网络连接状态", "lastConnected: " + lastConnected
+ + " currentConnected: " + isConnected);
+ return NETWORN_NONE;
+ }
+
+ boolean lastConnected = isConnected;
+ isConnected = true;
+ LogUtil.zzz("网络连接状态", "lastConnected: " + lastConnected
+ + " currentConnected: " + isConnected);
+ return NETWORN_MOBILE;
+ }
+
+ private String getNetWorkType(int netWorkState) {
+ switch (netWorkState) {
+ case NETWORN_NONE:
+ return "无网络";
+ case NETWORN_WIFI:
+ return "wifi连接";
+ case NETWORN_2G:
+ return "2g网";
+ case NETWORN_3G:
+ return "3g网";
+ case NETWORN_4G:
+ return "4g网";
+ case NETWORN_MOBILE:
+ return "5g网";
+ default:
+ return "";
+ }
+ }
+
+ private String getLocalIpAddress() {
+ try {
+ ArrayList nilist = Collections.list(
+ NetworkInterface.getNetworkInterfaces());
+ for (NetworkInterface ni : nilist) {
+ ArrayList ialist = Collections.list(ni.getInetAddresses());
+ for (InetAddress address : ialist) {
+ if (!address.isLoopbackAddress() && address instanceof Inet4Address) {
+ return address.getHostAddress();
+ }
+ }
+
+ }
+ } catch (SocketException ex) {
+ }
+ return "";
+ }
+
+ private String intToIp(int ipInt) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(ipInt & 0xFF).append(".");
+ sb.append((ipInt >> 8) & 0xFF).append(".");
+ sb.append((ipInt >> 16) & 0xFF).append(".");
+ sb.append((ipInt >> 24) & 0xFF);
+ return sb.toString();
+ }
+
+ private boolean isWifiConnect(NetworkInfo wifiInfo) {
+ if (wifiInfo != null) {
+ NetworkInfo.State state = wifiInfo.getState();
+ if (state != null)
+ if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
+ LogUtil.zzz("WIFI", "网络详情",
+ "wifiInfo.getState(): " + wifiInfo.getState(),
+ " wifiInfo.getSubtype(): " + wifiInfo.getSubtype(),
+ " wifiInfo.getSubtypeName(): " + wifiInfo.getSubtypeName(),
+ " wifiInfo.getType(): " + wifiInfo.getType(),
+ " wifiInfo.getTypeName(): " + wifiInfo.getTypeName());
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private int getMobileNetworkState(NetworkInfo networkInfo, NetworkInfo activeNetInfo) {
+ if (networkInfo != null) {
+ NetworkInfo.State state = networkInfo.getState();
+ String strSubTypeName = networkInfo.getSubtypeName();
+ if (state != null) {
+ if (state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING) {
+ LogUtil.zzz("移动网", "网络详情",
+ "networkInfo.getState(): " + networkInfo.getState(),
+ " networkInfo.getSubtype(): " + networkInfo.getSubtype(),
+ " networkInfo.getSubtypeName(): " + networkInfo.getSubtypeName(),
+ " networkInfo.getType(): " + networkInfo.getType(),
+ " networkInfo.getTypeName(): " + networkInfo.getTypeName());
+ switch (getNetWorkType()) {
+ case TelephonyManager.NETWORK_TYPE_GPRS: // 联通2g
+ case TelephonyManager.NETWORK_TYPE_CDMA: // 电信2g
+ case TelephonyManager.NETWORK_TYPE_EDGE: // 移动2g
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ case TelephonyManager.NETWORK_TYPE_IDEN:
+ return NETWORN_2G;
+ case TelephonyManager.NETWORK_TYPE_EVDO_A: // 电信3g
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ case TelephonyManager.NETWORK_TYPE_EVDO_0:
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ case TelephonyManager.NETWORK_TYPE_EVDO_B:
+ case TelephonyManager.NETWORK_TYPE_EHRPD:
+ case TelephonyManager.NETWORK_TYPE_HSPAP:
+ return NETWORN_3G;
+ case TelephonyManager.NETWORK_TYPE_LTE:
+ return NETWORN_4G;
+ case TelephonyManager.NETWORK_TYPE_NR:
+ LogUtil.zzz("5g网");
+ return NETWORN_MOBILE;
+ default:
+ // 有机型返回16,17
+ //中国移动 联通 电信 三种3G制式
+ if (strSubTypeName.equalsIgnoreCase("TD-SCDMA") ||
+ strSubTypeName.equalsIgnoreCase("WCDMA") ||
+ strSubTypeName.equalsIgnoreCase("CDMA2000")) {
+ return NETWORN_3G;
+ } else {
+ return NETWORN_MOBILE;
+ }
+ }
+ }
+ }
+ }
+ return NETWORN_NONE;
+ }
+
+ private void getCurrentIp(NetworkInfo wifiInfo, NetworkInfo networkInfo) {
+ String lastIpAddress = ip;
+ if (networkInfo != null && networkInfo.isConnected()) {
+ ip = getLocalIpAddress();
+ } else if (wifiInfo != null && wifiInfo.isConnected()) {
+ WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wifi = wifiManager.getConnectionInfo();
+ int ipAddress = wifi.getIpAddress();
+ ip = intToIp(ipAddress);
+ } else {
+ ip = "";
+ }
+ LogUtil.zzz("IP变化", "lastIpAddress: (" + LogUtil.commonDisplay(lastIpAddress) +
+ ") currentIpAddress: (" + LogUtil.commonDisplay(ip) + ")");
+ }
+
+ /**
+ * 获取当前移动网络类型
+ *
+ * @return
+ */
+ @SuppressLint("MissingPermission")
+ private int getNetWorkType() {
+ int networkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ try {
+ int defaultDataSubId = getSubId();
+ if (defaultDataSubId == -1) {
+ networkType = tm.getNetworkType();
+ } else {
+ try {
+ Method dataNetworkType = TelephonyManager.class
+ .getDeclaredMethod("getDataNetworkType", new Class[]{int.class});
+ dataNetworkType.setAccessible(true);
+ networkType = (int) dataNetworkType.invoke(tm, defaultDataSubId);
+ } catch (Throwable t) {
+ networkType = tm.getNetworkType();
+ }
+ }
+ } catch (Throwable t) {
+ // do nothing
+ }
+ if (networkType == TelephonyManager.NETWORK_TYPE_LTE) {
+ networkType = adjustNetworkType(networkType);
+ }
+ return networkType;
+ }
+
+ private int adjustNetworkType(int networkTypeFromSys) {
+ int networkType = networkTypeFromSys;
+ if (Build.VERSION.SDK_INT >= SDK_VERSION_Q
+ && context.checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED) {
+ try {
+ TelephonyManager tm = (TelephonyManager) context
+ .getSystemService(Context.TELEPHONY_SERVICE);
+ ServiceState ss;
+ int defaultDataSubId = getSubId();
+ if (defaultDataSubId == -1) {
+ ss = tm.getServiceState();
+ } else {
+ try {
+ Class infTm = TelephonyManager.class;
+ Method method = infTm
+ .getDeclaredMethod("getServiceStateForSubscriber",
+ new Class[]{int.class});
+ method.setAccessible(true);
+ ss = (ServiceState) method.invoke(tm, defaultDataSubId);
+ } catch (Throwable t) {
+ ss = tm.getServiceState();
+ }
+ }
+ if (ss != null && isServiceStateFiveGAvailable(ss.toString())) {
+ networkType = NETWORK_TYPE_NR;
+ }
+ } catch (Exception e) {
+ // do nothing
+ }
+ }
+ return networkType;
+ }
+
+ private int getSubId() {
+ int defaultDataSubId = -1;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+ }
+ return defaultDataSubId;
+ }
+
+ private boolean isServiceStateFiveGAvailable(String ss) {
+ boolean available = false;
+ if (!TextUtils.isEmpty(ss)
+ && (ss.contains("nrState=NOT_RESTRICTED")
+ || ss.contains("nrState=CONNECTED"))) {
+ available = true;
+ }
+ return available;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/NotifyUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/NotifyUtil.java
new file mode 100644
index 0000000..f01e61e
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/NotifyUtil.java
@@ -0,0 +1,395 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import android.app.AppOpsManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.Settings;
+
+import com.tengshisoft.chatmodule.R;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.PathConfig;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import androidx.annotation.RequiresApi;
+import androidx.core.app.NotificationCompat;
+import androidx.core.app.NotificationManagerCompat;
+
+import static android.content.Context.NOTIFICATION_SERVICE;
+import static com.tengshisoft.chatmodule.hwclud.utils.LocContext.getPackageName;
+import static com.tengshisoft.chatmodule.hwclud.utils.LocContext.getResources;
+
+public class NotifyUtil {
+
+ static String CHANNEL_ID = "111";
+ private static final String CALL_CHANNEL_ID = CHANNEL_ID;
+ private static NotificationManager manager;
+
+ private static NotificationManager getManager(Context context) {
+ if (manager == null) {
+ manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+ return manager;
+ }
+
+ public static boolean isEnable() {
+ return enable;
+ }
+
+ public static void setEnable(boolean enable) {
+ NotifyUtil.enable = enable;
+ }
+
+ public static boolean enable = true;
+
+ public static NotificationCompat.Builder getNotificationBuilder(Context mContext, String title, String content, String channelId) {
+ //大于8.0
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ //id随便指定
+ NotificationChannel channel = new NotificationChannel(channelId, mContext.getPackageName(), NotificationManager.IMPORTANCE_DEFAULT);
+ channel.canBypassDnd();//可否绕过,请勿打扰模式
+ channel.enableLights(true);//闪光
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);//锁屏显示通知
+ channel.setLightColor(Color.RED);//指定闪光是的灯光颜色
+ channel.setShowBadge(false);
+ channel.enableVibration(true);//是否允许震动
+ channel.setSound(null, null);
+ //channel.getAudioAttributes();//获取系统通知响铃声音配置
+ channel.getGroup();//获取通知渠道组
+ channel.setBypassDnd(true);//设置可以绕过,请勿打扰模式
+ channel.setVibrationPattern(new long[]{100, 100, 200});//震动的模式,震3次,第一次100,第二次100,第三次200毫秒
+ channel.shouldShowLights();//是否会闪光
+ //通知管理者创建的渠道
+ getManager(mContext).createNotificationChannel(channel);
+ }
+ return new NotificationCompat.Builder(mContext, channelId).setAutoCancel(true)
+ .setContentTitle(title)
+ .setContentText(content).setSmallIcon(R.mipmap.ic_launcher);
+
+ }
+
+ public static void showNotificationProgressApkDown(Context mContext, int progress) {
+// final NotificationCompat.Builder builder = getNotificationBuilder(mContext, mContext.getString(R.string.cloudLink_mine_downloading) + progress + "%...", "", "222");
+// Intent intent = new Intent(mContext, DownloadActivity.class);
+// PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+// builder.setContentIntent(pi);
+// builder.setOnlyAlertOnce(true);
+// builder.setDefaults(Notification.FLAG_ONLY_ALERT_ONCE);
+// builder.setProgress(100, progress, false);
+// builder.setWhen(System.currentTimeMillis());
+// getManager(mContext).notify(R.drawable.ic_launcher, builder.build());
+ }
+
+ public static void showNotificationProgressApkDown(Context mContext, String info) {
+// final NotificationCompat.Builder builder = getNotificationBuilder(mContext, info, "", "222");
+// Intent intent = new Intent(mContext, DownloadActivity.class);
+// PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+// builder.setContentIntent(pi);
+// builder.setOnlyAlertOnce(true);
+// builder.setDefaults(Notification.FLAG_ONLY_ALERT_ONCE);
+// builder.setProgress(100, 100, false);
+// builder.setWhen(System.currentTimeMillis());
+// getManager(mContext).notify(R.drawable.ic_launcher, builder.build());
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ public static void showCallPointNotify(String name, boolean isConf, boolean isVideo) {
+ getManager(BaseAppContext.getInstance()).deleteNotificationChannel(CALL_CHANNEL_ID);
+ String content;
+ if (isConf) {
+ if (isVideo) {
+ content = BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_wantToVideoMeeting);
+ } else {
+ content = BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_wantToCallMeeting);
+ }
+ } else {
+ if (isVideo) {
+ content = BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_wantToVideo);
+ } else {
+ content = BaseAppContext.getInstance().getString(R.string.cloudLink_meeting_wantToCall);
+ }
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationChannel channel = new NotificationChannel(CALL_CHANNEL_ID, BaseAppContext.getInstance().getPackageName(), NotificationManager.IMPORTANCE_HIGH);
+ channel.setBypassDnd(true);
+ channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ getManager(BaseAppContext.getInstance()).createNotificationChannel(channel);
+ }
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(BaseAppContext.getInstance(), CALL_CHANNEL_ID);
+ Intent intent = new Intent();
+ intent.setAction(PathConfig.ACTION_CALL_MSG);
+ PendingIntent pi = PendingIntent.getActivity(BaseAppContext.getInstance(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ builder.setContentIntent(pi);
+ builder.setContentTitle(name);
+ builder.setContentText(content);
+ builder.setSmallIcon(R.mipmap.ic_launcher);
+ builder.setOnlyAlertOnce(true);
+ builder.setAutoCancel(true);
+ builder.setDefaults(Notification.FLAG_ONLY_ALERT_ONCE);
+ builder.setWhen(System.currentTimeMillis());
+ Notification notification = builder.build();
+ getManager(BaseAppContext.getInstance()).notify(R.drawable.app_logo_smal, notification);
+ String mFilePath = Environment.getExternalStorageDirectory() + File.separator + CallFunc.RINGING_FILE;
+ CallMgrV2.getInstance().startPlayRingingTone(mFilePath);
+ UIUtil.wakeUp();
+ }
+
+ public static void cancleNotification() {
+ getManager(BaseAppContext.getInstance()).cancelAll();
+ }
+
+
+ /**
+ * 呼叫取消,跳转到main
+ */
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ public static void callEndNotify(Context context, String user) {
+ getManager(BaseAppContext.getInstance()).deleteNotificationChannel(CALL_CHANNEL_ID);
+ if (enable) {
+// TODO Intent intent = new Intent(context, MainActivityV2.class);
+ Intent intent = new Intent();
+ String name = context.getString(R.string.cloudLink_meeting_voiceNotify);
+ // PendingIntent.getActivity 接收4个参数
+ // 第1个参数是Context
+ // 第2个参数一般用不到,传0即可
+ // 第3个参数是一个Intent对象,可以通过这个对象构建出PendingIntent的意图
+ // 这里的Intent表达了我要启动NotificationManager活动的企图
+ // 第4个参数是用于确定PendingIntent的行为,有4个默认值,通常情况下传入0即可
+ PendingIntent pi = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ //第一步:获取状态通知栏管理:
+ NotificationManager manager =
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ Notification notification = null;
+ //第二步:实例化通知栏构造器NotificationCompat.Builder:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//判断API
+ NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name,
+ NotificationManager.IMPORTANCE_HIGH);
+// mChannel.setLockscreenVisibility(Notification.VISIBILITY_SECRET);
+ manager.createNotificationChannel(mChannel);
+ }
+ notification = new NotificationCompat.Builder(context, CHANNEL_ID)
+ .setContentTitle(context.getString(R.string.app_name))//设置通知栏标题
+ .setContentText(user + ":" + context.getString(R.string.cloudLink_meeting_callEnd)) //设置通知栏显示内容
+ .setWhen(System.currentTimeMillis())//通知产生的时间。
+ // 会在通知信息里显示,通常是系统获取到的时间
+ .setSmallIcon(R.mipmap.ic_launcher)//设置通知小ICON
+ .setLargeIcon(BitmapFactory.decodeResource(getResources()
+ , R.mipmap.ic_launcher))//设置通知大ICON
+ .setContentIntent(pi)
+ .setAutoCancel(true)
+ .build();
+// if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
+// notification.headsUpContentView = getRemoteViews();
+// }
+ //第三步:对Builder进行配置:
+ manager.notify(1, notification);
+ }
+ }
+
+ public static void initChannel(Context context) {
+ if (enable) {
+ String name = context.getString(R.string.cloudLink_meeting_voiceNotify);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {//判断API
+ NotificationManager manager =
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name,
+ NotificationManager.IMPORTANCE_HIGH);
+ manager.createNotificationChannel(mChannel);
+ }
+ }
+ }
+
+ /**
+ * 取消通知
+ */
+ public static void cancelNotify(Context context) {
+ if (enable) {
+ NotificationManager manager =
+ (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ manager.cancelAll();
+ }
+ }
+
+ /**
+ * 作者:CnPeng
+ * 时间:2018/7/12 上午9:02
+ * 功用:检查是否已经开启了通知权限
+ * 说明:
+ */
+ public static boolean checkNotifySetting(Context context) {
+ NotificationManagerCompat manager = NotificationManagerCompat.from(context);
+ // areNotificationsEnabled方法的有效性官方只最低支持到API 19,低于19的仍可调用此方法不过只会返回true,即默认为用户已经开启了通知。
+ return manager.areNotificationsEnabled();
+ }
+
+ /**
+ * 功用:跳转请求通知
+ */
+ public static void requestNotify(Context context) {
+ try {
+ // 根据isOpened结果,判断是否需要提醒用户跳转AppInfo页面,去打开App通知权限
+ Intent intent = new Intent();
+ //这种方案适用于 API 26, 即8.0(含8.0)以上可以用
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
+// intent.putExtra(Settings.EXTRA_CHANNEL_ID, CHANNEL_ID);
+ intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
+ //这种方案适用于 API21——25,即 5.0——7.1 之间的版本可以使用
+ intent.putExtra("app_package", getPackageName());
+ intent.putExtra("app_uid", context.getApplicationInfo().uid);
+ // 小米6 -MIUI9.6-8.0.0系统,是个特例,通知设置界面只能控制"允许使用通知圆点"——然而这个玩意并没有卵用,我想对雷布斯说:I'm not ok!!!
+ // if ("MI 6".equals(Build.MODEL)) {
+ // intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ // Uri uri = Uri.fromParts("package", getPackageName(), null);
+ // intent.setData(uri);
+ // // intent.setAction("com.android.settings/.SubSettings");
+ // }
+ context.startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ // 出现异常则跳转到应用设置界面:锤子坚果3——OC105 API25
+ Intent intent = new Intent();
+
+ //下面这种方案是直接跳转到当前应用的设置界面。
+ //https://blog.csdn.net/ysy950803/article/details/71910806
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ intent.setData(uri);
+ context.startActivity(intent);
+ }
+ }
+
+ /**
+ * 功用:跳转请求通知渠道
+ */
+ public static void requestChannel(Context context) {
+ try {
+ // 根据isOpened结果,判断是否需要提醒用户跳转AppInfo页面,去打开App通知权限
+ Intent intent = new Intent();
+ intent.setPackage(context.getPackageName());
+ //这种方案适用于 API 26, 即8.0(含8.0)以上可以用
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ intent.setAction(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
+ intent.putExtra(Settings.EXTRA_CHANNEL_ID, CHANNEL_ID);
+ }
+ // 小米6 -MIUI9.6-8.0.0系统,是个特例,通知设置界面只能控制"允许使用通知圆点"——然而这个玩意并没有卵用,我想对雷布斯说:I'm not ok!!!
+ // if ("MI 6".equals(Build.MODEL)) {
+ // intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ // Uri uri = Uri.fromParts("package", getPackageName(), null);
+ // intent.setData(uri);
+ // // intent.setAction("com.android.settings/.SubSettings");
+ // }
+ context.startActivity(intent);
+ } catch (Exception e) {
+ e.printStackTrace();
+ // 出现异常则跳转到应用设置界面:锤子坚果3——OC105 API25
+ Intent intent = new Intent();
+
+ //下面这种方案是直接跳转到当前应用的设置界面。
+ //https://blog.csdn.net/ysy950803/article/details/71910806
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ intent.setData(uri);
+ context.startActivity(intent);
+ }
+ }
+
+ /**
+ * 功用:跳转请求通知渠道
+ */
+ public static void requestSetting(Context context) {
+ Intent intent = new Intent();
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", getPackageName(), null);
+ intent.setData(uri);
+ context.startActivity(intent);
+ }
+
+ public static boolean isNotificationEnabled(Context context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ NotificationManager manager = (NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
+ NotificationChannel channel = manager.getNotificationChannel(CHANNEL_ID);
+ if (channel != null) {
+ /**
+ * 这里是应用总通知开关已经打开 但是你注册的单个通知开关关闭了
+ */
+ if (channel.getImportance() == NotificationManager.IMPORTANCE_NONE) {
+ return false;
+ } else {
+ try {
+ /**
+ * 这是应用总通知开关关闭了
+ */
+ ApplicationInfo appInfo = context.getApplicationInfo();
+ String pkg = context.getApplicationContext().getPackageName();
+ int uid = appInfo.uid;
+ NotificationManager notificationManager = (NotificationManager)
+ context.getSystemService(Context.NOTIFICATION_SERVICE);
+ Method sServiceField = notificationManager.getClass().getDeclaredMethod("getService");
+ sServiceField.setAccessible(true);
+ Object sService = sServiceField.invoke(notificationManager);
+
+ Method method = sService.getClass().getDeclaredMethod("areNotificationsEnabledForPackage"
+ , String.class, Integer.TYPE);
+ method.setAccessible(true);
+ return (boolean) method.invoke(sService, pkg, uid);
+ } catch (Exception e) {
+ return true;
+ }
+ }
+ } else {
+ return true;
+ }
+
+ } else {
+ AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ ApplicationInfo appInfo = context.getApplicationInfo();
+ String pkg = context.getApplicationContext().getPackageName();
+ int uid = appInfo.uid;
+ Class appOpsClass = null;
+ try {
+ appOpsClass = Class.forName(AppOpsManager.class.getName());
+ Method checkOpNoThrowMethod =
+ appOpsClass.getMethod(
+ "checkOpNoThrow",
+ Integer.TYPE,
+ Integer.TYPE,
+ String.class
+ );
+ Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION");
+ int value = (int) opPostNotificationValue.get(Integer.class);
+
+ int invoke = (int) checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg);
+ return (invoke == AppOpsManager.MODE_ALLOWED);
+
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (NoSuchMethodException e) {
+ e.printStackTrace();
+ } catch (NoSuchFieldException e) {
+ e.printStackTrace();
+ } catch (InvocationTargetException e) {
+ e.printStackTrace();
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/OppoUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/OppoUtils.java
new file mode 100644
index 0000000..cf7884b
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/OppoUtils.java
@@ -0,0 +1,70 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Build;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+/**
+ * Description:
+ *
+ * @author Shawn_Dut
+ * @since 2018-02-01
+ */
+public class OppoUtils {
+
+ private static final String TAG = "OppoUtils";
+
+ /**
+ * 检测 360 悬浮窗权限
+ */
+ public static boolean checkFloatWindowPermission(Context context) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
+ }
+ return true;
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static boolean checkOp(Context context, int op) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ try {
+ Class clazz = AppOpsManager.class;
+ Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
+ return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ } else {
+ Log.e(TAG, "Below API 19 cannot invoke!");
+ }
+ return false;
+ }
+
+ /**
+ * oppo ROM 权限申请
+ */
+ public static void applyOppoPermission(Context context) {
+ //merge request from https://github.com/zhaozepeng/FloatWindowPermission/pull/26
+ try {
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ //com.coloros.safecenter/.sysfloatwindow.FloatWindowListActivity
+ ComponentName comp = new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.sysfloatwindow.FloatWindowListActivity");//悬浮窗管理页面
+ intent.setComponent(comp);
+ context.startActivity(intent);
+ }
+ catch(Exception e){
+ Log.e(TAG, "",e);
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/PermissionUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/PermissionUtils.java
new file mode 100644
index 0000000..1bffbd8
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/PermissionUtils.java
@@ -0,0 +1,145 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import androidx.core.app.AppOpsManagerCompat;
+import androidx.core.content.ContextCompat;
+
+/**
+ * 权限管理工具类
+ */
+public class PermissionUtils {
+
+ private static final String TAG = "PermissionUtils";
+
+ // 构造函数私有防止不必要的初始化
+ private PermissionUtils() {
+ throw new UnsupportedOperationException("you can not instantiate me...");
+ }
+
+ /**
+ * 检查悬浮窗权限
+ *
+ * @param context
+ * @return
+ */
+ public static boolean checkFloatWindowPermission(Context context) {
+ //6.0 版本之后由于 google 增加了对悬浮窗权限的管理,所以方式就统一了
+ if (Build.VERSION.SDK_INT < 23) {
+ if (RomUtils.checkIsMiuiRom()) {
+ return MiuiUtils.checkFloatWindowPermission(context);
+ } else if (RomUtils.checkIsMeizuRom()) {
+ return MeizuUtils.checkFloatWindowPermission(context);
+ } else if (RomUtils.checkIsHuaweiRom()) {
+ return HuaweiUtils.checkFloatWindowPermission(context);
+ } else if (RomUtils.checkIs360Rom()) {
+ return QikuUtils.checkFloatWindowPermission(context);
+ } else if (RomUtils.checkIsOppoRom()) {
+ return OppoUtils.checkFloatWindowPermission(context);
+ }
+ }
+ return commonROMPermissionCheck(context);
+ }
+
+ public static boolean hasPermission( Context context, String permission) {
+ List permisstions = new ArrayList<>();
+ permisstions.add(permission);
+ return hasPermission(context, permisstions);
+ }
+
+ /**
+ * 系统层的权限判断
+ *
+ * @param context 上下文
+ * @param permissions 申请的权限 Manifest.permission.READ_CONTACTS
+ * @return 是否有权限 :其中有一个获取不了就是失败了
+ */
+ public static boolean hasPermission( Context context, List permissions) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return true;
+ for (String permission : permissions) {
+ try {
+ String op = AppOpsManagerCompat.permissionToOp(permission);
+ if (TextUtils.isEmpty(op)) continue;
+ int result = AppOpsManagerCompat.noteOp(context, op, android.os.Process.myUid(), context.getPackageName());
+ if (result == AppOpsManagerCompat.MODE_IGNORED) return false;
+ AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ String ops = AppOpsManager.permissionToOp(permission);
+ int locationOp = appOpsManager.checkOp(ops, Binder.getCallingUid(), context.getPackageName());
+ if (locationOp == AppOpsManager.MODE_IGNORED) return false;
+ result = ContextCompat.checkSelfPermission(context, permission);
+ if (result != PackageManager.PERMISSION_GRANTED) return false;
+ } catch (Exception ex) {
+ Log.e(TAG, "[hasPermission] error ", ex);
+ }
+ }
+ return true;
+ }
+
+ /**
+ * 跳转到权限设置界面
+ *
+ * @param context
+ */
+ public static void toPermissionSetting(Context context) throws NoSuchFieldException, IllegalAccessException {
+ if (Build.VERSION.SDK_INT < 23) {
+ if (RomUtils.checkIsMiuiRom()) {
+ MiuiUtils.applyMiuiPermission(context);
+ } else if (RomUtils.checkIsMeizuRom()) {
+ MeizuUtils.applyPermission(context);
+ } else if (RomUtils.checkIsHuaweiRom()) {
+ HuaweiUtils.applyPermission(context);
+ } else if (RomUtils.checkIs360Rom()) {
+ QikuUtils.applyPermission(context);
+ } else if (RomUtils.checkIsOppoRom()) {
+ OppoUtils.applyOppoPermission(context);
+ } else {
+ RomUtils.getAppDetailSettingIntent(context);
+ }
+ } else {
+ if (RomUtils.checkIsMeizuRom()) {
+ MeizuUtils.applyPermission(context);
+ } else {
+ if (RomUtils.checkIsOppoRom() || RomUtils.checkIsVivoRom()
+ || RomUtils.checkIsHuaweiRom() || RomUtils.checkIsSamsunRom()) {
+ RomUtils.getAppDetailSettingIntent(context);
+ } else if (RomUtils.checkIsMiuiRom()) {
+ MiuiUtils.toPermisstionSetting(context);
+ } else {
+ RomUtils.commonROMPermissionApplyInternal(context);
+ }
+ }
+ }
+ }
+
+ private static boolean commonROMPermissionCheck(Context context) {
+ //最新发现魅族6.0的系统这种方式不好用,天杀的,只有你是奇葩,没办法,单独适配一下
+ if (RomUtils.checkIsMeizuRom()) {
+ return MeizuUtils.checkFloatWindowPermission(context);
+ } else {
+ Boolean result = true;
+ if (Build.VERSION.SDK_INT >= 23) {
+ try {
+ Class clazz = Settings.class;
+ Method canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context.class);
+ result = (Boolean) canDrawOverlays.invoke(null, context);
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/PhoneUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/PhoneUtil.java
new file mode 100644
index 0000000..aaacf6d
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/PhoneUtil.java
@@ -0,0 +1,110 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+
+
+import com.hjq.toast.ToastUtils;
+import com.tengshisoft.chatmodule.R;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+import java.util.Calendar;
+
+public class PhoneUtil {
+
+ public static void call(Context context, String num) {
+ Intent intent = new Intent(Intent.ACTION_DIAL);
+ Uri data = Uri.parse("tel:" + num);
+ intent.setData(data);
+ context.startActivity(intent);
+ }
+
+ public static boolean isTelephonyCalling() {
+ boolean calling = false;
+ TelephonyManager telephonyManager = (TelephonyManager) BaseAppContext.getInstance().getSystemService(Context.TELEPHONY_SERVICE);
+ if (TelephonyManager.CALL_STATE_OFFHOOK == telephonyManager.getCallState() || TelephonyManager.CALL_STATE_RINGING == telephonyManager.getCallState()) {
+ calling = true;
+ }
+ return calling;
+ }
+
+ /**
+ * 获取当前时区
+ */
+ public static String getTimeZone() {
+ return Calendar.getInstance().getTimeZone().getID();
+ }
+
+ /**
+ * 获取当前时区偏移量
+ */
+ public static int getTimeZoneOffset() {
+ return Calendar.getInstance().getTimeZone().getRawOffset();
+ }
+
+ /**
+ * 复制文本内容到系统剪贴板
+ */
+ public static void copyToClipboard(String info) {
+ //获取剪贴板管理器:
+ ClipboardManager cm = (ClipboardManager) BaseAppContext.getInstance().getSystemService(Context.CLIPBOARD_SERVICE);
+ // 创建普通字符型ClipData
+ ClipData mClipData = ClipData.newPlainText("Label", info);
+ // 将ClipData内容放到系统剪贴板里。
+ cm.setPrimaryClip(mClipData);
+ ToastUtils.show(BaseAppContext.getInstance().getResources().getString(R.string.cloudLink_meeting_copyToClipboard));
+ }
+
+ /**
+ * 判断是否开启了 “屏幕自动旋转”,true则为开启
+ */
+ public static boolean isScreenAutoRotate() {
+ try {
+ int screenchange = Settings.System.getInt(BaseAppContext.getInstance().getContentResolver(), Settings.System.ACCELEROMETER_ROTATION);
+ return screenchange == 1;
+ } catch (Settings.SettingNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
+ * 获取当前手机系统版本号
+ *
+ * @return 系统版本号
+ */
+ public static String getSystemVersion() {
+ return android.os.Build.VERSION.RELEASE;
+ }
+
+ /**
+ * 获取当前手机API版本号
+ *
+ * @return API版本号
+ */
+ public static String getAPIVersion() {
+ return android.os.Build.VERSION.SDK_INT+"";
+ }
+
+ /**
+ * 获取手机型号
+ *
+ * @return 手机型号
+ */
+ public static String getSystemModel() {
+ return android.os.Build.MODEL;
+ }
+
+ /**
+ * 获取手机厂商
+ *
+ * @return 手机厂商
+ */
+ public static String getDeviceBrand() {
+ return android.os.Build.BRAND;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Platform.kt b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Platform.kt
new file mode 100644
index 0000000..f716ac8
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/Platform.kt
@@ -0,0 +1,65 @@
+package com.tengshisoft.chatmodule.hwclud.utils
+
+import android.os.Looper
+import android.os.SystemClock
+import kotlinx.coroutines.*
+import java.lang.Runnable
+
+class Platform {
+
+ companion object {
+
+ @JvmStatic
+ fun runUi(run: Runnable) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ run.run()
+ } else {
+ GlobalScope.launch {
+ withContext(Dispatchers.Main) {
+ run.run()
+ }
+ }
+ }
+ }
+
+ @JvmStatic
+ fun runUiDelay(run: Runnable, delay: Long) =
+ GlobalScope.launch {
+ SystemClock.sleep(delay)
+ withContext(Dispatchers.Main) {
+ run.run()
+ }
+ }
+
+ // 执行一个Ui线程,执行一个子线程,当UI线程执行完毕后执行子线程
+ @JvmStatic
+ fun run(uiRun: Runnable, ioRun: Runnable) {
+ GlobalScope.launch {
+ val result = async {
+ withContext(Dispatchers.Main) {
+ uiRun.run()
+ }
+ }
+ result.await()
+ ioRun.run()
+ }
+ }
+
+
+ fun getFormatName(name: String): String {
+ var newName = name
+ var pas = ""
+ if (!name.contains("*")) {
+ return newName
+ }
+ val split = name.split("\\*".toRegex()).toTypedArray()
+ if (split.size > 1) {
+ split[split.lastIndex] = ""
+ }
+ for (i in split.indices) {
+ pas += split[i]
+ }
+ return pas
+ }
+ }
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/QikuUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/QikuUtils.java
new file mode 100644
index 0000000..8675482
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/QikuUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
+ */
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.util.Log;
+
+import java.lang.reflect.Method;
+
+/**
+ * 360ROM工具类
+ */
+public class QikuUtils {
+ private static final String TAG = "QikuUtils";
+
+ /**
+ * 检测 360 悬浮窗权限
+ */
+ public static boolean checkFloatWindowPermission(Context context) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ return checkOp(context, 24); //OP_SYSTEM_ALERT_WINDOW = 24;
+ }
+ return true;
+ }
+
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ private static boolean checkOp(Context context, int op) {
+ final int version = Build.VERSION.SDK_INT;
+ if (version >= 19) {
+ AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ try {
+ Class clazz = AppOpsManager.class;
+ Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class);
+ return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName());
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ } else {
+ Log.e("", "Below API 19 cannot invoke!");
+ }
+ return false;
+ }
+
+ /**
+ * 去360权限申请页面
+ */
+ public static void applyPermission(Context context) {
+ Intent intent = new Intent();
+ intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.AppEnterActivity");
+ if (isIntentAvailable(intent, context)) {
+ context.startActivity(intent);
+ } else {
+ Log.e(TAG, "can't open permission page with particular name, please use " +
+ "\"adb shell dumpsys activity\" command and tell me the name of the float window permission page");
+ }
+ }
+ }
+
+ private static boolean isIntentAvailable(Intent intent, Context context) {
+ if (intent == null) {
+ return false;
+ }
+ return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/RomUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/RomUtil.java
new file mode 100644
index 0000000..c0f5227
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/RomUtil.java
@@ -0,0 +1,244 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Method;
+
+public class RomUtil {
+
+ private static final String TAG = "Rom";
+
+ public static final String ROM_MIUI = "MIUI";
+ public static final String ROM_EMUI = "EMUI";
+ public static final String ROM_FLYME = "FLYME";
+ public static final String ROM_OPPO = "OPPO";
+ public static final String ROM_SMARTISAN = "SMARTISAN";
+ public static final String ROM_VIVO = "VIVO";
+ public static final String ROM_QIKU = "QIKU";
+
+ private static final String KEY_VERSION_MIUI = "ro.miui.ui.version.name";
+ private static final String KEY_VERSION_EMUI = "ro.build.version.emui";
+ private static final String KEY_VERSION_OPPO = "ro.build.version.opporom";
+ private static final String KEY_VERSION_SMARTISAN = "ro.smartisan.version";
+ private static final String KEY_VERSION_VIVO = "ro.vivo.os.version";
+
+ private static String sName;
+ private static String sVersion;
+
+ //华为
+ public static boolean isEmui() {
+ return check(ROM_EMUI);
+ }
+
+ //小米
+ public static boolean isMiui() {
+ return check(ROM_MIUI);
+ }
+
+ //vivo
+ public static boolean isVivo() {
+ return check(ROM_VIVO);
+ }
+
+ //oppo
+ public static boolean isOppo() {
+ return check(ROM_OPPO);
+ }
+
+ //魅族
+ public static boolean isFlyme() {
+ return check(ROM_FLYME);
+ }
+
+ //360手机
+ public static boolean is360() {
+ return check(ROM_QIKU) || check("360");
+ }
+
+ public static boolean isSmartisan() {
+ return check(ROM_SMARTISAN);
+ }
+
+ public static String getName() {
+ if (sName == null) {
+ check("");
+ }
+ return sName;
+ }
+
+ public static String getVersion() {
+ if (sVersion == null) {
+ check("");
+ }
+ return sVersion;
+ }
+
+ public static boolean check(String rom) {
+ if (sName != null) {
+ return sName.equals(rom);
+ }
+
+ if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_MIUI))) {
+ sName = ROM_MIUI;
+ } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_EMUI))) {
+ sName = ROM_EMUI;
+ } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_OPPO))) {
+ sName = ROM_OPPO;
+ } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_VIVO))) {
+ sName = ROM_VIVO;
+ } else if (!TextUtils.isEmpty(sVersion = getProp(KEY_VERSION_SMARTISAN))) {
+ sName = ROM_SMARTISAN;
+ } else {
+ sVersion = Build.DISPLAY;
+ if (sVersion.toUpperCase().contains(ROM_FLYME)) {
+ sName = ROM_FLYME;
+ } else {
+ sVersion = Build.UNKNOWN;
+ sName = Build.MANUFACTURER.toUpperCase();
+ }
+ }
+ return sName.equals(rom);
+ }
+
+ public static String getProp(String name) {
+ String line = null;
+ BufferedReader input = null;
+ try {
+ Process p = Runtime.getRuntime().exec("getprop " + name);
+ input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
+ line = input.readLine();
+ input.close();
+ } catch (IOException ex) {
+ Log.e(TAG, "Unable to read prop " + name, ex);
+ return null;
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return line;
+ }
+
+
+ /**
+ * 小米后台弹出界面检测方法
+ *
+ * @param context
+ * @return
+ */
+ public static boolean canBackStartForXiaoMi(Context context) {
+ AppOpsManager ops = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ ops = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ }
+ try {
+ int op = 10021;
+ Method method = ops.getClass().getMethod("checkOpNoThrow", new Class[]{int.class, int.class, String.class});
+ Integer result = (Integer) method.invoke(ops, op, android.os.Process.myUid(), context.getPackageName());
+ return result == AppOpsManager.MODE_ALLOWED;
+ } catch (Exception e) {
+ }
+ return false;
+ }
+
+ /**
+ * 判断vivo后台弹出界面
+ *
+ * @param context
+ * @return
+ */
+ public static boolean canBackStartForVivo(Context context) {
+ String packageName = context.getPackageName();
+ Uri uri2 = Uri.parse("content://com.vivo.permissionmanager.provider.permission/start_bg_activity");
+ String selection = "pkgname = ?";
+ String[] selectionArgs = new String[]{packageName};
+ try {
+ Cursor cursor = context
+ .getContentResolver()
+ .query(uri2, null, selection, selectionArgs, null);
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ int currentmode = cursor.getInt(cursor.getColumnIndex("currentstate"));
+ cursor.close();
+ return currentmode == 0;
+ } else {
+ cursor.close();
+ return false;
+ }
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * 判断vivo锁屏显示 1未开启 0开启
+ *
+ * @param context
+ * @return
+ */
+ public static boolean checkLockStatusForVivo(Context context) {
+ String packageName = context.getPackageName();
+ Uri uri2 = Uri.parse("content://com.vivo.permissionmanager.provider.permission/control_locked_screen_action");
+ String selection = "pkgname = ?";
+ String[] selectionArgs = new String[]{packageName};
+ try {
+ Cursor cursor = context
+ .getContentResolver()
+ .query(uri2, null, selection, selectionArgs, null);
+ if (cursor != null) {
+ if (cursor.moveToFirst()) {
+ int currentmode = cursor.getInt(cursor.getColumnIndex("currentstate"));
+ cursor.close();
+ return currentmode == 0;
+ } else {
+ cursor.close();
+ return false;
+ }
+ }
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+ return false;
+ }
+
+ /**
+ * 小米后台锁屏检测方法
+ * @param context
+ * @return
+ */
+ public static boolean checkLockStatusForXiaoMi(Context context) {
+ AppOpsManager ops = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ ops = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ }
+ try {
+ int op = 10020; // >= 23
+ // ops.checkOpNoThrow(op, uid, packageName)
+ Method method = ops.getClass().getMethod("checkOpNoThrow", new Class[]
+ {int.class, int.class, String.class}
+ );
+ Integer result = (Integer) method.invoke(ops, op, android.os.Process.myUid(), context.getPackageName());
+
+ return result == AppOpsManager.MODE_ALLOWED;
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return false;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/RomUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/RomUtils.java
new file mode 100644
index 0000000..8fed832
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/RomUtils.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved.
+ */
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.TargetApi;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+
+/**
+ * Description:
+ *
+ * @author zhaozp
+ * @since 2016-05-23
+ */
+public class RomUtils {
+ private static final String TAG = "RomUtils";
+
+ /**
+ * 获取 emui 版本号
+ *
+ * @return
+ */
+ public static double getEmuiVersion() {
+ try {
+ String emuiVersion = getSystemProperty("ro.build.version.emui");
+ String version = emuiVersion.substring(emuiVersion.indexOf("_") + 1);
+ return Double.parseDouble(version);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ }
+ return 4.0;
+ }
+
+ /**
+ * 获取小米 rom 版本号,获取失败返回 -1
+ *
+ * @return miui rom version code, if fail , return -1
+ */
+ public static int getMiuiVersion() {
+ String version = getSystemProperty("ro.miui.ui.version.name");
+ if (version != null) {
+ try {
+ return Integer.parseInt(version.substring(1));
+ } catch (Exception e) {
+ Log.e(TAG, "get miui version code error, version : " + version);
+ }
+ }
+ return -1;
+ }
+
+ public static String getSystemProperty(String propName) {
+ String line;
+ BufferedReader input = null;
+ try {
+ Process p = Runtime.getRuntime().exec("getprop " + propName);
+ input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);
+ line = input.readLine();
+ input.close();
+ } catch (IOException ex) {
+ Log.e(TAG, "Unable to read sysprop " + propName, ex);
+ return null;
+ } finally {
+ if (input != null) {
+ try {
+ input.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Exception while closing InputStream", e);
+ }
+ }
+ }
+ return line;
+ }
+
+ public static boolean checkIsHuaweiRom() {
+ return Build.MANUFACTURER.contains("HUAWEI");
+ }
+
+ /**
+ * check if is miui ROM
+ */
+ public static boolean checkIsMiuiRom() {
+ return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name"));
+ }
+
+ public static boolean checkIsMeizuRom() {
+ //return Build.MANUFACTURER.contains("Meizu");
+ String meizuFlymeOSFlag = getSystemProperty("ro.build.display.id");
+ if (TextUtils.isEmpty(meizuFlymeOSFlag)) {
+ return false;
+ } else if (meizuFlymeOSFlag.contains("flyme") || meizuFlymeOSFlag.toLowerCase().contains("flyme")) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static boolean checkIs360Rom() {
+ //fix issue https://github.com/zhaozepeng/FloatWindowPermission/issues/9
+ return Build.MANUFACTURER.contains("QiKU")
+ || Build.MANUFACTURER.contains("360");
+ }
+
+ public static boolean checkIsOppoRom() {
+ //https://github.com/zhaozepeng/FloatWindowPermission/pull/26
+ return Build.MANUFACTURER.contains("OPPO") || Build.MANUFACTURER.contains("oppo");
+ }
+
+ public static boolean checkIsVivoRom() {
+ return Build.MANUFACTURER.contains("vivo") || Build.MANUFACTURER.contains("VIVO");
+ }
+
+ public static boolean checkIsSamsunRom() {
+ return Build.MANUFACTURER.contains("samsung");
+ }
+
+ /**
+ * 判断小米MIUI系统中授权管理中对应的权限授取
+ *
+ * @return false 存在核心的未收取的权限 true 核心权限已经全部授权
+ */
+ @TargetApi(Build.VERSION_CODES.KITKAT)
+ public static boolean hasMiuiPermission(Context context) {
+ AppOpsManager appOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+ int locationOp = appOpsManager.checkOp(AppOpsManager.OPSTR_FINE_LOCATION, Binder.getCallingUid(), context.getPackageName());
+ if (locationOp == AppOpsManager.MODE_IGNORED) {
+ return false;
+ }
+ return true;
+ }
+
+ public static void commonROMPermissionApplyInternal(Context context) throws NoSuchFieldException, IllegalAccessException {
+ Class clazz = Settings.class;
+ Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION");
+
+ Intent intent = new Intent(field.get(null).toString());
+ intent.setPackage(context.getPackageName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setData(Uri.parse("package:" + context.getPackageName()));
+ context.startActivity(intent);
+ }
+
+ /**
+ * 跳转到权限设置界面
+ */
+ public static void getAppDetailSettingIntent(Context context) {
+
+ // vivo 点击设置图标>加速白名单>我的app
+ // 点击软件管理>软件管理权限>软件>我的app>信任该软件
+ Intent appIntent = context.getPackageManager().getLaunchIntentForPackage("com.iqoo.secure");
+ if (appIntent != null) {
+ context.startActivity(appIntent);
+ return;
+ }
+
+ // oppo 点击设置图标>应用权限管理>按应用程序管理>我的app>我信任该应用
+ // 点击权限隐私>自启动管理>我的app
+ appIntent = context.getPackageManager().getLaunchIntentForPackage("com.oppo.safe");
+ if (appIntent != null) {
+ context.startActivity(appIntent);
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ if (Build.VERSION.SDK_INT >= 9) {
+ intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
+ intent.setData(Uri.fromParts("package", context.getPackageName(), null));
+ } else if (Build.VERSION.SDK_INT <= 8) {
+ intent.setAction(Intent.ACTION_VIEW);
+ intent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails");
+ intent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName());
+ }
+ intent.setPackage(context.getPackageName());
+ context.startActivity(intent);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ScreenUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ScreenUtil.java
new file mode 100644
index 0000000..bc94424
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ScreenUtil.java
@@ -0,0 +1,32 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.content.Context;
+
+import com.tenlionsoft.baselib.app.BaseAppContext;
+
+public class ScreenUtil {
+ /**
+ * 根据手机的分辨率从 dp 的单位 转成为 px(像素)
+ *
+ * @param context
+ * @param dpValue
+ * @return
+ */
+ public static int dp2ps(Context context, float dpValue) {
+ float scale = context.getResources().getDisplayMetrics().density;
+ return (int) (scale * dpValue + 0.5f);
+ }
+
+
+ /**
+ * 根据手机的分辨率从 sp 的单位 转成为 px(像素)
+ *
+ * @param sp sp 字体大小
+ * @return
+ */
+ public static int sp2px(float sp) {
+ float scaledDensity = BaseAppContext.getInstance().getResources().getDisplayMetrics().scaledDensity;
+ return (int) (sp * scaledDensity);
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SdkHelpUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SdkHelpUtil.java
new file mode 100644
index 0000000..83b0c9f
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SdkHelpUtil.java
@@ -0,0 +1,139 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.os.Build;
+
+import com.huawei.ecterminalsdk.base.TsdkCertDateResult;
+import com.huawei.ecterminalsdk.base.TsdkCertificateInfo;
+import com.huawei.ecterminalsdk.base.TsdkLocalAddress;
+import com.huawei.ecterminalsdk.base.TsdkUserDefBandwidthLevel;
+import com.huawei.ecterminalsdk.models.TsdkManager;
+import com.tengshisoft.chatmodule.beans.ServiceSettingBeanV2;
+import com.tengshisoft.chatmodule.hwclud.manager.CallMgrV2;
+import com.tengshisoft.chatmodule.hwclud.manager.LoginMangerV2;
+import com.tengshisoft.chatmodule.hwclud.manager.MeetingMgrV2;
+import com.tengshisoft.chatmodule.hwclud.notification.CallFunc;
+import com.tengshisoft.chatmodule.hwclud.notification.ConfFunc;
+import com.tengshisoft.chatmodule.hwclud.receiver.LoginReceiver;
+import com.tengshisoft.chatmodule.hwclud.serivce.ServiceManger;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.constant.ConstantsV2;
+import com.tenlionsoft.baselib.constant.PathConfig;
+import com.tenlionsoft.baselib.core.retrofit_net.BaseUrlApi;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+
+import androidx.annotation.RequiresApi;
+
+/**
+ * @Time: 2021/6/10
+ * @Author: isoftstone
+ * @Description:Sdk初始化工具
+ */
+public class SdkHelpUtil {
+
+ private SdkHelpUtil() {
+ }
+
+ /**
+ * 初始化SDK
+ */
+ @RequiresApi(api = Build.VERSION_CODES.M)
+ public static void initSdk() {
+ String appPath = BaseAppContext.getInstance().getApplicationInfo().dataDir + "/lib";
+ ServiceManger.getServiceMgr().startService(BaseAppContext.getInstance(),
+ appPath, 0);
+ LoginMangerV2.getInstance().regLoginEventNotification(LoginReceiver.getInstance());
+ CallMgrV2.getInstance().regCallServiceNotification(CallFunc.getInstance());
+ MeetingMgrV2.getInstance().regConfServiceNotification(ConfFunc.getInstance());
+// EnterpriseAddressBookMgr.getInstance().
+// registerNotification(EnterpriseAddrBookFunc.getInstance());
+ int port = BaseUrlApi.HW_CLOUD_PORT;
+ int transport = 1;
+ String ipAddress = BaseUrlApi.HW_CLOUD_URL;
+ ServiceManger.getServiceMgr().networkParam(port + "", ipAddress, transport);
+ String localIpAddress = DeviceManager.getLocalIpAddress(false);
+ LogUtil.e("SdkHelpUtil", localIpAddress);
+ TsdkLocalAddress localAddress = new TsdkLocalAddress(localIpAddress);
+ // 设置本地ip前必须先设置sip
+ TsdkManager.getInstance().setConfigParam(localAddress);
+ ServiceManger.getServiceMgr().securityParam(ServiceSettingBeanV2.getInstance().getRtpMode(),
+ ServiceSettingBeanV2.getInstance().getBfpTransportMode());
+ GMUtil.setSecretCertificate(BaseAppContext.getInstance(), ServiceSettingBeanV2.
+ getInstance().getIsOpenGm());
+ }
+
+ /**
+ * 判断证书有效期
+ *
+ * @return true-有效期内 false-过期
+ */
+ public static boolean certificate() {
+ long today = System.currentTimeMillis() / ConstantsV2.DELAY_MILLIS_1000;
+ TsdkCertificateInfo root = new TsdkCertificateInfo();
+ root.setCertType(0);
+ root.setAcName("thundersoft");
+ root.setCertPrivkeyPwd("Change_Me");
+ root.setCertFilePath(BaseAppContext.getInstance().getFilesDir() + "/root" + "/root_thundersoft.pem");
+ root.setCertKeyFilePath(BaseAppContext.getInstance().getFilesDir() + "/root" + "/root_thundersoft.pem");
+ boolean cerRoot = false;
+ if (null != TsdkManager.getInstance()) {
+ TsdkCertDateResult rootResult = TsdkManager.getInstance().certificateVerify(root);
+ if (null != rootResult) {
+ cerRoot = rootResult.getCertSec() > today;
+ }
+ }
+
+ TsdkCertificateInfo incer = new TsdkCertificateInfo();
+ incer.setCertType(ConstantsV2.CERT_TYPE_1);
+ incer.setAcName("thundersoft");
+ incer.setCertPrivkeyPwd("Change_Me");
+ incer.setCertFilePath(BaseAppContext.getInstance().getFilesDir() + "/" + BaseAppContext.inDir + "/equip.pem");
+ incer.setCertKeyFilePath(BaseAppContext.getInstance().getFilesDir() + "/"
+ + BaseAppContext.inDir + "/equip_key.pem");
+ boolean cerIncer = false;
+ if (null != TsdkManager.getInstance()) {
+ TsdkCertDateResult incerResult = TsdkManager.getInstance().certificateVerify(root);
+ if (null != incerResult) {
+ cerIncer = incerResult.getCertSec() > today;
+ }
+ }
+
+ TsdkCertificateInfo smcer = new TsdkCertificateInfo();
+ smcer.setCertType(ConstantsV2.CERT_TYPE_2);
+ smcer.setAcName("thundersoft");
+ smcer.setCertPrivkeyPwd("Change_Me");
+ smcer.setCertFilePath(BaseAppContext.getInstance().getFilesDir()
+ + "/" + BaseAppContext.smDir + "/equip_cloudlink.pem");
+ smcer.setCertKeyFilePath(BaseAppContext.getInstance().getFilesDir()
+ + "/" + BaseAppContext.smDir + "/equip_key_cloudlink.pem");
+ boolean smcerIncer = false;
+ if (null != TsdkManager.getInstance()) {
+ TsdkCertDateResult smcerResult = TsdkManager.getInstance().certificateVerify(root);
+ if (null != smcerResult) {
+ smcerIncer = smcerResult.getCertSec() > today;
+ }
+ }
+
+ return cerRoot && cerIncer && smcerIncer;
+ }
+
+ /**
+ * 设置视频清晰度策略
+ *
+ * @param policy 1-高清 2-普清
+ */
+ public static int setVideoDefinitionPolicy(int policy) {
+ if (null != TsdkManager.getInstance()) {
+ int result = TsdkManager.getInstance().setVideoDefinitionPolicy(policy);
+ if (policy == ConstantsV2.VIDEO_DEFINITION_POLICY) {
+ TsdkManager.getInstance().getCallManager().
+ setUserDefBandwidth(TsdkUserDefBandwidthLevel.TSDK_E_USER_DEF_BANDWIDTH_2M);
+ } else {
+ TsdkManager.getInstance().getCallManager().
+ setUserDefBandwidth(TsdkUserDefBandwidthLevel.TSDK_E_USER_DEF_BANDWIDTH_4M);
+ }
+ return result;
+ }
+ return 0;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SingleLineUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SingleLineUtil.java
new file mode 100644
index 0000000..d2afbaf
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SingleLineUtil.java
@@ -0,0 +1,38 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @antuher limp
+ * @date 2020/10/21
+ */
+public class SingleLineUtil {
+ private static SingleLineUtil mInstance;
+ private ExecutorService singleThreadExecutor;
+
+ public static SingleLineUtil getInstance(){
+ if (mInstance == null){
+ synchronized (SingleLineUtil.class){
+ if (mInstance == null){
+ mInstance = new SingleLineUtil();
+ }
+ }
+ }
+ return mInstance;
+ }
+
+ private SingleLineUtil() {}
+
+ public ExecutorService getSingle(){
+ if (singleThreadExecutor == null){
+ synchronized (SingleLineUtil.class){
+ if (singleThreadExecutor == null){
+ singleThreadExecutor = Executors.newSingleThreadExecutor();
+ }
+ }
+ }
+ return singleThreadExecutor;
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/StorageFactory.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/StorageFactory.java
new file mode 100644
index 0000000..a15d900
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/StorageFactory.java
@@ -0,0 +1,19 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import com.tengshisoft.chatmodule.hwclud.api.IPermission;
+import com.tengshisoft.chatmodule.hwclud.api.IPermissionFactory;
+
+public class StorageFactory implements IPermissionFactory {
+
+ @Override
+ public IPermission permission() {
+ return new StoragePermission();
+ }
+
+ @Override
+ public IPermission multiPermission(Class>... factories) {
+ return new MultiPermission(factories);
+ }
+
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/StoragePermission.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/StoragePermission.java
new file mode 100644
index 0000000..8f936c8
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/StoragePermission.java
@@ -0,0 +1,16 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import com.tenlionsoft.baselib.constant.PermissionConstants;
+
+import java.util.ArrayList;
+
+public class StoragePermission extends ImpPermission {
+
+ public StoragePermission() {
+ code = PermissionConstants.REQUEST_CODE_STORAGE;
+ requestPermission = new ArrayList<>();
+ requestPermission.add(PermissionConstants.WRITE_EXTERNAL_STORAGE);
+ requestPermission.add(PermissionConstants.READ_EXTERNAL_STORAGE);
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SubtitleLanguage.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SubtitleLanguage.java
new file mode 100644
index 0000000..4fc5324
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SubtitleLanguage.java
@@ -0,0 +1,30 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+/**
+ * 字幕源语言实体类
+ */
+public class SubtitleLanguage {
+ private int id;
+ private String name;
+
+ public SubtitleLanguage(int id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SystemUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SystemUtil.java
new file mode 100644
index 0000000..8cd97fc
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/SystemUtil.java
@@ -0,0 +1,127 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * 系统帮助类
+ */
+public class SystemUtil {
+
+ /**
+ * 构造函数私有防止不必要的初始化
+ */
+ private SystemUtil() {
+ throw new UnsupportedOperationException("you can not instantiate me...");
+ }
+
+ /**
+ * 判断本地是否已经安装好了指定的应用程序包
+ *
+ * @param packageNameTarget :待判断的 App 包名,如 微博 com.sina.weibo
+ * @return 已安装时返回 true,不存在时返回 false
+ */
+ public static boolean appIsExist(Context context, String packageNameTarget) {
+ if (!"".equals(packageNameTarget.trim())) {
+ PackageManager packageManager = context.getPackageManager();
+ List packageInfoList = packageManager.getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ for (PackageInfo packageInfo : packageInfoList) {
+ String packageNameSource = packageInfo.packageName;
+ if (packageNameSource.equals(packageNameTarget)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 将本应用置顶到最前端
+ * 当本应用位于后台时,则将它切换到最前端
+ *
+ * @param context
+ */
+ public static void setTopApp(Context context) {
+ if (!isRunningForeground(context)) {
+ /**获取ActivityManager*/
+ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+ /**获得当前运行的task(任务)*/
+ List taskInfoList = activityManager.getRunningTasks(20);
+ for (ActivityManager.RunningTaskInfo taskInfo : taskInfoList) {
+ /**找到本应用的 task,并将它切换到前台*/
+ if (taskInfo.topActivity.getPackageName().equals(context.getPackageName())) {
+ activityManager.moveTaskToFront(taskInfo.id, ActivityManager.MOVE_TASK_WITH_HOME);
+ String className = taskInfo.topActivity.getClassName();
+ Intent intentgo = new Intent();
+ intentgo.setAction(Intent.ACTION_MAIN);
+ intentgo.addCategory(Intent.CATEGORY_LAUNCHER);
+ try {
+ intentgo.setComponent(new ComponentName(context, Class.forName(className)));
+ } catch (ClassNotFoundException e) {
+ Log.e(LogUtil.CLOUNDLINK, "setTopApp: " + e.getMessage());
+ }
+ intentgo.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ context.startActivity(intentgo);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * 判断本应用是否已经位于最前端
+ *
+ * @param context
+ * @return 本应用已经位于最前端时,返回 true;否则返回 false
+ */
+ public static boolean isRunningForeground(Context context) {
+ ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ List appProcessInfoList = activityManager.getRunningAppProcesses();
+ /**枚举进程*/
+ if (appProcessInfoList == null) {
+ return false;
+ }
+ for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfoList) {
+ if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
+ if (appProcessInfo.processName.equals(context.getApplicationInfo().processName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 判断某个服务是否正在运行的方法
+ *
+ * @param mContext
+ * @param className 是包名+服务的类名(例如:net.loonggg.testbackstage.TestService)
+ * @return true代表正在运行,false代表服务没有正在运行
+ */
+ public static boolean isServiceWork(Context mContext, String className) {
+ ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
+ List serviceList = activityManager.getRunningServices(Integer.MAX_VALUE);
+ if (!(serviceList.size() > 0)) {
+ return false;
+ }
+ for (int i = 0; i < serviceList.size(); i++) {
+ ActivityManager.RunningServiceInfo serviceInfo = serviceList.get(i);
+ ComponentName serviceName = serviceInfo.service;
+ if (serviceName.getClassName().equals(className)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ThreadHelper.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ThreadHelper.java
new file mode 100644
index 0000000..92d600c
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/ThreadHelper.java
@@ -0,0 +1,45 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * @Time: 2021/6/15
+ * @Author: isoftstone
+ * @Description:线程工具,用于执行线程等
+ */
+public final class ThreadHelper {
+ public static final ThreadHelper INST = new ThreadHelper();
+
+ private ExecutorService executors;
+
+ private ThreadHelper() {
+ }
+
+ /**
+ * 在线程中执行
+ *
+ * @param runnable 要执行的runnable
+ */
+ public void execute(Runnable runnable) {
+ ExecutorService executorService = getExecutorService();
+ if (executorService != null) {
+ executorService.execute(runnable);
+ } else {
+ new Thread(runnable).start();
+ }
+ }
+
+ /**
+ * 获取缓存线程池
+ *
+ * @return 缓存线程池服务
+ */
+ private ExecutorService getExecutorService() {
+ if (executors == null) {
+ executors = Executors.newCachedThreadPool();
+ }
+
+ return executors;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimeSelectUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimeSelectUtils.java
new file mode 100644
index 0000000..6d55860
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimeSelectUtils.java
@@ -0,0 +1,47 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TimeSelectUtils {
+ static List hour = new ArrayList<>();
+ static List minute = new ArrayList<>();
+ List hour1 = new ArrayList<>();
+ List minute1 = new ArrayList<>();
+ List minute2 = new ArrayList<>();
+ public static List getHour() {
+ for (int i = 0; i < 24; i++) {
+ hour.add(i);
+ }
+ return hour;
+ }
+
+ public static List getMinute() {
+ minute.add(00);
+ minute.add(15);
+ minute.add(30);
+ minute.add(45);
+ return minute;
+ }
+ public List getHour1() {
+ for (int i = 0; i < 24; i++) {
+ hour1.add(i);
+ }
+ return hour1;
+ }
+ public List getMinute1() {
+ minute1.add(0);
+ minute1.add(15);
+ minute1.add(30);
+ minute1.add(45);
+ return minute1;
+ }
+ public List getMinute2() {
+ int j = 0;
+ for (int i = 0; i < 24 ; i++) {
+ j += 15;
+ minute2.add(j);
+ }
+ return minute2;
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimeUtils.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimeUtils.java
new file mode 100644
index 0000000..0ee3856
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimeUtils.java
@@ -0,0 +1,1702 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+
+import com.tenlionsoft.baselib.constant.TimeConstants;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+
+import androidx.annotation.NonNull;
+
+public final class TimeUtils {
+
+ private static final ThreadLocal SDF_THREAD_LOCAL = new ThreadLocal<>();
+
+ private static SimpleDateFormat getDefaultFormat() {
+ return getDateFormat("yyyy-MM-dd HH:mm:ss");
+ }
+
+ private static SimpleDateFormat getConfFormat() {
+ return getDateFormat("yyyy-MM-dd");
+ }
+
+ private static SimpleDateFormat getDateFormat(String pattern) {
+ SimpleDateFormat simpleDateFormat = SDF_THREAD_LOCAL.get();
+ if (simpleDateFormat == null) {
+ simpleDateFormat = new SimpleDateFormat(pattern, Locale.getDefault());
+ SDF_THREAD_LOCAL.set(simpleDateFormat);
+ } else {
+ simpleDateFormat.applyPattern(pattern);
+ }
+ return simpleDateFormat;
+ }
+
+ private TimeUtils() {
+ throw new UnsupportedOperationException("u can't instantiate me...");
+ }
+
+ /**
+ * Milliseconds to the formatted time string.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param millis The milliseconds.
+ * @return the formatted time string
+ */
+ public static String millis2String(final long millis) {
+ return millis2String(millis, getDefaultFormat());
+ }
+
+ /**
+ * Milliseconds to the formatted time string.
+ *
+ * @param millis The milliseconds.
+ * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm
+ * @return the formatted time string
+ */
+ public static String millis2String(long millis, @NonNull final String pattern) {
+ return millis2String(millis, getDateFormat(pattern));
+ }
+
+ /**
+ * Milliseconds to the formatted time string.
+ *
+ * @param millis The milliseconds.
+ * @param format The format.
+ * @return the formatted time string
+ */
+ public static String millis2String(final long millis, @NonNull final DateFormat format) {
+ return format.format(new Date(millis));
+ }
+
+ /**
+ * Formatted time string to the milliseconds.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the milliseconds
+ */
+ public static long string2Millis(final String time) {
+ return string2Millis(time, getDefaultFormat());
+ }
+
+ /**
+ * Formatted time string to the milliseconds.
+ *
+ * @param time The formatted time string.
+ * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm
+ * @return the milliseconds
+ */
+ public static long string2Millis(final String time, @NonNull final String pattern) {
+ return string2Millis(time, getDateFormat(pattern));
+ }
+
+ /**
+ * Formatted time string to the milliseconds.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the milliseconds
+ */
+ public static long string2Millis(final String time, @NonNull final DateFormat format) {
+ try {
+ return format.parse(time).getTime();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return -1;
+ }
+
+ /**
+ * Formatted time string to the date.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the date
+ */
+ public static Date string2Date(final String time) {
+ return string2Date(time, getDefaultFormat());
+ }
+
+ /**
+ * Formatted time string to the date.
+ *
+ * @param time The formatted time string.
+ * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm
+ * @return the date
+ */
+ public static Date string2Date(final String time, @NonNull final String pattern) {
+ return string2Date(time, getDateFormat(pattern));
+ }
+
+ /**
+ * Formatted time string to the date.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the date
+ */
+ public static Date string2Date(final String time, @NonNull final DateFormat format) {
+ try {
+ return format.parse(time);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Date to the formatted time string.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param date The date.
+ * @return the formatted time string
+ */
+ public static String date2String(final Date date) {
+ return date2String(date, getDefaultFormat());
+ }
+
+ /**
+ * Date to the formatted time string.
+ *
+ * @param date The date.
+ * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm
+ * @return the formatted time string
+ */
+ public static String date2String(final Date date, @NonNull final String pattern) {
+ return getDateFormat(pattern).format(date);
+ }
+
+ /**
+ * Date to the formatted time string.
+ *
+ * @param date The date.
+ * @param format The format.
+ * @return the formatted time string
+ */
+ public static String date2String(final Date date, @NonNull final DateFormat format) {
+ return format.format(date);
+ }
+
+ public static String String2DateConf(String time) {
+ return date2String(string2Date(time, getConfFormat()), getConfFormat());
+ }
+
+ /**
+ * Date to the milliseconds.
+ *
+ * @param date The date.
+ * @return the milliseconds
+ */
+ public static long date2Millis(final Date date) {
+ return date.getTime();
+ }
+
+ /**
+ * Milliseconds to the date.
+ *
+ * @param millis The milliseconds.
+ * @return the date
+ */
+ public static Date millis2Date(final long millis) {
+ return new Date(millis);
+ }
+
+ /**
+ * Return the time span, in unit.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time1 The first formatted time string.
+ * @param time2 The second formatted time string.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span, in unit
+ */
+ public static long getTimeSpan(final String time1,
+ final String time2,
+ @TimeConstants.Unit final int unit) {
+ return getTimeSpan(time1, time2, getDefaultFormat(), unit);
+ }
+
+ /**
+ * Return the time span, in unit.
+ *
+ * @param time1 The first formatted time string.
+ * @param time2 The second formatted time string.
+ * @param format The format.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span, in unit
+ */
+ public static long getTimeSpan(final String time1,
+ final String time2,
+ @NonNull final DateFormat format,
+ @TimeConstants.Unit final int unit) {
+ return millis2TimeSpan(string2Millis(time1, format) - string2Millis(time2, format), unit);
+ }
+
+ /**
+ * Return the time span, in unit.
+ *
+ * @param date1 The first date.
+ * @param date2 The second date.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span, in unit
+ */
+ public static long getTimeSpan(final Date date1,
+ final Date date2,
+ @TimeConstants.Unit final int unit) {
+ return millis2TimeSpan(date2Millis(date1) - date2Millis(date2), unit);
+ }
+
+ /**
+ * Return the time span, in unit.
+ *
+ * @param millis1 The first milliseconds.
+ * @param millis2 The second milliseconds.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span, in unit
+ */
+ public static long getTimeSpan(final long millis1,
+ final long millis2,
+ @TimeConstants.Unit final int unit) {
+ return millis2TimeSpan(millis1 - millis2, unit);
+ }
+
+ /**
+ * Return the fit time span.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time1 The first formatted time string.
+ * @param time2 The second formatted time string.
+ * @param precision The precision of time span.
+ *
+ * precision = 0, return null
+ * precision = 1, return 天
+ * precision = 2, return 天, 小时
+ * precision = 3, return 天, 小时, 分钟
+ * precision = 4, return 天, 小时, 分钟, 秒
+ * precision >= 5,return 天, 小时, 分钟, 秒, 毫秒
+ *
+ * @return the fit time span
+ */
+ public static String getFitTimeSpan(final String time1,
+ final String time2,
+ final int precision) {
+ long delta = string2Millis(time1, getDefaultFormat()) - string2Millis(time2, getDefaultFormat());
+ return millis2FitTimeSpan(delta, precision);
+ }
+
+ /**
+ * Return the fit time span.
+ *
+ * @param time1 The first formatted time string.
+ * @param time2 The second formatted time string.
+ * @param format The format.
+ * @param precision The precision of time span.
+ *
+ * precision = 0, return null
+ * precision = 1, return 天
+ * precision = 2, return 天, 小时
+ * precision = 3, return 天, 小时, 分钟
+ * precision = 4, return 天, 小时, 分钟, 秒
+ * precision >= 5,return 天, 小时, 分钟, 秒, 毫秒
+ *
+ * @return the fit time span
+ */
+ public static String getFitTimeSpan(final String time1,
+ final String time2,
+ @NonNull final DateFormat format,
+ final int precision) {
+ long delta = string2Millis(time1, format) - string2Millis(time2, format);
+ return millis2FitTimeSpan(delta, precision);
+ }
+
+ /**
+ * Return the fit time span.
+ *
+ * @param date1 The first date.
+ * @param date2 The second date.
+ * @param precision The precision of time span.
+ *
+ * precision = 0, return null
+ * precision = 1, return 天
+ * precision = 2, return 天, 小时
+ * precision = 3, return 天, 小时, 分钟
+ * precision = 4, return 天, 小时, 分钟, 秒
+ * precision >= 5,return 天, 小时, 分钟, 秒, 毫秒
+ *
+ * @return the fit time span
+ */
+ public static String getFitTimeSpan(final Date date1, final Date date2, final int precision) {
+ return millis2FitTimeSpan(date2Millis(date1) - date2Millis(date2), precision);
+ }
+
+ /**
+ * Return the fit time span.
+ *
+ * @param millis1 The first milliseconds.
+ * @param millis2 The second milliseconds.
+ * @param precision The precision of time span.
+ *
+ * precision = 0, return null
+ * precision = 1, return 天
+ * precision = 2, return 天, 小时
+ * precision = 3, return 天, 小时, 分钟
+ * precision = 4, return 天, 小时, 分钟, 秒
+ * precision >= 5,return 天, 小时, 分钟, 秒, 毫秒
+ *
+ * @return the fit time span
+ */
+ public static String getFitTimeSpan(final long millis1,
+ final long millis2,
+ final int precision) {
+ return millis2FitTimeSpan(millis1 - millis2, precision);
+ }
+
+ /**
+ * Return the current time in milliseconds.
+ *
+ * @return the current time in milliseconds
+ */
+ public static long getNowMills() {
+ return System.currentTimeMillis();
+ }
+
+ /**
+ * Return the current formatted time string.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @return the current formatted time string
+ */
+ public static String getNowString() {
+ return millis2String(System.currentTimeMillis(), getDefaultFormat());
+ }
+
+ /**
+ * Return the current formatted time string.
+ *
+ * @param format The format.
+ * @return the current formatted time string
+ */
+ public static String getNowString(@NonNull final DateFormat format) {
+ return millis2String(System.currentTimeMillis(), format);
+ }
+
+ /**
+ * Return the current date.
+ *
+ * @return the current date
+ */
+ public static Date getNowDate() {
+ return new Date();
+ }
+
+ /**
+ * Return the time span by now, in unit.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span by now, in unit
+ */
+ public static long getTimeSpanByNow(final String time, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(time, getNowString(), getDefaultFormat(), unit);
+ }
+
+ /**
+ * Return the time span by now, in unit.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span by now, in unit
+ */
+ public static long getTimeSpanByNow(final String time,
+ @NonNull final DateFormat format,
+ @TimeConstants.Unit final int unit) {
+ return getTimeSpan(time, getNowString(format), format, unit);
+ }
+
+ /**
+ * Return the time span by now, in unit.
+ *
+ * @param date The date.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span by now, in unit
+ */
+ public static long getTimeSpanByNow(final Date date, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(date, new Date(), unit);
+ }
+
+ /**
+ * Return the time span by now, in unit.
+ *
+ * @param millis The milliseconds.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the time span by now, in unit
+ */
+ public static long getTimeSpanByNow(final long millis, @TimeConstants.Unit final int unit) {
+ return getTimeSpan(millis, System.currentTimeMillis(), unit);
+ }
+
+ /**
+ * Return the fit time span by now.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @param precision The precision of time span.
+ *
+ * precision = 0,返回 null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return the fit time span by now
+ */
+ public static String getFitTimeSpanByNow(final String time, final int precision) {
+ return getFitTimeSpan(time, getNowString(), getDefaultFormat(), precision);
+ }
+
+ /**
+ * Return the fit time span by now.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @param precision The precision of time span.
+ *
+ * precision = 0,返回 null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return the fit time span by now
+ */
+ public static String getFitTimeSpanByNow(final String time,
+ @NonNull final DateFormat format,
+ final int precision) {
+ return getFitTimeSpan(time, getNowString(format), format, precision);
+ }
+
+ /**
+ * Return the fit time span by now.
+ *
+ * @param date The date.
+ * @param precision The precision of time span.
+ *
+ * precision = 0,返回 null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return the fit time span by now
+ */
+ public static String getFitTimeSpanByNow(final Date date, final int precision) {
+ return getFitTimeSpan(date, getNowDate(), precision);
+ }
+
+ /**
+ * Return the fit time span by now.
+ *
+ * @param millis The milliseconds.
+ * @param precision The precision of time span.
+ *
+ * precision = 0,返回 null
+ * precision = 1,返回天
+ * precision = 2,返回天和小时
+ * precision = 3,返回天、小时和分钟
+ * precision = 4,返回天、小时、分钟和秒
+ * precision >= 5,返回天、小时、分钟、秒和毫秒
+ *
+ * @return the fit time span by now
+ */
+ public static String getFitTimeSpanByNow(final long millis, final int precision) {
+ return getFitTimeSpan(millis, System.currentTimeMillis(), precision);
+ }
+
+ /**
+ * Return the friendly time span by now.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the friendly time span by now
+ *
+ * 如果小于 1 秒钟内,显示刚刚
+ * 如果在 1 分钟内,显示 XXX秒前
+ * 如果在 1 小时内,显示 XXX分钟前
+ * 如果在 1 小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final String time) {
+ return getFriendlyTimeSpanByNow(time, getDefaultFormat());
+ }
+
+ /**
+ * Return the friendly time span by now.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the friendly time span by now
+ *
+ * 如果小于 1 秒钟内,显示刚刚
+ * 如果在 1 分钟内,显示 XXX秒前
+ * 如果在 1 小时内,显示 XXX分钟前
+ * 如果在 1 小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final String time,
+ @NonNull final DateFormat format) {
+ return getFriendlyTimeSpanByNow(string2Millis(time, format));
+ }
+
+ /**
+ * Return the friendly time span by now.
+ *
+ * @param date The date.
+ * @return the friendly time span by now
+ *
+ * 如果小于 1 秒钟内,显示刚刚
+ * 如果在 1 分钟内,显示 XXX秒前
+ * 如果在 1 小时内,显示 XXX分钟前
+ * 如果在 1 小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final Date date) {
+ return getFriendlyTimeSpanByNow(date.getTime());
+ }
+
+ /**
+ * Return the friendly time span by now.
+ *
+ * @param millis The milliseconds.
+ * @return the friendly time span by now
+ *
+ * 如果小于 1 秒钟内,显示刚刚
+ * 如果在 1 分钟内,显示 XXX秒前
+ * 如果在 1 小时内,显示 XXX分钟前
+ * 如果在 1 小时外的今天内,显示今天15:32
+ * 如果是昨天的,显示昨天15:32
+ * 其余显示,2016-10-15
+ * 时间不合法的情况全部日期和时间信息,如星期六 十月 27 14:21:20 CST 2007
+ *
+ */
+ public static String getFriendlyTimeSpanByNow(final long millis) {
+ long now = System.currentTimeMillis();
+ long span = now - millis;
+ if (span < 0)
+ // U can read http://www.apihome.cn/api/java/Formatter.html to understand it.
+ return String.format("%tc", millis);
+ if (span < 1000) {
+ return "刚刚";
+ } else if (span < TimeConstants.MIN) {
+ return String.format(Locale.getDefault(), "%d秒前", span / TimeConstants.SEC);
+ } else if (span < TimeConstants.HOUR) {
+ return String.format(Locale.getDefault(), "%d分钟前", span / TimeConstants.MIN);
+ }
+ // 获取当天 00:00
+ long wee = getWeeOfToday();
+ if (millis >= wee) {
+ return String.format("今天%tR", millis);
+ } else if (millis >= wee - TimeConstants.DAY) {
+ return String.format("昨天%tR", millis);
+ } else {
+ return String.format("%tF", millis);
+ }
+ }
+
+ private static long getWeeOfToday() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ return cal.getTimeInMillis();
+ }
+
+ /**
+ * Return the milliseconds differ time span.
+ *
+ * @param millis The milliseconds.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the milliseconds differ time span
+ */
+ public static long getMillis(final long millis,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis + timeSpan2Millis(timeSpan, unit);
+ }
+
+ /**
+ * Return the milliseconds differ time span.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the milliseconds differ time span
+ */
+ public static long getMillis(final String time,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return getMillis(time, getDefaultFormat(), timeSpan, unit);
+ }
+
+ /**
+ * Return the milliseconds differ time span.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the milliseconds differ time span.
+ */
+ public static long getMillis(final String time,
+ @NonNull final DateFormat format,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return string2Millis(time, format) + timeSpan2Millis(timeSpan, unit);
+ }
+
+ /**
+ * Return the milliseconds differ time span.
+ *
+ * @param date The date.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the milliseconds differ time span.
+ */
+ public static long getMillis(final Date date,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return date2Millis(date) + timeSpan2Millis(timeSpan, unit);
+ }
+
+ /**
+ * Return the formatted time string differ time span.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param millis The milliseconds.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span
+ */
+ public static String getString(final long millis,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return getString(millis, getDefaultFormat(), timeSpan, unit);
+ }
+
+ /**
+ * Return the formatted time string differ time span.
+ *
+ * @param millis The milliseconds.
+ * @param format The format.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span
+ */
+ public static String getString(final long millis,
+ @NonNull final DateFormat format,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis2String(millis + timeSpan2Millis(timeSpan, unit), format);
+ }
+
+ /**
+ * Return the formatted time string differ time span.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span
+ */
+ public static String getString(final String time,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return getString(time, getDefaultFormat(), timeSpan, unit);
+ }
+
+ /**
+ * Return the formatted time string differ time span.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span
+ */
+ public static String getString(final String time,
+ @NonNull final DateFormat format,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis2String(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit), format);
+ }
+
+ /**
+ * Return the formatted time string differ time span.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param date The date.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span
+ */
+ public static String getString(final Date date,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return getString(date, getDefaultFormat(), timeSpan, unit);
+ }
+
+ /**
+ * Return the formatted time string differ time span.
+ *
+ * @param date The date.
+ * @param format The format.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span
+ */
+ public static String getString(final Date date,
+ @NonNull final DateFormat format,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis2String(date2Millis(date) + timeSpan2Millis(timeSpan, unit), format);
+ }
+
+ /**
+ * Return the date differ time span.
+ *
+ * @param millis The milliseconds.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the date differ time span
+ */
+ public static Date getDate(final long millis,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis2Date(millis + timeSpan2Millis(timeSpan, unit));
+ }
+
+ /**
+ * Return the date differ time span.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the date differ time span
+ */
+ public static Date getDate(final String time,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return getDate(time, getDefaultFormat(), timeSpan, unit);
+ }
+
+ /**
+ * Return the date differ time span.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the date differ time span
+ */
+ public static Date getDate(final String time,
+ @NonNull final DateFormat format,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis2Date(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit));
+ }
+
+ /**
+ * Return the date differ time span.
+ *
+ * @param date The date.
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the date differ time span
+ */
+ public static Date getDate(final Date date,
+ final long timeSpan,
+ @TimeConstants.Unit final int unit) {
+ return millis2Date(date2Millis(date) + timeSpan2Millis(timeSpan, unit));
+ }
+
+ /**
+ * Return the milliseconds differ time span by now.
+ *
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the milliseconds differ time span by now
+ */
+ public static long getMillisByNow(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getMillis(getNowMills(), timeSpan, unit);
+ }
+
+ /**
+ * Return the formatted time string differ time span by now.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span by now
+ */
+ public static String getStringByNow(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getStringByNow(timeSpan, getDefaultFormat(), unit);
+ }
+
+ /**
+ * Return the formatted time string differ time span by now.
+ *
+ * @param timeSpan The time span.
+ * @param format The format.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the formatted time string differ time span by now
+ */
+ public static String getStringByNow(final long timeSpan,
+ @NonNull final DateFormat format,
+ @TimeConstants.Unit final int unit) {
+ return getString(getNowMills(), format, timeSpan, unit);
+ }
+
+ /**
+ * Return the date differ time span by now.
+ *
+ * @param timeSpan The time span.
+ * @param unit The unit of time span.
+ *
+ * {@link TimeConstants#MSEC}
+ * {@link TimeConstants#SEC }
+ * {@link TimeConstants#MIN }
+ * {@link TimeConstants#HOUR}
+ * {@link TimeConstants#DAY }
+ *
+ * @return the date differ time span by now
+ */
+ public static Date getDateByNow(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return getDate(getNowMills(), timeSpan, unit);
+ }
+
+ /**
+ * Return whether it is today.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isToday(final String time) {
+ return isToday(string2Millis(time, getConfFormat()));
+ }
+
+ /**
+ * Return whether it is today.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isToday(final String time, @NonNull final DateFormat format) {
+ return isToday(string2Millis(time, format));
+ }
+
+ /**
+ * Return whether it is today.
+ *
+ * @param date The date.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isToday(final Date date) {
+ return isToday(date.getTime());
+ }
+
+ /**
+ * Return whether it is today.
+ *
+ * @param millis The milliseconds.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isToday(final long millis) {
+ long wee = getWeeOfToday();
+ return millis >= wee && millis < wee + TimeConstants.DAY;
+ }
+
+
+ /**
+ * Return whether it is leap year.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isLeapYear(final String time) {
+ return isLeapYear(string2Date(time, getDefaultFormat()));
+ }
+
+ /**
+ * Return whether it is leap year.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isLeapYear(final String time, @NonNull final DateFormat format) {
+ return isLeapYear(string2Date(time, format));
+ }
+
+ /**
+ * Return whether it is leap year.
+ *
+ * @param date The date.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isLeapYear(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ int year = cal.get(Calendar.YEAR);
+ return isLeapYear(year);
+ }
+
+ /**
+ * Return whether it is leap year.
+ *
+ * @param millis The milliseconds.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isLeapYear(final long millis) {
+ return isLeapYear(millis2Date(millis));
+ }
+
+ /**
+ * Return whether it is leap year.
+ *
+ * @param year The year.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isLeapYear(final int year) {
+ return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;
+ }
+
+ /**
+ * Return the day of week in Chinese.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the day of week in Chinese
+ */
+ public static String getChineseWeek(final String time) {
+ return getChineseWeek(string2Date(time, getDefaultFormat()));
+ }
+
+ /**
+ * Return the day of week in Chinese.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the day of week in Chinese
+ */
+ public static String getChineseWeek(final String time, @NonNull final DateFormat format) {
+ return getChineseWeek(string2Date(time, format));
+ }
+
+ /**
+ * Return the day of week in Chinese.
+ *
+ * @param date The date.
+ * @return the day of week in Chinese
+ */
+ public static String getChineseWeek(final Date date) {
+ return new SimpleDateFormat("E", Locale.CHINA).format(date);
+ }
+
+ /**
+ * Return the day of week in Chinese.
+ *
+ * @param millis The milliseconds.
+ * @return the day of week in Chinese
+ */
+ public static String getChineseWeek(final long millis) {
+ return getChineseWeek(new Date(millis));
+ }
+
+ /**
+ * Return the day of week in US.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the day of week in US
+ */
+ public static String getUSWeek(final String time) {
+ return getUSWeek(string2Date(time, getDefaultFormat()));
+ }
+
+ /**
+ * Return the day of week in US.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the day of week in US
+ */
+ public static String getUSWeek(final String time, @NonNull final DateFormat format) {
+ return getUSWeek(string2Date(time, format));
+ }
+
+ /**
+ * Return the day of week in US.
+ *
+ * @param date The date.
+ * @return the day of week in US
+ */
+ public static String getUSWeek(final Date date) {
+ return new SimpleDateFormat("EEEE", Locale.US).format(date);
+ }
+
+ /**
+ * Return the day of week in US.
+ *
+ * @param millis The milliseconds.
+ * @return the day of week in US
+ */
+ public static String getUSWeek(final long millis) {
+ return getUSWeek(new Date(millis));
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isAm() {
+ Calendar cal = Calendar.getInstance();
+ return cal.get(GregorianCalendar.AM_PM) == 0;
+ }
+
+ /**
+ * Return whether it is am.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isAm(final String time) {
+ return getValueByCalendarField(time, getDefaultFormat(), GregorianCalendar.AM_PM) == 0;
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isAm(final String time,
+ @NonNull final DateFormat format) {
+ return getValueByCalendarField(time, format, GregorianCalendar.AM_PM) == 0;
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @param date The date.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isAm(final Date date) {
+ return getValueByCalendarField(date, GregorianCalendar.AM_PM) == 0;
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @param millis The milliseconds.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isAm(final long millis) {
+ return getValueByCalendarField(millis, GregorianCalendar.AM_PM) == 0;
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isPm() {
+ return !isAm();
+ }
+
+ /**
+ * Return whether it is am.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isPm(final String time) {
+ return !isAm(time);
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isPm(final String time,
+ @NonNull final DateFormat format) {
+ return !isAm(time, format);
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @param date The date.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isPm(final Date date) {
+ return !isAm(date);
+ }
+
+ /**
+ * Return whether it is am.
+ *
+ * @param millis The milliseconds.
+ * @return {@code true}: yes {@code false}: no
+ */
+ public static boolean isPm(final long millis) {
+ return !isAm(millis);
+ }
+
+ /**
+ * Returns the value of the given calendar field.
+ *
+ * @param field The given calendar field.
+ *
+ * {@link Calendar#ERA}
+ * {@link Calendar#YEAR}
+ * {@link Calendar#MONTH}
+ * ...
+ * {@link Calendar#DST_OFFSET}
+ *
+ * @return the value of the given calendar field
+ */
+ public static int getValueByCalendarField(final int field) {
+ Calendar cal = Calendar.getInstance();
+ return cal.get(field);
+ }
+
+ /**
+ * Returns the value of the given calendar field.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @param field The given calendar field.
+ *
+ * {@link Calendar#ERA}
+ * {@link Calendar#YEAR}
+ * {@link Calendar#MONTH}
+ * ...
+ * {@link Calendar#DST_OFFSET}
+ *
+ * @return the value of the given calendar field
+ */
+ public static int getValueByCalendarField(final String time, final int field) {
+ return getValueByCalendarField(string2Date(time, getDefaultFormat()), field);
+ }
+
+ /**
+ * Returns the value of the given calendar field.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @param field The given calendar field.
+ *
+ * {@link Calendar#ERA}
+ * {@link Calendar#YEAR}
+ * {@link Calendar#MONTH}
+ * ...
+ * {@link Calendar#DST_OFFSET}
+ *
+ * @return the value of the given calendar field
+ */
+ public static int getValueByCalendarField(final String time,
+ @NonNull final DateFormat format,
+ final int field) {
+ return getValueByCalendarField(string2Date(time, format), field);
+ }
+
+ /**
+ * Returns the value of the given calendar field.
+ *
+ * @param date The date.
+ * @param field The given calendar field.
+ *
+ * {@link Calendar#ERA}
+ * {@link Calendar#YEAR}
+ * {@link Calendar#MONTH}
+ * ...
+ * {@link Calendar#DST_OFFSET}
+ *
+ * @return the value of the given calendar field
+ */
+ public static int getValueByCalendarField(final Date date, final int field) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return cal.get(field);
+ }
+
+ /**
+ * Returns the value of the given calendar field.
+ *
+ * @param millis The milliseconds.
+ * @param field The given calendar field.
+ *
+ * {@link Calendar#ERA}
+ * {@link Calendar#YEAR}
+ * {@link Calendar#MONTH}
+ * ...
+ * {@link Calendar#DST_OFFSET}
+ *
+ * @return the value of the given calendar field
+ */
+ public static int getValueByCalendarField(final long millis, final int field) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTimeInMillis(millis);
+ return cal.get(field);
+ }
+
+ private static final String[] CHINESE_ZODIAC =
+ {"猴", "鸡", "狗", "猪", "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊"};
+
+ /**
+ * Return the Chinese zodiac.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the Chinese zodiac
+ */
+ public static String getChineseZodiac(final String time) {
+ return getChineseZodiac(string2Date(time, getDefaultFormat()));
+ }
+
+ /**
+ * Return the Chinese zodiac.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the Chinese zodiac
+ */
+ public static String getChineseZodiac(final String time, @NonNull final DateFormat format) {
+ return getChineseZodiac(string2Date(time, format));
+ }
+
+ /**
+ * Return the Chinese zodiac.
+ *
+ * @param date The date.
+ * @return the Chinese zodiac
+ */
+ public static String getChineseZodiac(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ return CHINESE_ZODIAC[cal.get(Calendar.YEAR) % 12];
+ }
+
+ /**
+ * Return the Chinese zodiac.
+ *
+ * @param millis The milliseconds.
+ * @return the Chinese zodiac
+ */
+ public static String getChineseZodiac(final long millis) {
+ return getChineseZodiac(millis2Date(millis));
+ }
+
+ /**
+ * Return the Chinese zodiac.
+ *
+ * @param year The year.
+ * @return the Chinese zodiac
+ */
+ public static String getChineseZodiac(final int year) {
+ return CHINESE_ZODIAC[year % 12];
+ }
+
+ private static final int[] ZODIAC_FLAGS = {20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22};
+ private static final String[] ZODIAC = {
+ "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座", "巨蟹座",
+ "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "魔羯座"
+ };
+
+ /**
+ * Return the zodiac.
+ * The pattern is {@code yyyy-MM-dd HH:mm:ss}.
+ *
+ * @param time The formatted time string.
+ * @return the zodiac
+ */
+ public static String getZodiac(final String time) {
+ return getZodiac(string2Date(time, getDefaultFormat()));
+ }
+
+ /**
+ * Return the zodiac.
+ *
+ * @param time The formatted time string.
+ * @param format The format.
+ * @return the zodiac
+ */
+ public static String getZodiac(final String time, @NonNull final DateFormat format) {
+ return getZodiac(string2Date(time, format));
+ }
+
+ /**
+ * Return the zodiac.
+ *
+ * @param date The date.
+ * @return the zodiac
+ */
+ public static String getZodiac(final Date date) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(date);
+ int month = cal.get(Calendar.MONTH) + 1;
+ int day = cal.get(Calendar.DAY_OF_MONTH);
+ return getZodiac(month, day);
+ }
+
+ /**
+ * Return the zodiac.
+ *
+ * @param millis The milliseconds.
+ * @return the zodiac
+ */
+ public static String getZodiac(final long millis) {
+ return getZodiac(millis2Date(millis));
+ }
+
+ /**
+ * Return the zodiac.
+ *
+ * @param month The month.
+ * @param day The day.
+ * @return the zodiac
+ */
+ public static String getZodiac(final int month, final int day) {
+ return ZODIAC[day >= ZODIAC_FLAGS[month - 1]
+ ? month - 1
+ : (month + 10) % 12];
+ }
+
+ private static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {
+ return timeSpan * unit;
+ }
+
+ private static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {
+ return millis / unit;
+ }
+
+ static String millis2FitTimeSpan(long millis, int precision) {
+ if (precision <= 0) return null;
+ precision = Math.min(precision, 5);
+ String[] units = {"天", "小时", "分钟", "秒", "毫秒"};
+ if (millis == 0) return 0 + units[precision - 1];
+ StringBuilder sb = new StringBuilder();
+ if (millis < 0) {
+ sb.append("-");
+ millis = -millis;
+ }
+ int[] unitLen = {86400000, 3600000, 60000, 1000, 1};
+ for (int i = 0; i < precision; i++) {
+ if (millis >= unitLen[i]) {
+ long mode = millis / unitLen[i];
+ millis -= mode * unitLen[i];
+ sb.append(mode).append(units[i]);
+ }
+ }
+ return sb.toString();
+ }
+
+ private static ThreadLocal DateLocal = new ThreadLocal();
+
+ public static SimpleDateFormat getDateFormat() {
+ if (null == DateLocal.get()) {
+ DateLocal.set(new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA));
+ }
+ return DateLocal.get();
+ }
+
+ public static boolean IsTomorrowday(String day){
+
+ Calendar pre = Calendar.getInstance();
+ Date predate = new Date(System.currentTimeMillis());
+ pre.setTime(predate);
+
+ Calendar cal = Calendar.getInstance();
+ Date date = null;
+ try {
+ date = getDateFormat().parse(day);
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ cal.setTime(date);
+
+ if (cal.get(Calendar.YEAR) == (pre.get(Calendar.YEAR))) {
+ int diffDay = cal.get(Calendar.DAY_OF_YEAR)
+ - pre.get(Calendar.DAY_OF_YEAR);
+
+ if (diffDay == 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 获取当前日期
+ * @return 年月日时分秒
+ */
+ public static String getToday(){
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
+ String today = dateFormat.format(new Date());
+ return today;
+ }
+
+ /**
+ * 获取当前日期
+ * @return 年月日时分秒
+ */
+ public static String getTodayAll(){
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd- HH:mm:ss");
+ String today = dateFormat.format(new Date());
+ return today;
+ }
+
+ /**
+ * 获取当前日期
+ * @return 年-月-日
+ */
+ public static String getToday2(){
+ SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
+ String today = dateFormat.format(new Date());
+ return today;
+ }
+
+
+
+ /**
+ * @author LuoB.
+ * @param oldStr 较小的时间
+ * @param todayStr 较大的时间 (如果为空 默认当前时间 ,表示和当前时间相比)
+ * @return 2前天 1昨天. 0:今天 . -1 :至少是大前天.
+ * @throws ParseException 转换异常
+ */
+ public static int isWhichday(String oldStr,String todayStr) throws ParseException{
+
+ //将下面的 理解成 yyyy-MM-dd 00:00:00 更好理解点
+ SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
+ Date today = format.parse(todayStr);
+ Date oldTime=format.parse(oldStr);
+ //昨天 86400000=24*60*60*1000 一天
+ if((today.getTime()-oldTime.getTime())>86400000 && (today.getTime()-oldTime.getTime())<=86400000*2){
+ return 2;
+ }else if((today.getTime()-oldTime.getTime())>0 && (today.getTime()-oldTime.getTime())<=86400000) {
+ return 1;
+ }else if((today.getTime()-oldTime.getTime())<=0){ //至少是今天
+ return 0;
+ }else{ //至少是前天
+ return -1;
+ }
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimerUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimerUtil.java
new file mode 100644
index 0000000..11cddaa
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/TimerUtil.java
@@ -0,0 +1,87 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * @author: Limp
+ * Date: 2020/8/1 15:16
+ */
+public class TimerUtil {
+ static Timer timer = null;
+
+ /**
+ * @param period 重复执行的间隔,单位:ms
+ * @param back UI线程的回调
+ */
+ public static void startTimer(int period, TimerUiCallBack back) {
+ if (timer != null) {
+ stopTimer();
+ }
+ timer = new Timer();
+ Handler handler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ back.onUI();
+ }
+ };
+ timer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ handler.post(new TimerTask() {
+ @Override
+ public void run() {
+ handler.dispatchMessage(new Message());
+ }
+ });
+ }
+ }, 1000, period);
+ }
+
+ public static void stopTimer() {
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ }
+
+ }
+
+ /**
+ * 延迟后再UI线程执行
+ *
+ * @param delay 延迟时间,毫秒
+ * @param run 执行方法
+ */
+ public static void delay(int delay, Runnable run) {
+ Handler handler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ run.run();
+ }
+ };
+ new Thread() {
+ @Override
+ public void run() {
+ SystemClock.sleep(delay);
+ handler.post(new TimerTask() {
+ @Override
+ public void run() {
+ handler.dispatchMessage(new Message());
+ }
+ });
+ }
+ }.start();
+ }
+
+ public interface TimerUiCallBack {
+ /**
+ * UI线程的回调
+ */
+ void onUI();
+ }
+}
diff --git a/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/UIUtil.java b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/UIUtil.java
new file mode 100644
index 0000000..7fdec07
--- /dev/null
+++ b/chatmodule/src/main/java/com/tengshisoft/chatmodule/hwclud/utils/UIUtil.java
@@ -0,0 +1,1072 @@
+package com.tengshisoft.chatmodule.hwclud.utils;
+
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.hardware.Camera;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.SpinnerAdapter;
+import android.widget.TextView;
+
+import com.hjq.toast.ToastUtils;
+import com.tengshisoft.chatmodule.R;
+import com.tenlionsoft.baselib.app.BaseAppContext;
+import com.tenlionsoft.baselib.core.beans.Member;
+import com.tenlionsoft.baselib.utils.EncryptedSPTool;
+import com.tenlionsoft.baselib.utils.LogUtils;
+
+import java.lang.reflect.Field;
+import java.security.InvalidParameterException;
+import java.text.DecimalFormat;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.TimerTask;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import androidx.core.app.NotificationCompat;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import io.reactivex.Observable;
+import io.reactivex.schedulers.Schedulers;
+
+import static android.content.Context.KEYGUARD_SERVICE;
+import static android.content.Context.NOTIFICATION_SERVICE;
+import static com.tengshisoft.chatmodule.hwclud.utils.LocContext.getResources;
+
+//import cn.hotapk.fastandrutils.utils.FToastUtils;
+
+/**
+ *
+ * Created by Jelly on 2015/8/4.
+ */
+public class UIUtil {
+
+ private static Context context = BaseAppContext.getInstance();
+
+ public static List