同步ios接听界面
@ -174,6 +174,7 @@ dependencies {
|
||||
|
||||
api project(path:':TabLayout')
|
||||
api project(path:':ViewPager2Delegate')
|
||||
api project(path:':callkit')//// 音视频通话能力 UI 组件
|
||||
//implementation 'com.github.angcyo.DslTablayout:TabLayout:3.5.5'
|
||||
//可选
|
||||
//implementation 'com.github.angcyo.DslTablayout:ViewPager2Delegate:3.5.5'
|
||||
@ -182,7 +183,7 @@ dependencies {
|
||||
implementation 'com.google.android.exoplayer:exoplayer:2.18.5'
|
||||
implementation 'com.google.android.exoplayer:exoplayer-core:2.18.5@aar'
|
||||
|
||||
implementation 'cn.rongcloud.sdk:call_kit:5.5.0' // 音视频通话能力 UI 组件
|
||||
// implementation 'cn.rongcloud.sdk:call_kit:5.5.0' // 音视频通话能力 UI 组件
|
||||
implementation 'com.github.luqiming666:SwipeRecyclerView:1.4.8'//支持侧滑删除
|
||||
implementation 'com.google.android.material:material:1.6.1'
|
||||
implementation 'com.blankj:utilcode:1.30.0'//獲取uuid
|
||||
|
@ -33,7 +33,7 @@ import java.util.List;
|
||||
public class DiamondExchangeActivity extends AbsOTOActivity {
|
||||
private RecyclerView diamondExchangeList;
|
||||
private DiamondExchangeAdapter exchangeAdapter;
|
||||
private TextView title, totalConvertibility;
|
||||
private TextView title, totalConvertibility,totalConvertibilityDes;
|
||||
private String type = "yuanbao";
|
||||
|
||||
private EditText diamondExchangeInput;
|
||||
@ -63,6 +63,7 @@ public class DiamondExchangeActivity extends AbsOTOActivity {
|
||||
title = findViewById(R.id.title);
|
||||
diamondExchangeInput = findViewById(R.id.diamond_exchange_input);
|
||||
totalConvertibility = findViewById(R.id.total_convertibility);
|
||||
totalConvertibilityDes = findViewById(R.id.total_convertibility_des);
|
||||
diamondExchangeList.addItemDecoration(new ItemDecoration(mContext, Color.parseColor("#ffffff"), 10, 2));
|
||||
diamondExchangeList.setLayoutManager(new GridLayoutManager(mContext, 3));
|
||||
exchangeAdapter = new DiamondExchangeAdapter();
|
||||
@ -114,15 +115,15 @@ public class DiamondExchangeActivity extends AbsOTOActivity {
|
||||
if (data.size() > 0) {
|
||||
if ((data.size() - 1) > index && index > 0) {
|
||||
data.get(index).setSelect(true);
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getNum()));
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getSum()));
|
||||
number = data.get(index).getNum();
|
||||
} else {
|
||||
if (TextUtils.isEmpty(diamondExchangeInput.getText().toString())) {
|
||||
data.get(0).setSelect(true);
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getNum()));
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getSum()));
|
||||
number = data.get(0).getNum();
|
||||
} else {
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getNum()));
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getSum()));
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
data.get(i).setSelect(false);
|
||||
}
|
||||
@ -188,7 +189,7 @@ public class DiamondExchangeActivity extends AbsOTOActivity {
|
||||
public void onSuccess(List<ExchangeModel> data) {
|
||||
if (data.size() > 0) {
|
||||
data.get(0).setSelect(true);
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getNum()));
|
||||
totalConvertibility.setText(String.valueOf(data.get(0).getSum()));
|
||||
number = data.get(0).getNum();
|
||||
exchangeAdapter.addData(data);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public class ExchangeRecordActivity extends AbsOTOActivity {
|
||||
|
||||
private void initData() {
|
||||
OTONetManager.getInstance(mContext).
|
||||
getExchangeRecord(type, "3", "2", page, new HttpCallback<List<ExchangeRecordModel>>() {
|
||||
getExchangeRecord("10,11", "3", "2", page, new HttpCallback<List<ExchangeRecordModel>>() {
|
||||
@Override
|
||||
public void onSuccess(List<ExchangeRecordModel> data) {
|
||||
if (page != 1 && data.isEmpty()) {
|
||||
|
@ -552,7 +552,13 @@ public class ChatMessageFragment extends AbsConversationFragment {
|
||||
ViewClicksAntiShake.clicksAntiShake(home, new ViewClicksAntiShake.ViewClicksCallBack() {
|
||||
@Override
|
||||
public void onViewClicks() {
|
||||
UserManager.toHomePage(targetId);
|
||||
UserManager.toHomePage(targetId,true);
|
||||
}
|
||||
});
|
||||
ViewClicksAntiShake.clicksAntiShake(avatar, new ViewClicksAntiShake.ViewClicksCallBack() {
|
||||
@Override
|
||||
public void onViewClicks() {
|
||||
UserManager.toHomePage(targetId,true);
|
||||
}
|
||||
});
|
||||
follow.setOnClickListener(v -> {
|
||||
|
@ -43,7 +43,7 @@ public class MessageInteractiveFragment extends AbsConversationFragment {
|
||||
@Override
|
||||
public void main() {
|
||||
setTitle(noticeBean.getTitle());
|
||||
mList.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, true));
|
||||
mList.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false));
|
||||
initData();
|
||||
|
||||
}
|
||||
|
@ -375,8 +375,8 @@ public class MsgMessageFragment extends BaseFragment implements BaseAdapter.OnIt
|
||||
this.mRefreshLayout.setOnRefreshListener(new OnRefreshListener() {
|
||||
public void onRefresh(@NonNull RefreshLayout refreshLayout) {
|
||||
initSystemNotice();
|
||||
MsgMessageFragment.this.onConversationListRefresh(refreshLayout);
|
||||
updateUserInfo();
|
||||
MsgMessageFragment.this.onConversationListRefresh(refreshLayout);
|
||||
}
|
||||
});
|
||||
this.mRefreshLayout.setOnLoadMoreListener(new OnLoadMoreListener() {
|
||||
|
@ -2,6 +2,7 @@ package com.shayu.onetoone.activity.message;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
@ -42,6 +43,7 @@ import com.shayu.onetoone.manager.OTONetManager;
|
||||
import com.shayu.onetoone.manager.RouteManager;
|
||||
import com.shayu.onetoone.utils.ConversationUtils;
|
||||
import com.shayu.onetoone.utils.HtmlUrlUtils;
|
||||
import com.yunbao.common.activity.MyWalletActivity;
|
||||
import com.yunbao.common.glide.ImgLoader;
|
||||
import com.yunbao.common.http.base.HttpCallback;
|
||||
import com.yunbao.common.interfaces.OnItemClickListener;
|
||||
@ -345,8 +347,8 @@ public class CallAudioActivity extends AbsOTOActivity implements View.OnClickLis
|
||||
finish();
|
||||
if (toPay) {
|
||||
Log.e(TAG, "调起支付界面");
|
||||
// RouteManager.forwardWebViewActivity(null, "https://www.baidu.com");
|
||||
RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext,false,HtmlUrlUtils.URL_PAY_COIN));
|
||||
// RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext,false,HtmlUrlUtils.URL_PAY_COIN));
|
||||
mContext.startActivity(new Intent(mContext, MyWalletActivity.class).putExtra("p", 1));
|
||||
}
|
||||
if (toChatView) {
|
||||
ConversationUtils.startConversation(mContext, targetId);
|
||||
|
@ -3,6 +3,7 @@ package com.shayu.onetoone.activity.message;
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
@ -44,6 +45,7 @@ import com.shayu.onetoone.manager.OTONetManager;
|
||||
import com.shayu.onetoone.manager.RouteManager;
|
||||
import com.shayu.onetoone.utils.ConversationUtils;
|
||||
import com.shayu.onetoone.utils.HtmlUrlUtils;
|
||||
import com.yunbao.common.activity.MyWalletActivity;
|
||||
import com.yunbao.common.glide.ImgLoader;
|
||||
import com.yunbao.common.http.base.HttpCallback;
|
||||
import com.yunbao.common.interfaces.OnItemClickListener;
|
||||
@ -455,7 +457,8 @@ public class CallVideoActivity extends AbsOTOActivity {
|
||||
}
|
||||
}).show();
|
||||
if (toPay) {
|
||||
RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
// RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
mContext.startActivity(new Intent(mContext, MyWalletActivity.class).putExtra("p", 1));
|
||||
}
|
||||
if (toChatView) {
|
||||
ConversationUtils.startConversation(mContext, targetId);
|
||||
|
@ -65,6 +65,11 @@ public class ChatActivity extends AbsOTOActivity {
|
||||
return R.layout.activity_msg_chat;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void main(Bundle savedInstanceState) {
|
||||
int type = getIntent().getIntExtra("model", 0);
|
||||
|
@ -5,6 +5,7 @@ import android.text.SpannableString;
|
||||
import com.shayu.onetoone.R;
|
||||
import com.shayu.onetoone.bean.MessageChatAuthContent;
|
||||
import com.shayu.onetoone.bean.MessageChatGiftContent;
|
||||
import com.shayu.onetoone.bean.MessageChatTipsContent;
|
||||
import com.yanzhenjie.recyclerview.SwipeRecyclerView;
|
||||
import com.yunbao.common.utils.WordUtil;
|
||||
|
||||
@ -75,6 +76,8 @@ public class MsgMessageRecyclerViewAdapter extends ConversationListAdapter {
|
||||
datum.mConversationContent = new SpannableString(WordUtil.getNewString(R.string.gift));
|
||||
} else if (datum.mCore.getLatestMessage() instanceof MessageChatAuthContent) {
|
||||
datum.mConversationContent = new SpannableString(WordUtil.getNewString(R.string.authentication));
|
||||
}else if(datum.mCore.getLatestMessage() instanceof MessageChatTipsContent){
|
||||
datum.mConversationContent = new SpannableString(WordUtil.getNewString(R.string.system_tips));
|
||||
}
|
||||
if (datum.mCore.isTop()) {
|
||||
top.add(datum);
|
||||
|
@ -13,6 +13,9 @@ public class ExchangeModel extends BaseModel {
|
||||
private String title;
|
||||
@SerializedName("num")
|
||||
private String num;
|
||||
@SerializedName("sum")
|
||||
private String sum;
|
||||
|
||||
private boolean select = false;
|
||||
|
||||
public boolean isSelect() {
|
||||
@ -24,6 +27,14 @@ public class ExchangeModel extends BaseModel {
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getSum() {
|
||||
return sum;
|
||||
}
|
||||
|
||||
public void setSum(String sum) {
|
||||
this.sum = sum;
|
||||
}
|
||||
|
||||
public String getTop() {
|
||||
return top;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.shayu.onetoone.dialog;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
@ -28,6 +29,7 @@ import com.shayu.onetoone.view.MsgInputPanelForGift;
|
||||
import com.shayu.onetoone.widget.PagerConfig;
|
||||
import com.shayu.onetoone.widget.PagerGridLayoutManager;
|
||||
import com.shayu.onetoone.widget.PagerGridSnapHelper;
|
||||
import com.yunbao.common.activity.MyWalletActivity;
|
||||
import com.yunbao.common.dialog.AbsDialogPopupWindow;
|
||||
import com.yunbao.common.http.base.HttpCallback;
|
||||
import com.yunbao.common.interfaces.OnItemClickListener;
|
||||
@ -133,7 +135,8 @@ public class GiftDialog extends AbsDialogPopupWindow {
|
||||
ViewClicksAntiShake.clicksAntiShake(topUpBtn, new ViewClicksAntiShake.ViewClicksCallBack() {
|
||||
@Override
|
||||
public void onViewClicks() {
|
||||
RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
//RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
mContext.startActivity(new Intent(mContext, MyWalletActivity.class).putExtra("p", 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.shayu.onetoone.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
@ -13,6 +14,7 @@ import com.shayu.onetoone.R;
|
||||
import com.shayu.onetoone.listener.OnDialogClickListener;
|
||||
import com.shayu.onetoone.manager.RouteManager;
|
||||
import com.shayu.onetoone.utils.HtmlUrlUtils;
|
||||
import com.yunbao.common.activity.MyWalletActivity;
|
||||
import com.yunbao.common.dialog.AbsDialogCenterPopupWindow;
|
||||
import com.yunbao.common.utils.StringUtil;
|
||||
import com.yunbao.common.utils.WordUtil;
|
||||
@ -113,7 +115,8 @@ public class TipsDialog extends AbsDialogCenterPopupWindow {
|
||||
if (!StringUtil.isEmpty(applyText)&&(
|
||||
applyText.equals(WordUtil.getNewString(R.string.money_apply))
|
||||
|| applyText.equals(WordUtil.getNewString(R.string.dialog_to_money_tip)))) {
|
||||
RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
// RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
mContext.startActivity(new Intent(mContext, MyWalletActivity.class).putExtra("p", 1));
|
||||
}
|
||||
if (onDialogClickListener != null) {
|
||||
onDialogClickListener.onApply(dialog);
|
||||
|
@ -33,10 +33,10 @@ public class CustomConversationProvider extends BaseConversationProvider {
|
||||
try {
|
||||
if (json.getInteger("sex") == -1) {
|
||||
holder.getView(R.id.sex).setVisibility(View.GONE);
|
||||
} else if (json.getInteger("sex") == 2) {
|
||||
holder.setImageResource(R.id.sex, R.mipmap.ic_message_tab_woman);
|
||||
} else {
|
||||
} else if (json.getInteger("sex") == 1) {
|
||||
holder.setImageResource(R.id.sex, R.mipmap.ic_message_tab_man);
|
||||
} else {
|
||||
holder.setImageResource(R.id.sex, R.mipmap.ic_message_tab_woman);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -120,6 +120,8 @@ public class UserManager {
|
||||
}
|
||||
|
||||
public static void toHomePage(int userId) {
|
||||
}
|
||||
public static void toHomePage(String userId,boolean isGoto) {
|
||||
toHomePage(userId+"");
|
||||
}
|
||||
public static void toHomePage(String userId) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package com.shayu.onetoone.view;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
@ -27,6 +28,7 @@ import com.shayu.onetoone.utils.UserManager;
|
||||
import com.shayu.onetoone.widget.PagerConfig;
|
||||
import com.shayu.onetoone.widget.PagerGridLayoutManager;
|
||||
import com.shayu.onetoone.widget.PagerGridSnapHelper;
|
||||
import com.yunbao.common.activity.MyWalletActivity;
|
||||
import com.yunbao.common.http.base.HttpCallback;
|
||||
import com.yunbao.common.interfaces.OnItemClickListener;
|
||||
import com.yunbao.common.manager.IMLoginManager;
|
||||
@ -117,7 +119,8 @@ public class MsgInputPanelForGift extends AbsInputPanel {
|
||||
ViewClicksAntiShake.clicksAntiShake(topUpBtn, new ViewClicksAntiShake.ViewClicksCallBack() {
|
||||
@Override
|
||||
public void onViewClicks() {
|
||||
RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
//RouteManager.forwardWebViewActivity(null, HtmlUrlUtils.getPayUrl(mContext, false, HtmlUrlUtils.URL_PAY_COIN));
|
||||
mContext.startActivity(new Intent(mContext, MyWalletActivity.class).putExtra("p", 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -84,6 +84,7 @@
|
||||
android:textSize="22sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/total_convertibility_des"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="3dp"
|
||||
|
@ -2,8 +2,8 @@
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="26dp">
|
||||
android:background="@color/rc_white_color"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
|
||||
<FrameLayout
|
||||
|
@ -46,7 +46,7 @@
|
||||
android:textSize="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/rc_refresh"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="gone" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rc_unread_message_count"
|
||||
|
@ -46,7 +46,7 @@
|
||||
android:textSize="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@id/rc_refresh"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
tools:visibility="gone" />
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rc_unread_message_count"
|
||||
|
@ -97,6 +97,7 @@
|
||||
<string name="money_apply">前往充值</string>
|
||||
<string name="gift">[禮物]</string>
|
||||
<string name="authentication">[邀請認證]</string>
|
||||
<string name="system_tips">[系統提示]</string>
|
||||
<string name="call_tips1">等待對方接受邀請…</string>
|
||||
<string name="call_tips2">連接成功</string>
|
||||
<string name="permission_request_title">需要獲取您的權限</string>
|
||||
|
@ -97,6 +97,7 @@
|
||||
<string name="money_apply">前往充值</string>
|
||||
<string name="gift">[禮物]</string>
|
||||
<string name="authentication">[邀請認證]</string>
|
||||
<string name="system_tips">[系統提示]</string>
|
||||
<string name="call_tips1">等待對方接受邀請…</string>
|
||||
<string name="call_tips2">連接成功</string>
|
||||
<string name="permission_request_title">需要獲取您的權限</string>
|
||||
|
@ -99,6 +99,7 @@
|
||||
<string name="money_apply">前往充值</string>
|
||||
<string name="gift">[禮物]</string>
|
||||
<string name="authentication">[邀請認證]</string>
|
||||
<string name="system_tips">[系統提示]</string>
|
||||
<string name="call_tips1">等待對方接受邀請…</string>
|
||||
<string name="call_tips2">連接成功</string>
|
||||
<string name="permission_request_title">需要獲取您的權限</string>
|
||||
|
1
callkit/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
43
callkit/README.md
Normal file
@ -0,0 +1,43 @@
|
||||
## callkit-android
|
||||
|
||||
Open-source code of RongCloud VoIP Audio/Video UI. 融云音视频通话功能 UI 界面 SDK 开源代码。
|
||||
|
||||
## 适用场景
|
||||
|
||||
融云提供 CallKit 源码,是为方便开发者根据 App 风格对呼叫 UI 做个性化的修改,比如色调搭配,按钮位置等,都可以自由定制。
|
||||
|
||||
## 集成步骤
|
||||
|
||||
1. 先按照 Maven 导入或本地手动导入的方式,集成 CallLib、IMKit、IMLib 三个 CallKit 依赖库,并确保都是当时官网的最新版本,如下:
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
implementation 'cn.rongcloud.sdk:call_lib:x.y.z'
|
||||
implementation 'cn.rongcloud.sdk:im_kit:x.y.z'
|
||||
implementation 'cn.rongcloud.sdk:im_lib:x.y.z'
|
||||
}
|
||||
```
|
||||
|
||||
> CallKit 源码因为是开源的,融云不提供老版本的下载。用户配合 CallKit 所使用的 CallLib、IMKit、IMLib 版本应该也是官网此时的最新版本。
|
||||
|
||||
2. 进入工程目录,克隆 CallKit 源码:
|
||||
|
||||
```shell
|
||||
cd <ProjectFolder>
|
||||
git clone https://github.com/rongcloud/callkit-android.git
|
||||
```
|
||||
|
||||
3. 在 `settings.gradle` 文件中,添加引用:
|
||||
|
||||
```groovy
|
||||
include ':callkit-android'
|
||||
```
|
||||
|
||||
4. 在应用的 `build.gradle` 中,添加依赖:
|
||||
|
||||
```groovy
|
||||
dependencies {
|
||||
...
|
||||
implementation project(':callkit-android')
|
||||
}
|
||||
```
|
26
callkit/build.gradle
Normal file
@ -0,0 +1,26 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 31
|
||||
versionName "5.6.5"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'src/main/AndroidManifest.xml'
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api fileTree(dir: 'libs', include: ['*.jar'])
|
||||
api project(path: ':common')
|
||||
|
||||
implementation 'androidx.media:media:1.2.1'
|
||||
implementation 'com.github.bumptech.glide:glide:4.9.0'
|
||||
}
|
17
callkit/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/jiangecho/apps/adt-bundle-mac-x86_64-20140702/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
95
callkit/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,95 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.rong.callkit">
|
||||
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_ADMIN"
|
||||
android:maxSdkVersion="30" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission
|
||||
android:name="android.permission.BLUETOOTH_SCAN"
|
||||
android:usesPermissionFlags="neverForLocation" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
|
||||
<application android:supportsRtl="true">
|
||||
<meta-data
|
||||
android:name="rc.callkit"
|
||||
android:value="io.rong.callkit.RongCallKit" />
|
||||
|
||||
<activity
|
||||
android:name=".MultiVideoCallActivity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="io.rong.intent.action.voip.MULTIVIDEO" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".SingleCallActivity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="io.rong.intent.action.voip.SINGLEVIDEO" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="io.rong.intent.action.voip.SINGLEAUDIO" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".MultiAudioCallActivity"
|
||||
android:exported="false"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<intent-filter>
|
||||
<action android:name="io.rong.intent.action.voip.MULTIAUDIO" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Android Q 后台无法弹出Activity,通过通知栏提醒用户-->
|
||||
<receiver
|
||||
android:name="io.rong.callkit.VoIPBroadcastReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="action.push.CallInviteMessage" />
|
||||
<action android:name="action.push.CallInviteMessage.CLICKED" />
|
||||
<action android:name="action.push.voip.hangup.click" />
|
||||
<action android:name="action.voip.notification.clear" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<activity android:name=".CallSelectMemberActivity" />
|
||||
|
||||
<receiver
|
||||
android:name=".util.RTCPhoneStateReceiver"
|
||||
android:exported="false">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.PHONE_STATE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name=".CallForegroundService"
|
||||
android:foregroundServiceType="camera|microphone"/>
|
||||
</application>
|
||||
</manifest>
|
201
callkit/src/main/java/io/rong/callkit/AudioPlugin.java
Normal file
@ -0,0 +1,201 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import static io.rong.callkit.BaseCallActivity.REQUEST_CODE_ADD_MEMBER;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.callkit.util.RongCallPermissionUtil;
|
||||
import io.rong.callkit.util.permission.PermissionType;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.conversation.extension.RongExtension;
|
||||
import io.rong.imkit.conversation.extension.component.plugin.IPluginModule;
|
||||
import io.rong.imkit.conversation.extension.component.plugin.IPluginRequestPermissionResultCallback;
|
||||
import io.rong.imlib.IRongCoreCallback;
|
||||
import io.rong.imlib.IRongCoreEnum;
|
||||
import io.rong.imlib.RongIMClient;
|
||||
import io.rong.imlib.discussion.base.RongDiscussionClient;
|
||||
import io.rong.imlib.discussion.model.Discussion;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/** Created by weiqinxiao on 16/8/16. */
|
||||
public class AudioPlugin implements IPluginModule, IPluginRequestPermissionResultCallback {
|
||||
private static final String TAG = "AudioPlugin";
|
||||
private ArrayList<String> allMembers;
|
||||
private Context context;
|
||||
private static final int REQEUST_CODE_RECORD_AUDIO_PERMISSION = 101;
|
||||
|
||||
private Conversation.ConversationType conversationType;
|
||||
private String targetId;
|
||||
|
||||
@Override
|
||||
public Drawable obtainDrawable(Context context) {
|
||||
return context.getResources().getDrawable(R.drawable.rc_ic_phone_selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String obtainTitle(Context context) {
|
||||
return context.getString(R.string.rc_voip_audio);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(Fragment currentFragment, RongExtension extension, int index) {
|
||||
context = currentFragment.getActivity().getApplicationContext();
|
||||
conversationType = extension.getConversationType();
|
||||
targetId = extension.getTargetId();
|
||||
Log.i(TAG, "---- targetId==" + targetId);
|
||||
|
||||
PermissionType[] audioCallPermissions =
|
||||
RongCallPermissionUtil.getAudioCallPermissions(context);
|
||||
String[] permissions = new String[audioCallPermissions.length];
|
||||
for (int i = 0; i < audioCallPermissions.length; i++) {
|
||||
permissions[i] = audioCallPermissions[i].getPermissionName();
|
||||
}
|
||||
|
||||
if (RongCallPermissionUtil.checkPermissions(currentFragment.getActivity(), permissions)) {
|
||||
Log.i(TAG, "---- startAudioActivity ----");
|
||||
startAudioActivity(currentFragment, extension);
|
||||
} else {
|
||||
Log.i(TAG, "---- requestPermissionForPluginResult ----");
|
||||
extension.requestPermissionForPluginResult(
|
||||
permissions, REQEUST_CODE_RECORD_AUDIO_PERMISSION, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void startAudioActivity(Fragment currentFragment, final RongExtension extension) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
RongCallSession profile = RongCallClient.getInstance().getCallSession();
|
||||
if (profile != null && profile.getStartTime() > 0) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
profile.getMediaType() == RongCallCommon.CallMediaType.AUDIO
|
||||
? currentFragment.getString(
|
||||
R.string.rc_voip_call_audio_start_fail)
|
||||
: currentFragment.getString(
|
||||
R.string.rc_voip_call_video_start_fail),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
if (!CallKitUtils.isNetworkAvailable(context)) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
currentFragment.getString(R.string.rc_voip_call_network_error),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (conversationType.equals(Conversation.ConversationType.PRIVATE)) {
|
||||
Intent intent = new Intent(RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEAUDIO);
|
||||
intent.putExtra("conversationType", conversationType.getName().toLowerCase());
|
||||
Log.i(
|
||||
TAG,
|
||||
"---- conversationType.getName().toLowerCase() =-"
|
||||
+ conversationType.getName().toLowerCase());
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
Log.i(TAG, "---- callAction=" + RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Log.i(TAG, "getPackageName===" + context.getPackageName());
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
} else if (conversationType.equals(Conversation.ConversationType.DISCUSSION)) {
|
||||
RongDiscussionClient.getInstance()
|
||||
.getDiscussion(
|
||||
targetId,
|
||||
new IRongCoreCallback.ResultCallback<Discussion>() {
|
||||
@Override
|
||||
public void onSuccess(Discussion discussion) {
|
||||
Intent intent =
|
||||
new Intent(context, CallSelectMemberActivity.class);
|
||||
allMembers = (ArrayList<String>) discussion.getMemberIdList();
|
||||
intent.putStringArrayListExtra("allMembers", allMembers);
|
||||
intent.putExtra(
|
||||
"conversationType", conversationType.getValue());
|
||||
String myId = RongIMClient.getInstance().getCurrentUserId();
|
||||
ArrayList<String> invited = new ArrayList<>();
|
||||
invited.add(myId);
|
||||
intent.putStringArrayListExtra("invitedMembers", invited);
|
||||
intent.putExtra(
|
||||
"mediaType",
|
||||
RongCallCommon.CallMediaType.AUDIO.getValue());
|
||||
extension.startActivityForPluginResult(
|
||||
intent, 110, AudioPlugin.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(IRongCoreEnum.CoreErrorCode e) {
|
||||
RLog.d(TAG, "get discussion errorCode = " + e.getValue());
|
||||
}
|
||||
});
|
||||
} else if (conversationType.equals(Conversation.ConversationType.GROUP)) {
|
||||
Intent intent = new Intent(context, CallSelectMemberActivity.class);
|
||||
String myId = RongIMClient.getInstance().getCurrentUserId();
|
||||
ArrayList<String> invited = new ArrayList<>();
|
||||
invited.add(myId);
|
||||
intent.putStringArrayListExtra("invitedMembers", invited);
|
||||
intent.putExtra("conversationType", conversationType.getValue());
|
||||
intent.putExtra("groupId", targetId);
|
||||
intent.putExtra("mediaType", RongCallCommon.CallMediaType.AUDIO.getValue());
|
||||
extension.startActivityForPluginResult(intent, 110, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_CODE_ADD_MEMBER) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (data.getBooleanExtra("remote_hangup", false)) {
|
||||
RLog.d(TAG, "Remote exit, end the call.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Intent intent = new Intent(RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO);
|
||||
ArrayList<String> userIds = data.getStringArrayListExtra("invited");
|
||||
ArrayList<String> observers = data.getStringArrayListExtra("observers");
|
||||
userIds.add(RongIMClient.getInstance().getCurrentUserId());
|
||||
intent.putExtra("conversationType", conversationType.getName().toLowerCase());
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.putStringArrayListExtra("invitedUsers", userIds);
|
||||
intent.putStringArrayListExtra("observers", observers);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRequestPermissionResult(
|
||||
Fragment fragment,
|
||||
RongExtension extension,
|
||||
int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
Context context = fragment.getContext();
|
||||
if (RongCallPermissionUtil.checkPermissions(context, permissions)) {
|
||||
startAudioActivity(fragment, extension);
|
||||
} else {
|
||||
RongCallPermissionUtil.showRequestPermissionFailedAlter(
|
||||
context, permissions, grantResults);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
862
callkit/src/main/java/io/rong/callkit/BaseCallActivity.java
Normal file
@ -0,0 +1,862 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import static io.rong.callkit.CallFloatBoxView.showFB;
|
||||
import static io.rong.callkit.util.CallKitUtils.isDial;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHeadset;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.PowerManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import cn.rongcloud.rtc.api.RCRTCAudioRouteManager;
|
||||
import cn.rongcloud.rtc.api.callback.IRCRTCAudioRouteListener;
|
||||
import cn.rongcloud.rtc.api.stream.RCRTCVideoStreamConfig.Builder;
|
||||
import cn.rongcloud.rtc.audioroute.RCAudioRouteType;
|
||||
import cn.rongcloud.rtc.base.RCRTCParamsType.RCRTCVideoFps;
|
||||
import cn.rongcloud.rtc.base.RCRTCParamsType.RCRTCVideoResolution;
|
||||
import cn.rongcloud.rtc.utils.FinLog;
|
||||
import io.rong.callkit.util.BluetoothUtil;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.callkit.util.CallRingingUtil;
|
||||
import io.rong.callkit.util.HeadsetInfo;
|
||||
import io.rong.callkit.util.RingingMode;
|
||||
import io.rong.callkit.util.RongCallPermissionUtil;
|
||||
import io.rong.calllib.IRongCallListener;
|
||||
import io.rong.calllib.PublishCallBack;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallCommon.CallMediaType;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.manager.AudioPlayManager;
|
||||
import io.rong.imkit.manager.AudioRecordManager;
|
||||
import io.rong.imkit.notification.NotificationUtil;
|
||||
import io.rong.imkit.userinfo.RongUserInfoManager;
|
||||
import io.rong.imkit.userinfo.model.GroupUserInfo;
|
||||
import io.rong.imlib.model.Group;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Created by weiqinxiao on 16/3/9. */
|
||||
public class BaseCallActivity extends BaseNoActionBarActivity
|
||||
implements IRongCallListener,
|
||||
PickupDetector.PickupDetectListener,
|
||||
RongUserInfoManager.UserDataObserver {
|
||||
|
||||
private static final String TAG = "BaseCallActivity";
|
||||
private static final String MEDIAPLAYERTAG = "MEDIAPLAYERTAG";
|
||||
private static final long DELAY_TIME = 1000;
|
||||
static final int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 100;
|
||||
static final int REQUEST_CODE_ADD_MEMBER = 110;
|
||||
public final int REQUEST_CODE_ADD_MEMBER_NONE = 120;
|
||||
static final int VOIP_MAX_NORMAL_COUNT = 6;
|
||||
|
||||
private long time = 0;
|
||||
private Runnable updateTimeRunnable;
|
||||
|
||||
private boolean shouldRestoreFloat;
|
||||
// 是否是请求开启悬浮窗权限的过程中
|
||||
private boolean checkingOverlaysPermission;
|
||||
private boolean notRemindRequestFloatWindowPermissionAgain = false;
|
||||
protected Handler handler;
|
||||
/** 表示是否正在挂断 */
|
||||
protected boolean isFinishing;
|
||||
|
||||
protected PickupDetector pickupDetector;
|
||||
protected PowerManager powerManager;
|
||||
protected PowerManager.WakeLock wakeLock;
|
||||
protected PowerManager.WakeLock screenLock;
|
||||
|
||||
// static final String[] VIDEO_CALL_PERMISSIONS = {
|
||||
// Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA
|
||||
// };
|
||||
// static final String[] AUDIO_CALL_PERMISSIONS = {Manifest.permission.RECORD_AUDIO};
|
||||
|
||||
public static final int CALL_NOTIFICATION_ID = 4000;
|
||||
boolean isMuteCamera = false;
|
||||
|
||||
/**
|
||||
* 融云 SDK 默认麦克风、摄像头流唯一标识,和 RongCallClient#publishCustomVideoStream(tag, PublishCallBack) 方法中 tag
|
||||
* 用法一致; 用户发布自定义视频流唯一标示,不允许带下划线,不能为 “RongCloudRTC”;
|
||||
*
|
||||
* @see RongCallClient#publishCustomVideoStream(String, PublishCallBack)
|
||||
*/
|
||||
public static final String RONG_TAG_CALL = "RongCloudRTC";
|
||||
|
||||
RelativeLayout.LayoutParams mLargeLayoutParams =
|
||||
new RelativeLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
|
||||
public void setShouldShowFloat(boolean ssf) {
|
||||
CallKitUtils.shouldShowFloat = ssf;
|
||||
}
|
||||
|
||||
public void showShortToast(String text) {
|
||||
Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public void postRunnableDelay(Runnable runnable) {
|
||||
handler.postDelayed(runnable, DELAY_TIME);
|
||||
}
|
||||
|
||||
private HeadsetPlugReceiver headsetPlugReceiver = null;
|
||||
private AudioManager.OnAudioFocusChangeListener onAudioFocusChangeListener;
|
||||
|
||||
public static final String EXTRA_BUNDLE_KEY_MUTECAMERA = "muteCamera";
|
||||
public static final String EXTRA_BUNDLE_KEY_MUTEMIC = "muteMIC";
|
||||
public static final String EXTRA_BUNDLE_KEY_LOCALVIEWUSERID = "localViewUserId";
|
||||
public static final String EXTRA_BUNDLE_KEY_CALLACTION = "callAction";
|
||||
public static final String EXTRA_BUNDLE_KEY_MEDIATYPE = "mediaType";
|
||||
public static final String EXTRA_BUNDLE_KEY_USER_TOP_NAME = "rc_voip_user_top_name";
|
||||
public static final String EXTRA_BUNDLE_KEY_USER_TOP_NAME_TAG = "rc_voip_user_top_name_tag";
|
||||
public static final String EXTRA_BUNDLE_KEY_USER_PROFILE_TAG_ORDER_TAG =
|
||||
"extra_bundle_key_user_profile_tag_order_tag";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
RLog.d(TAG, "BaseCallActivity onCreate");
|
||||
audioVideoConfig();
|
||||
getWindow()
|
||||
.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow()
|
||||
.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
getWindow()
|
||||
.addFlags(
|
||||
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
||||
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
|
||||
shouldRestoreFloat = true;
|
||||
CallKitUtils.shouldShowFloat = false;
|
||||
|
||||
createPowerManager();
|
||||
boolean isScreenOn = powerManager.isScreenOn();
|
||||
if (!isScreenOn) {
|
||||
wakeLock.acquire();
|
||||
}
|
||||
handler = new Handler();
|
||||
mLargeLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||
RongCallProxy.getInstance().setCallListener(this);
|
||||
|
||||
AudioPlayManager.getInstance().stopPlay();
|
||||
AudioRecordManager.getInstance().destroyRecord();
|
||||
RongUserInfoManager.getInstance().addUserDataObserver(this);
|
||||
|
||||
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
onAudioFocusChangeListener =
|
||||
new AudioManager.OnAudioFocusChangeListener() {
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {}
|
||||
};
|
||||
am.requestAudioFocus(
|
||||
onAudioFocusChangeListener,
|
||||
AudioManager.STREAM_MUSIC,
|
||||
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 31
|
||||
&& getApplication().getApplicationInfo().targetSdkVersion >= 31) {
|
||||
// Android 12 禁止了通过广播和服务跳板的方式启动 Activity,此代码是为了兼容之前的逻辑
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(VoIPBroadcastReceiver.ACTION_CLEAR_VOIP_NOTIFICATION);
|
||||
intent.setPackage(getPackageName());
|
||||
sendBroadcast(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
Intent intent = getIntent();
|
||||
Bundle bundle = intent.getBundleExtra("floatbox");
|
||||
if (shouldRestoreFloat && bundle != null) {
|
||||
onRestoreFloatBox(bundle);
|
||||
}
|
||||
}
|
||||
|
||||
public void callRinging(RingingMode mode) {
|
||||
CallRingingUtil.getInstance().startRinging(this, mode);
|
||||
}
|
||||
|
||||
public void onIncomingCallRinging(RongCallSession callSession) {
|
||||
CallRingingUtil.getInstance().startRinging(this, RingingMode.Incoming);
|
||||
if (callSession != null) {
|
||||
String callId = callSession.getCallId();
|
||||
if (!TextUtils.isEmpty(callId)
|
||||
&& callId.equals(
|
||||
getIntent()
|
||||
.getStringExtra(
|
||||
RongIncomingCallService.KEY_NEED_AUTO_ANSWER))) {
|
||||
RongCallClient.getInstance().acceptCall(callId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setupTime(final TextView timeView) {
|
||||
try {
|
||||
if (updateTimeRunnable != null) {
|
||||
handler.removeCallbacks(updateTimeRunnable);
|
||||
}
|
||||
timeView.setVisibility(View.VISIBLE);
|
||||
updateTimeRunnable = new UpdateTimeRunnable(timeView);
|
||||
handler.post(updateTimeRunnable);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancelTime() {
|
||||
if (handler != null && updateTimeRunnable != null) {
|
||||
handler.removeCallbacks(updateTimeRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
public long getTime(long activeTime) {
|
||||
long tmpTime = activeTime == 0 ? 0 : (System.currentTimeMillis() - activeTime) / 1000;
|
||||
time = tmpTime == 0 ? time : tmpTime;
|
||||
return time;
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
protected void stopRing() {
|
||||
CallRingingUtil.getInstance().stopRinging();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallIncoming(RongCallSession callSession, SurfaceView localVideo) {
|
||||
CallKitUtils.shouldShowFloat = true;
|
||||
CallKitUtils.isDial = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallOutgoing(RongCallSession callProfile, SurfaceView localVideo) {
|
||||
CallKitUtils.shouldShowFloat = true;
|
||||
CallKitUtils.isDial = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserRinging(String userId) {}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserAccept(String userId, CallMediaType mediaType) {}
|
||||
|
||||
@Override
|
||||
public void onCallDisconnected(
|
||||
RongCallSession callProfile, RongCallCommon.CallDisconnectedReason reason) {
|
||||
stopForegroundService();
|
||||
if (RongCallKit.getCustomerHandlerListener() != null) {
|
||||
RongCallKit.getCustomerHandlerListener().onCallDisconnected(callProfile, reason);
|
||||
}
|
||||
CallKitUtils.callConnected = false;
|
||||
CallKitUtils.shouldShowFloat = false;
|
||||
|
||||
toastDisconnectReason(reason);
|
||||
|
||||
AudioPlayManager.getInstance().setInVoipMode(false);
|
||||
stopRing();
|
||||
NotificationUtil.getInstance()
|
||||
.clearNotification(this, BaseCallActivity.CALL_NOTIFICATION_ID);
|
||||
RongCallProxy.getInstance().setCallListener(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserJoined(
|
||||
String userId,
|
||||
RongCallCommon.CallMediaType mediaType,
|
||||
int userType,
|
||||
SurfaceView remoteVideo) {
|
||||
CallKitUtils.isDial = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserInvited(String userId, RongCallCommon.CallMediaType mediaType) {
|
||||
if (RongCallKit.getCustomerHandlerListener() != null) {
|
||||
RongCallKit.getCustomerHandlerListener().onRemoteUserInvited(userId, mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserLeft(String userId, RongCallCommon.CallDisconnectedReason reason) {
|
||||
RLog.i(
|
||||
TAG,
|
||||
"onRemoteUserLeft userId :"
|
||||
+ userId
|
||||
+ ", CallDisconnectedReason :"
|
||||
+ reason.name());
|
||||
toastDisconnectReason(reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaTypeChanged(
|
||||
String userId, RongCallCommon.CallMediaType mediaType, SurfaceView video) {}
|
||||
|
||||
@Override
|
||||
public void onError(RongCallCommon.CallErrorCode errorCode) {
|
||||
AudioPlayManager.getInstance().setInVoipMode(false);
|
||||
if (RongCallCommon.CallErrorCode.ENGINE_NOT_FOUND.getValue() == errorCode.getValue()) {
|
||||
Toast.makeText(
|
||||
this,
|
||||
getResources().getString(R.string.rc_voip_engine_notfound),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallConnected(RongCallSession callProfile, SurfaceView localVideo) {
|
||||
registerAudioRouteTypeChange();
|
||||
if (RongCallKit.getCustomerHandlerListener() != null) {
|
||||
RongCallKit.getCustomerHandlerListener().onCallConnected(callProfile, localVideo);
|
||||
}
|
||||
CallKitUtils.callConnected = true;
|
||||
CallKitUtils.shouldShowFloat = true;
|
||||
CallKitUtils.isDial = false;
|
||||
AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audioManager != null) {
|
||||
audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
}
|
||||
AudioPlayManager.getInstance().setInVoipMode(true);
|
||||
AudioRecordManager.getInstance().destroyRecord();
|
||||
}
|
||||
|
||||
private void registerAudioRouteTypeChange() {
|
||||
RCRTCAudioRouteManager.getInstance()
|
||||
.setOnAudioRouteChangedListener(
|
||||
new IRCRTCAudioRouteListener() {
|
||||
@Override
|
||||
public void onRouteChanged(RCAudioRouteType type) {
|
||||
resetHandFreeStatus(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRouteSwitchFailed(
|
||||
RCAudioRouteType fromType, RCAudioRouteType toType) {}
|
||||
});
|
||||
}
|
||||
|
||||
protected void resetHandFreeStatus(RCAudioRouteType type) {}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
RLog.d(TAG, "BaseCallActivity onStop");
|
||||
if (CallKitUtils.shouldShowFloat && !checkingOverlaysPermission) {
|
||||
showForegroundService();
|
||||
Bundle bundle = new Bundle();
|
||||
String action = onSaveFloatBoxState(bundle);
|
||||
if (checkDrawOverlaysPermission(true)) {
|
||||
if (action != null) {
|
||||
bundle.putString("action", action);
|
||||
showFB(getApplicationContext(), bundle);
|
||||
if (!isFinishing()) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
RLog.d(TAG, "BaseCallActivity onStop finishAndRemoveTask()");
|
||||
finishAndRemoveTask();
|
||||
} else {
|
||||
RLog.d(TAG, "BaseCallActivity onStop finish()");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this,
|
||||
getString(R.string.rc_voip_float_window_not_allowed),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
} else if (checkingOverlaysPermission) {
|
||||
showForegroundService();
|
||||
}
|
||||
}
|
||||
|
||||
private void showForegroundService() {
|
||||
RongCallSession callSession = RongCallClient.getInstance().getCallSession();
|
||||
if (callSession == null) {
|
||||
Log.e(TAG, "showForegroundService: RongCallSession is Null!");
|
||||
return;
|
||||
}
|
||||
|
||||
String content =
|
||||
callSession.getMediaType().getValue() == CallMediaType.AUDIO.getValue()
|
||||
? getString(R.string.rc_audio_call_on_going)
|
||||
: getString(R.string.rc_video_call_on_going);
|
||||
String action = getIntent().getAction();
|
||||
String title = getString(R.string.rc_call_on_going);
|
||||
Intent intent = new Intent(this, CallForegroundService.class);
|
||||
intent.putExtra("content", content);
|
||||
intent.putExtra("action", action);
|
||||
intent.putExtra("title", title);
|
||||
Bundle bundle = new Bundle();
|
||||
onSaveFloatBoxState(bundle);
|
||||
bundle.putBoolean("isDial", isDial);
|
||||
intent.putExtra("floatbox", bundle);
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.O) {
|
||||
startForegroundService(intent);
|
||||
} else {
|
||||
startService(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopForegroundService() {
|
||||
try {
|
||||
stopService(new Intent(this, CallForegroundService.class));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
stopForegroundService();
|
||||
RLog.d(TAG, "BaseCallActivity onResume");
|
||||
try {
|
||||
RongCallSession session = RongCallClient.getInstance().getCallSession();
|
||||
if (session != null) {
|
||||
if (session.getMediaType() == RongCallCommon.CallMediaType.VIDEO && !isMuteCamera) {
|
||||
RongCallClient.getInstance().startCapture();
|
||||
}
|
||||
RongCallProxy.getInstance().setCallListener(this);
|
||||
if (shouldRestoreFloat) {
|
||||
CallFloatBoxView.hideFloatBox();
|
||||
NotificationUtil.getInstance()
|
||||
.clearNotification(this, BaseCallActivity.CALL_NOTIFICATION_ID);
|
||||
CallRingingUtil.getInstance().stopServiceButContinueRinging(this);
|
||||
}
|
||||
time = getTime(session.getActiveTime());
|
||||
shouldRestoreFloat = true;
|
||||
if (time > 0) {
|
||||
CallKitUtils.shouldShowFloat = true;
|
||||
}
|
||||
if (checkingOverlaysPermission) {
|
||||
checkDrawOverlaysPermission(false);
|
||||
checkingOverlaysPermission = false;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
RLog.d(TAG, "BaseCallActivity onResume Error : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
shouldRestoreFloat = false;
|
||||
if (RongCallKit.getCustomerHandlerListener() != null) {
|
||||
List<String> selectedUserIds =
|
||||
RongCallKit.getCustomerHandlerListener()
|
||||
.handleActivityResult(requestCode, resultCode, data);
|
||||
if (selectedUserIds != null && selectedUserIds.size() > 0) onAddMember(selectedUserIds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
try {
|
||||
RLog.d(TAG, "BaseCallActivity onDestroy");
|
||||
RongUserInfoManager.getInstance().removeUserDataObserver(this);
|
||||
// RongUserInfoManager.getInstance().remove
|
||||
handler.removeCallbacks(updateTimeRunnable);
|
||||
CallRingingUtil.getInstance().stopRinging();
|
||||
|
||||
// 退出此页面后应设置成正常模式,否则按下音量键无法更改其他音频类型的音量
|
||||
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
am.setMode(AudioManager.MODE_NORMAL);
|
||||
if (onAudioFocusChangeListener != null) {
|
||||
am.abandonAudioFocus(onAudioFocusChangeListener);
|
||||
}
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
Log.i(MEDIAPLAYERTAG, "--- onDestroy IllegalStateException---");
|
||||
}
|
||||
super.onDestroy();
|
||||
// unRegisterHeadsetplugReceiver();
|
||||
if (wakeLock != null && wakeLock.isHeld()) {
|
||||
wakeLock.release();
|
||||
}
|
||||
|
||||
if (screenLock != null && screenLock.isHeld()) {
|
||||
try {
|
||||
screenLock.setReferenceCounted(false);
|
||||
screenLock.release();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteCameraDisabled(String userId, boolean disabled) {}
|
||||
|
||||
@Override
|
||||
public void onRemoteMicrophoneDisabled(String userId, boolean disabled) {}
|
||||
|
||||
@Override
|
||||
public void onNetworkReceiveLost(String userId, int lossRate) {}
|
||||
|
||||
@Override
|
||||
public void onNetworkSendLost(int lossRate, int delay) {}
|
||||
|
||||
@Override
|
||||
public void onFirstRemoteVideoFrame(String userId, int height, int width) {}
|
||||
|
||||
@Override
|
||||
public void onFirstRemoteAudioFrame(String userId) {}
|
||||
|
||||
@Override
|
||||
public void onAudioLevelSend(String audioLevel) {}
|
||||
|
||||
@Override
|
||||
public void onAudioLevelReceive(HashMap<String, String> audioLevel) {}
|
||||
|
||||
private void toastDisconnectReason(RongCallCommon.CallDisconnectedReason reason) {
|
||||
String text = null;
|
||||
switch (reason) {
|
||||
case CANCEL:
|
||||
text = getString(R.string.rc_voip_mo_cancel);
|
||||
break;
|
||||
case REJECT:
|
||||
text = getString(R.string.rc_voip_mo_reject);
|
||||
break;
|
||||
case NO_RESPONSE:
|
||||
case BUSY_LINE:
|
||||
text = getString(R.string.rc_voip_mo_no_response);
|
||||
break;
|
||||
case REMOTE_BUSY_LINE:
|
||||
text = getString(R.string.rc_voip_mt_busy);
|
||||
break;
|
||||
case REMOTE_CANCEL:
|
||||
text = getString(R.string.rc_voip_mt_cancel);
|
||||
break;
|
||||
case REMOTE_REJECT:
|
||||
text = getString(R.string.rc_voip_mt_reject);
|
||||
break;
|
||||
case REMOTE_NO_RESPONSE:
|
||||
text = getString(R.string.rc_voip_mt_no_response);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
if (!CallKitUtils.isNetworkAvailable(this)) {
|
||||
text = getString(R.string.rc_voip_call_network_error);
|
||||
} else {
|
||||
text = getString(R.string.rc_voip_call_terminalted);
|
||||
}
|
||||
break;
|
||||
case REMOTE_HANGUP:
|
||||
case HANGUP:
|
||||
case INIT_VIDEO_ERROR:
|
||||
text = getString(R.string.rc_voip_call_terminalted);
|
||||
break;
|
||||
case OTHER_DEVICE_HAD_ACCEPTED:
|
||||
text = getString(R.string.rc_voip_call_other);
|
||||
break;
|
||||
}
|
||||
if (text != null) {
|
||||
showShortToast(text);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRemoteUserPublishVideoStream(
|
||||
String userId, String streamId, String tag, SurfaceView surfaceView) {}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserUnpublishVideoStream(String userId, String streamId, String tag) {}
|
||||
|
||||
/** onStart时恢复浮窗 * */
|
||||
public void onRestoreFloatBox(Bundle bundle) {
|
||||
isMuteCamera = bundle.getBoolean(EXTRA_BUNDLE_KEY_MUTECAMERA);
|
||||
}
|
||||
|
||||
protected void addMember(ArrayList<String> currentMemberIds) {
|
||||
// do your job to add more member
|
||||
// after got your new member, call onAddMember
|
||||
if (RongCallKit.getCustomerHandlerListener() != null) {
|
||||
RongCallKit.getCustomerHandlerListener().addMember(this, currentMemberIds);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onAddMember(List<String> newMemberIds) {}
|
||||
|
||||
/** onPause时保存页面各状态数据 * */
|
||||
public String onSaveFloatBoxState(Bundle bundle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
boolean requestCallPermissions(RongCallCommon.CallMediaType type, int requestCode) {
|
||||
// String[] permissions = null;
|
||||
Log.i(TAG, "BaseActivty requestCallPermissions requestCode=" + requestCode);
|
||||
// if (type.equals(RongCallCommon.CallMediaType.VIDEO)
|
||||
// || type.equals(RongCallCommon.CallMediaType.AUDIO)) {
|
||||
// permissions = CallKitUtils.getCallpermissions();
|
||||
// }
|
||||
// boolean result = false;
|
||||
// if (permissions != null) {
|
||||
// boolean granted = CallKitUtils.checkPermissions(this, permissions);
|
||||
// Log.i(TAG, "BaseActivty requestCallPermissions granted=" + granted);
|
||||
// if (granted) {
|
||||
// result = true;
|
||||
// } else {
|
||||
// PermissionCheckUtil.requestPermissions(this, permissions, requestCode);
|
||||
// }
|
||||
// }
|
||||
|
||||
return RongCallPermissionUtil.checkAndRequestPermissionByCallType(this, type, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserUpdate(UserInfo info) {}
|
||||
|
||||
@Override
|
||||
public void onGroupUpdate(Group group) {}
|
||||
|
||||
@Override
|
||||
public void onGroupUserInfoUpdate(GroupUserInfo groupUserInfo) {}
|
||||
|
||||
private class UpdateTimeRunnable implements Runnable {
|
||||
private TextView timeView;
|
||||
|
||||
public UpdateTimeRunnable(TextView timeView) {
|
||||
this.timeView = timeView;
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public void run() {
|
||||
time++;
|
||||
if (time >= 3600) {
|
||||
timeView.setText(
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"%d:%02d:%02d",
|
||||
time / 3600,
|
||||
(time % 3600) / 60,
|
||||
(time % 60)));
|
||||
} else {
|
||||
timeView.setText(
|
||||
String.format(Locale.ROOT, "%02d:%02d", (time % 3600) / 60, (time % 60)));
|
||||
}
|
||||
handler.postDelayed(this, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
void onMinimizeClick(View view) {
|
||||
if (checkDrawOverlaysPermission(true)) {
|
||||
finish();
|
||||
} else {
|
||||
Toast.makeText(
|
||||
this,
|
||||
getString(R.string.rc_voip_float_window_not_allowed),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkDrawOverlaysPermission(boolean needOpenPermissionSetting) {
|
||||
if (RongCallPermissionUtil.checkFloatWindowPermission(this)) {
|
||||
checkingOverlaysPermission = false;
|
||||
return true;
|
||||
} else {
|
||||
if (needOpenPermissionSetting
|
||||
&& !checkingOverlaysPermission
|
||||
&& !notRemindRequestFloatWindowPermissionAgain) {
|
||||
RongCallPermissionUtil.requestFloatWindowNeedPermission(
|
||||
this,
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (DialogInterface.BUTTON_POSITIVE == which) {
|
||||
checkingOverlaysPermission = true;
|
||||
} else if (DialogInterface.BUTTON_NEGATIVE == which) {
|
||||
checkingOverlaysPermission = false;
|
||||
} else if (DialogInterface.BUTTON_NEUTRAL == which) {
|
||||
checkingOverlaysPermission = false;
|
||||
notRemindRequestFloatWindowPermissionAgain = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void createPowerManager() {
|
||||
if (powerManager == null) {
|
||||
powerManager = (PowerManager) getSystemService(POWER_SERVICE);
|
||||
wakeLock =
|
||||
powerManager.newWakeLock(
|
||||
PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK,
|
||||
TAG);
|
||||
wakeLock.setReferenceCounted(false);
|
||||
screenLock = powerManager.newWakeLock(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
|
||||
screenLock.setReferenceCounted(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected void createPickupDetector() {
|
||||
if (pickupDetector == null) {
|
||||
pickupDetector = new PickupDetector(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickupDetected(boolean isPickingUp) {
|
||||
if (screenLock == null) {
|
||||
RLog.d(TAG, "No PROXIMITY_SCREEN_OFF_WAKE_LOCK");
|
||||
return;
|
||||
}
|
||||
if (isPickingUp && !screenLock.isHeld()) {
|
||||
screenLock.acquire();
|
||||
}
|
||||
if (!isPickingUp && screenLock.isHeld()) {
|
||||
try {
|
||||
screenLock.setReferenceCounted(false);
|
||||
screenLock.release();
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (!RongCallPermissionUtil.checkPermissions(this, permissions)) {
|
||||
RongCallPermissionUtil.showRequestPermissionFailedAlter(
|
||||
this, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
/** outgoing (initView)incoming处注册 */
|
||||
public void regisHeadsetPlugReceiver() {
|
||||
if (BluetoothUtil.isSupportBluetooth()) {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction("android.intent.action.HEADSET_PLUG");
|
||||
intentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
|
||||
intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
|
||||
headsetPlugReceiver = new HeadsetPlugReceiver();
|
||||
registerReceiver(
|
||||
headsetPlugReceiver,
|
||||
intentFilter,
|
||||
this.getApplicationInfo().packageName + ".permission.RONG_ACCESS_RECEIVER",
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
/** onHangupBtnClick onDestory 处解绑 */
|
||||
public void unRegisterHeadsetplugReceiver() {
|
||||
if (headsetPlugReceiver != null) {
|
||||
unregisterReceiver(headsetPlugReceiver);
|
||||
headsetPlugReceiver = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置开始音视频参数配置信息<br>
|
||||
* 必须在{@link RongCallClient#startCall} 和 {@link RongCallClient#acceptCall(String)}之前设置 <br>
|
||||
*/
|
||||
public void audioVideoConfig() {
|
||||
// RongRTCConfig.Builder configBuilder = new RongRTCConfig.Builder();
|
||||
// configBuilder.setVideoResolution(RCRTCVideoResolution.RESOLUTION_480_640);
|
||||
// configBuilder.setVideoFps(RCRTCVideoFps.Fps_15);
|
||||
// configBuilder.setMaxRate(1000);
|
||||
// configBuilder.setMinRate(350);
|
||||
// /*
|
||||
// * 设置建立 Https 连接时,是否使用自签证书。
|
||||
// * 公有云用户无需调用此方法,私有云用户使用自签证书时调用此方法设置
|
||||
// */
|
||||
// // configBuilder.enableHttpsSelfCertificate(true);
|
||||
// RongCallClient.getInstance().setRTCConfig(configBuilder);
|
||||
|
||||
Builder builder =
|
||||
Builder.create()
|
||||
.setVideoResolution(RCRTCVideoResolution.RESOLUTION_480_640)
|
||||
.setVideoFps(RCRTCVideoFps.Fps_15)
|
||||
.setMaxRate(1000)
|
||||
.setMinRate(350);
|
||||
RongCallClient instance = RongCallClient.getInstance();
|
||||
if (instance != null) {
|
||||
instance.setVideoConfig(builder);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onHeadsetPlugUpdate(HeadsetInfo headsetInfo) {}
|
||||
|
||||
public class HeadsetPlugReceiver extends BroadcastReceiver {
|
||||
|
||||
private final String TAG = HeadsetPlugReceiver.class.getSimpleName();
|
||||
// 动态注册了监听有线耳机之后 默认会调用一次有限耳机拔出
|
||||
public boolean FIRST_HEADSET_PLUG_RECEIVER = false;
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
HeadsetInfo headsetInfo = null;
|
||||
if ("android.intent.action.HEADSET_PLUG".equals(action)) {
|
||||
int state = -1;
|
||||
if (FIRST_HEADSET_PLUG_RECEIVER) {
|
||||
if (intent.hasExtra("state")) {
|
||||
state = intent.getIntExtra("state", -1);
|
||||
}
|
||||
if (state == 1) {
|
||||
headsetInfo = new HeadsetInfo(true, HeadsetInfo.HeadsetType.WiredHeadset);
|
||||
} else if (state == 0) {
|
||||
headsetInfo = new HeadsetInfo(false, HeadsetInfo.HeadsetType.WiredHeadset);
|
||||
}
|
||||
} else {
|
||||
FIRST_HEADSET_PLUG_RECEIVER = true;
|
||||
}
|
||||
} else if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
|
||||
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
|
||||
switch (state) {
|
||||
case BluetoothProfile.STATE_DISCONNECTED:
|
||||
headsetInfo = new HeadsetInfo(false, HeadsetInfo.HeadsetType.BluetoothA2dp);
|
||||
break;
|
||||
case BluetoothProfile.STATE_CONNECTED:
|
||||
headsetInfo = new HeadsetInfo(true, HeadsetInfo.HeadsetType.BluetoothA2dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null != headsetInfo) { // onHandFreeButtonClick
|
||||
onHeadsetPlugUpdate(headsetInfo);
|
||||
} else {
|
||||
FinLog.e(TAG, "HeadsetPlugReceiver headsetInfo=null !");
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import io.rong.imkit.utils.language.RongConfigurationManager;
|
||||
|
||||
public class BaseNoActionBarActivity extends Activity {
|
||||
@Override
|
||||
protected void attachBaseContext(Context newBase) {
|
||||
Context newContext =
|
||||
RongConfigurationManager.getInstance().getConfigurationContext(newBase);
|
||||
super.attachBaseContext(newContext);
|
||||
}
|
||||
}
|
@ -0,0 +1,248 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import static io.rong.calllib.RongCallCommon.CallDisconnectedReason.HANGUP;
|
||||
import static io.rong.calllib.RongCallCommon.CallDisconnectedReason.OTHER_DEVICE_HAD_ACCEPTED;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.calllib.message.CallSTerminateMessage;
|
||||
import io.rong.imkit.conversation.messgelist.provider.BaseMessageItemProvider;
|
||||
import io.rong.imkit.model.UiMessage;
|
||||
import io.rong.imkit.widget.adapter.IViewProviderListener;
|
||||
import io.rong.imkit.widget.adapter.ViewHolder;
|
||||
import io.rong.imlib.model.Message;
|
||||
import io.rong.imlib.model.MessageContent;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class CallEndMessageItemProvider extends BaseMessageItemProvider<CallSTerminateMessage> {
|
||||
@Override
|
||||
protected io.rong.imkit.widget.adapter.ViewHolder onCreateMessageContentViewHolder(
|
||||
ViewGroup parent, int viewType) {
|
||||
View textView =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.rc_text_message_item, parent, false);
|
||||
return new ViewHolder(parent.getContext(), textView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindMessageContentViewHolder(
|
||||
ViewHolder holder,
|
||||
ViewHolder parentHolder,
|
||||
CallSTerminateMessage callSTerminateMessage,
|
||||
UiMessage uiMessage,
|
||||
int position,
|
||||
List<UiMessage> list,
|
||||
IViewProviderListener<UiMessage> listener) {
|
||||
Message message = uiMessage.getMessage();
|
||||
final TextView view = holder.getView(io.rong.imkit.R.id.rc_text);
|
||||
if (message.getMessageDirection() == Message.MessageDirection.SEND) {
|
||||
view.setBackgroundResource(R.drawable.rc_ic_bubble_right);
|
||||
} else {
|
||||
view.setBackgroundResource(R.drawable.rc_ic_bubble_left);
|
||||
}
|
||||
|
||||
RongCallCommon.CallMediaType mediaType = callSTerminateMessage.getMediaType();
|
||||
String direction = callSTerminateMessage.getDirection();
|
||||
Drawable drawable = null;
|
||||
|
||||
String msgContent = "";
|
||||
switch (callSTerminateMessage.getReason()) {
|
||||
case CANCEL:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mo_cancel);
|
||||
break;
|
||||
case REJECT:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mo_reject);
|
||||
break;
|
||||
case NO_RESPONSE:
|
||||
case BUSY_LINE:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mo_no_response);
|
||||
break;
|
||||
case REMOTE_BUSY_LINE:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mt_busy);
|
||||
break;
|
||||
case REMOTE_CANCEL:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mt_cancel);
|
||||
break;
|
||||
case REMOTE_REJECT:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mt_reject);
|
||||
break;
|
||||
case REMOTE_NO_RESPONSE:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_mt_no_response);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
case REMOTE_NETWORK_ERROR:
|
||||
case INIT_VIDEO_ERROR:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_call_interrupt);
|
||||
break;
|
||||
case OTHER_DEVICE_HAD_ACCEPTED:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_call_other);
|
||||
break;
|
||||
case SERVICE_NOT_OPENED:
|
||||
case REMOTE_ENGINE_UNSUPPORTED:
|
||||
msgContent = view.getResources().getString(R.string.rc_voip_engine_notfound);
|
||||
break;
|
||||
case REJECTED_BY_BLACKLIST:
|
||||
msgContent =
|
||||
view.getResources().getString(R.string.rc_voip_mo_rejected_by_blocklist);
|
||||
break;
|
||||
default:
|
||||
String mo_reject = view.getResources().getString(R.string.rc_voip_mo_reject);
|
||||
String mt_reject = view.getResources().getString(R.string.rc_voip_mt_reject);
|
||||
String extra = callSTerminateMessage.getExtra();
|
||||
String timeRegex = "([0-9]?[0-9]:)?([0-5][0-9]:)?([0-5][0-9])$";
|
||||
if (!TextUtils.isEmpty(extra)) {
|
||||
boolean val = extra.matches(timeRegex);
|
||||
if (val) {
|
||||
msgContent =
|
||||
view.getResources().getString(R.string.rc_voip_call_time_length);
|
||||
msgContent += extra;
|
||||
} else {
|
||||
msgContent =
|
||||
view.getResources().getString(R.string.rc_voip_call_time_length);
|
||||
}
|
||||
} else {
|
||||
msgContent =
|
||||
callSTerminateMessage.getReason() == HANGUP ? mo_reject : mt_reject;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
view.setText(msgContent);
|
||||
view.setCompoundDrawablePadding(15);
|
||||
|
||||
if (mediaType.equals(RongCallCommon.CallMediaType.VIDEO)) {
|
||||
if (direction != null && direction.equals("MO")) {
|
||||
drawable = view.getResources().getDrawable(R.drawable.rc_voip_video_right);
|
||||
drawable.setBounds(
|
||||
0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
view.setCompoundDrawablesRelative(null, null, drawable, null);
|
||||
view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_right));
|
||||
} else {
|
||||
drawable = view.getResources().getDrawable(R.drawable.rc_voip_video_left);
|
||||
drawable.setBounds(
|
||||
0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
view.setCompoundDrawablesRelative(drawable, null, null, null);
|
||||
view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_left));
|
||||
}
|
||||
} else {
|
||||
if (direction != null && direction.equals("MO")) {
|
||||
if (callSTerminateMessage.getReason().equals(HANGUP)
|
||||
|| callSTerminateMessage
|
||||
.getReason()
|
||||
.equals(RongCallCommon.CallDisconnectedReason.REMOTE_HANGUP)) {
|
||||
drawable =
|
||||
view.getResources()
|
||||
.getDrawable(R.drawable.rc_voip_audio_right_connected);
|
||||
} else {
|
||||
drawable =
|
||||
view.getResources().getDrawable(R.drawable.rc_voip_audio_right_cancel);
|
||||
}
|
||||
drawable.setBounds(
|
||||
0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
view.setCompoundDrawablesRelative(null, null, drawable, null);
|
||||
view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_right));
|
||||
} else {
|
||||
if (callSTerminateMessage.getReason().equals(HANGUP)
|
||||
|| callSTerminateMessage
|
||||
.getReason()
|
||||
.equals(RongCallCommon.CallDisconnectedReason.REMOTE_HANGUP)) {
|
||||
drawable =
|
||||
view.getResources()
|
||||
.getDrawable(R.drawable.rc_voip_audio_left_connected);
|
||||
} else {
|
||||
drawable =
|
||||
view.getResources().getDrawable(R.drawable.rc_voip_audio_left_cancel);
|
||||
}
|
||||
drawable.setBounds(
|
||||
0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
view.setCompoundDrawablesRelative(drawable, null, null, null);
|
||||
view.setTextColor(view.getResources().getColor(R.color.rc_voip_color_left));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onItemClick(
|
||||
ViewHolder holder,
|
||||
CallSTerminateMessage callSTerminateMessage,
|
||||
UiMessage uiMessage,
|
||||
int position,
|
||||
List<UiMessage> list,
|
||||
IViewProviderListener<UiMessage> listener) {
|
||||
if (callSTerminateMessage.getReason() == OTHER_DEVICE_HAD_ACCEPTED) {
|
||||
return true;
|
||||
}
|
||||
Context context = holder.getContext();
|
||||
RongCallSession profile = RongCallClient.getInstance().getCallSession();
|
||||
if (profile != null && profile.getActiveTime() > 0) {
|
||||
if (profile.getMediaType() == RongCallCommon.CallMediaType.AUDIO) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.rc_voip_call_audio_start_fail),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.rc_voip_call_video_start_fail),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (!CallKitUtils.isNetworkAvailable(context)) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.rc_voip_call_network_error),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
RongCallCommon.CallMediaType mediaType = callSTerminateMessage.getMediaType();
|
||||
String action = null;
|
||||
if (mediaType.equals(RongCallCommon.CallMediaType.VIDEO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEVIDEO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEAUDIO;
|
||||
}
|
||||
Intent intent = new Intent(action);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.putExtra(
|
||||
"conversationType",
|
||||
uiMessage.getMessage().getConversationType().getName().toLowerCase(Locale.US));
|
||||
intent.putExtra("targetId", uiMessage.getMessage().getTargetId());
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
context.startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMessageViewType(MessageContent messageContent) {
|
||||
return messageContent instanceof CallSTerminateMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spannable getSummarySpannable(
|
||||
Context context, CallSTerminateMessage callSTerminateMessage) {
|
||||
RongCallCommon.CallMediaType mediaType = callSTerminateMessage.getMediaType();
|
||||
if (mediaType.equals(RongCallCommon.CallMediaType.AUDIO)) {
|
||||
return new SpannableString(context.getString(R.string.rc_voip_message_audio));
|
||||
} else {
|
||||
return new SpannableString(context.getString(R.string.rc_voip_message_video));
|
||||
}
|
||||
}
|
||||
}
|
1216
callkit/src/main/java/io/rong/callkit/CallFloatBoxView.java
Normal file
124
callkit/src/main/java/io/rong/callkit/CallForegroundService.java
Normal file
@ -0,0 +1,124 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.IBinder;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.Nullable;
|
||||
import io.rong.callkit.util.CallRingingUtil;
|
||||
import io.rong.push.notification.RongNotificationInterface;
|
||||
import io.rong.push.notification.RongNotificationInterface.SoundType;
|
||||
|
||||
public class CallForegroundService extends Service {
|
||||
|
||||
private static final String TAG = "CallForegroundService";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
}
|
||||
|
||||
private void showNotification(
|
||||
String title, String content, PendingIntent pendingIntent, int notificationId) {
|
||||
Log.d(TAG, "showNotification: ");
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||
String channelId = CallRingingUtil.getInstance().getNotificationChannelId();
|
||||
Notification notification =
|
||||
RongNotificationInterface.createNotification(
|
||||
getApplicationContext(),
|
||||
title,
|
||||
pendingIntent,
|
||||
content,
|
||||
SoundType.SILENT,
|
||||
channelId);
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
int importance = NotificationManager.IMPORTANCE_LOW;
|
||||
String channelName = CallRingingUtil.getInstance().getNotificationChannelName(this);
|
||||
NotificationChannel notificationChannel =
|
||||
new NotificationChannel(channelId, channelName, importance);
|
||||
notificationChannel.enableLights(false);
|
||||
notificationChannel.setLightColor(Color.GREEN);
|
||||
notificationChannel.enableVibration(false);
|
||||
notificationChannel.setSound(null, null);
|
||||
notificationManager.createNotificationChannel(notificationChannel);
|
||||
}
|
||||
notification.defaults = Notification.DEFAULT_ALL;
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.Q) {
|
||||
startForeground(
|
||||
notificationId,
|
||||
notification,
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
|
||||
| ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA);
|
||||
} else {
|
||||
startForeground(notificationId, notification);
|
||||
}
|
||||
Log.d(TAG, "showNotification: startForeground");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
stopForeground(true);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
String action = intent.getStringExtra("action");
|
||||
String title = intent.getStringExtra("title");
|
||||
String content = intent.getStringExtra("content");
|
||||
ResolveInfo info =
|
||||
getPackageManager()
|
||||
.resolveActivity(new Intent(action), PackageManager.MATCH_DEFAULT_ONLY);
|
||||
ActivityInfo activityInfo;
|
||||
if (info == null || (activityInfo = info.activityInfo) == null) {
|
||||
Log.e(TAG, "onStartCommand: ResolveInfo is null! action=" + action);
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
Log.d(TAG, "onStartCommand: " + activityInfo.name);
|
||||
Intent launched = new Intent(action);
|
||||
launched.setClassName(activityInfo.packageName, activityInfo.name);
|
||||
launched.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
launched.putExtra("floatbox", intent.getBundleExtra("floatbox"));
|
||||
launched.putExtra("callAction", RongCallAction.ACTION_RESUME_CALL.getName());
|
||||
PendingIntent pendingIntent;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
this,
|
||||
1000,
|
||||
launched,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
this, 1000, launched, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
try {
|
||||
showNotification(title, content, pendingIntent, BaseCallActivity.CALL_NOTIFICATION_ID);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "showNotification: ", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
}
|
72
callkit/src/main/java/io/rong/callkit/CallOptionMenu.java
Normal file
@ -0,0 +1,72 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.TextView;
|
||||
|
||||
/** Created by mamingyang on 2018/3/19. */
|
||||
public class CallOptionMenu extends PopupWindow {
|
||||
private View.OnClickListener onItemClickListener;
|
||||
private TextView tv_right_text;
|
||||
private LinearLayout layoutAdd;
|
||||
private LinearLayout layoutWhiteBoard;
|
||||
private LinearLayout layoutHandUp;
|
||||
|
||||
public CallOptionMenu(Context context) {
|
||||
super(context);
|
||||
LayoutInflater inflater =
|
||||
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
View content = inflater.inflate(R.layout.rc_voip_pop_menu, null);
|
||||
setContentView(content);
|
||||
setWidth(LayoutParams.WRAP_CONTENT);
|
||||
setHeight(LayoutParams.WRAP_CONTENT);
|
||||
layoutAdd = (LinearLayout) content.findViewById(R.id.voipItemAdd);
|
||||
layoutAdd.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onItemClickListener != null) onItemClickListener.onClick(v);
|
||||
}
|
||||
});
|
||||
tv_right_text = content.findViewById(R.id.tv_right_text);
|
||||
tv_right_text.setText(context.getString(R.string.rc_voip_add_member));
|
||||
layoutWhiteBoard = (LinearLayout) content.findViewById(R.id.voipItemWhiteboard);
|
||||
layoutWhiteBoard.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onItemClickListener != null) onItemClickListener.onClick(v);
|
||||
}
|
||||
});
|
||||
|
||||
layoutHandUp = (LinearLayout) content.findViewById(R.id.voipItemHandup);
|
||||
layoutHandUp.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onItemClickListener != null) onItemClickListener.onClick(v);
|
||||
}
|
||||
});
|
||||
|
||||
setBackgroundDrawable(context.getResources().getDrawable(R.drawable.rc_voip_menu_bg));
|
||||
setOutsideTouchable(true);
|
||||
setFocusable(true);
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(View.OnClickListener onItemClickListener) {
|
||||
this.onItemClickListener = onItemClickListener;
|
||||
}
|
||||
|
||||
public void setHandUpvisibility(boolean isSeen) {
|
||||
if (layoutHandUp != null) {
|
||||
if (!isSeen) layoutHandUp.setVisibility(View.GONE);
|
||||
else {
|
||||
layoutHandUp.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
177
callkit/src/main/java/io/rong/callkit/CallPromptDialog.java
Normal file
@ -0,0 +1,177 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CallPromptDialog extends AlertDialog {
|
||||
private Context mContext;
|
||||
private OnPromptButtonClickedListener mPromptButtonClickedListener;
|
||||
private String mTitle;
|
||||
private String mPositiveButton;
|
||||
private String mNegativeButton;
|
||||
private String mMessage;
|
||||
private int mLayoutResId;
|
||||
private boolean disableCancel;
|
||||
private int positiveTxtColor = 0;
|
||||
private int negativeTxtColor = 0;
|
||||
|
||||
public static CallPromptDialog newInstance(
|
||||
final Context context, String title, String message) {
|
||||
return new CallPromptDialog(context, title, message);
|
||||
}
|
||||
|
||||
public static CallPromptDialog newInstance(final Context context, String message) {
|
||||
return new CallPromptDialog(context, message);
|
||||
}
|
||||
|
||||
public static CallPromptDialog newInstance(
|
||||
final Context context, String title, String message, String positiveButton) {
|
||||
return new CallPromptDialog(context, title, message, positiveButton);
|
||||
}
|
||||
|
||||
public static CallPromptDialog newInstance(
|
||||
final Context context,
|
||||
String title,
|
||||
String message,
|
||||
String positiveButton,
|
||||
String negativeButton) {
|
||||
return new CallPromptDialog(context, title, message, positiveButton, negativeButton);
|
||||
}
|
||||
|
||||
public CallPromptDialog(
|
||||
final Context context,
|
||||
String title,
|
||||
String message,
|
||||
String positiveButton,
|
||||
String negativeButton) {
|
||||
this(context, title, message, positiveButton);
|
||||
this.mNegativeButton = negativeButton;
|
||||
}
|
||||
|
||||
public CallPromptDialog(
|
||||
final Context context, String title, String message, String positiveButton) {
|
||||
this(context, title, message);
|
||||
mPositiveButton = positiveButton;
|
||||
}
|
||||
|
||||
public CallPromptDialog(final Context context, String title, String message) {
|
||||
super(context);
|
||||
mLayoutResId = R.layout.rc_voip_dialog_popup_prompt;
|
||||
mContext = context;
|
||||
mTitle = title;
|
||||
mMessage = message;
|
||||
}
|
||||
|
||||
public CallPromptDialog(final Context context, String message) {
|
||||
this(context, "", message);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
LayoutInflater inflater =
|
||||
(LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
final View view = inflater.inflate(mLayoutResId, null);
|
||||
TextView txtViewTitle = (TextView) view.findViewById(io.rong.imkit.R.id.popup_dialog_title);
|
||||
TextView txtViewMessage =
|
||||
(TextView) view.findViewById(io.rong.imkit.R.id.popup_dialog_message);
|
||||
TextView txtViewOK =
|
||||
(TextView) view.findViewById(io.rong.imkit.R.id.popup_dialog_button_ok);
|
||||
TextView txtViewCancel =
|
||||
(TextView) view.findViewById(io.rong.imkit.R.id.popup_dialog_button_cancel);
|
||||
if (disableCancel) txtViewCancel.setVisibility(View.GONE);
|
||||
if (positiveTxtColor != 0) {
|
||||
txtViewOK.setTextColor(positiveTxtColor);
|
||||
}
|
||||
if (negativeTxtColor != 0) {
|
||||
txtViewCancel.setTextColor(negativeTxtColor);
|
||||
}
|
||||
txtViewOK.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPromptButtonClickedListener != null) {
|
||||
mPromptButtonClickedListener.onPositiveButtonClicked();
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
txtViewCancel.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mPromptButtonClickedListener != null) {
|
||||
mPromptButtonClickedListener.onNegativeButtonClicked();
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
if (!TextUtils.isEmpty(mTitle)) {
|
||||
txtViewTitle.setText(mTitle);
|
||||
txtViewTitle.setVisibility(View.VISIBLE);
|
||||
}
|
||||
if (!TextUtils.isEmpty(mPositiveButton)) {
|
||||
txtViewOK.setText(mPositiveButton);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mNegativeButton)) {
|
||||
txtViewCancel.setText(mNegativeButton);
|
||||
txtViewCancel.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
txtViewMessage.setText(mMessage);
|
||||
|
||||
setContentView(view);
|
||||
WindowManager.LayoutParams layoutParams = getWindow().getAttributes();
|
||||
layoutParams.width = gePopupWidth();
|
||||
layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
getWindow().setAttributes(layoutParams);
|
||||
}
|
||||
|
||||
public void disableCancel() {
|
||||
disableCancel = true;
|
||||
}
|
||||
|
||||
public CallPromptDialog setPromptButtonClickedListener(
|
||||
OnPromptButtonClickedListener buttonClickedListener) {
|
||||
this.mPromptButtonClickedListener = buttonClickedListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CallPromptDialog setLayoutRes(int resId) {
|
||||
this.mLayoutResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setPositiveTextColor(int color) {
|
||||
positiveTxtColor = color;
|
||||
}
|
||||
|
||||
public void setNegativeTextColor(int color) {
|
||||
negativeTxtColor = color;
|
||||
}
|
||||
|
||||
public interface OnPromptButtonClickedListener {
|
||||
void onPositiveButtonClicked();
|
||||
|
||||
void onNegativeButtonClicked();
|
||||
}
|
||||
|
||||
private int gePopupWidth() {
|
||||
int distanceToBorder =
|
||||
(int) mContext.getResources().getDimension(R.dimen.callkit_dimen_size_40);
|
||||
return getScreenWidth() - 2 * (distanceToBorder);
|
||||
}
|
||||
|
||||
private int getScreenWidth() {
|
||||
return ((WindowManager) (mContext.getSystemService(Context.WINDOW_SERVICE)))
|
||||
.getDefaultDisplay()
|
||||
.getWidth();
|
||||
}
|
||||
}
|
@ -0,0 +1,820 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import io.rong.callkit.util.CallKitSearchBarListener;
|
||||
import io.rong.callkit.util.CallKitSearchBarView;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.callkit.util.CallSelectMemberSerializable;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.feature.mention.RongMentionManager;
|
||||
import io.rong.imkit.userinfo.RongUserInfoManager;
|
||||
import io.rong.imkit.userinfo.model.GroupUserInfo;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import io.rong.imlib.model.Group;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class CallSelectMemberActivity extends BaseNoActionBarActivity
|
||||
implements RongUserInfoManager.UserDataObserver {
|
||||
private static final String TAG = "CallSelectMemberActivity";
|
||||
public static final String DISCONNECT_ACTION = "call_disconnect";
|
||||
ArrayList<String> selectedMember;
|
||||
private boolean isFirstDialog = true;
|
||||
/** 已经选择的观察者列表 */
|
||||
private ArrayList<String> observerMember;
|
||||
|
||||
TextView txtvStart, callkit_conference_selected_number;
|
||||
ListAdapter mAdapter;
|
||||
ListView mList;
|
||||
RongCallCommon.CallMediaType mMediaType;
|
||||
private Conversation.ConversationType conversationType;
|
||||
private EditText searchView;
|
||||
private HashMap<String, String> tempNickmembers = new HashMap<>();
|
||||
|
||||
private ArrayList<UserInfo> searchMembers = new ArrayList<>();
|
||||
private ArrayList<String> invitedMembers;
|
||||
private ArrayList<UserInfo> tempMembers = new ArrayList<>();
|
||||
|
||||
private ArrayList<String> allObserver = null; // 保存当前通话中从多人音/视频传递过来的观察者列表
|
||||
|
||||
private String groupId;
|
||||
private String callId;
|
||||
private RelativeLayout rlSearchTop;
|
||||
private RelativeLayout rlActionBar;
|
||||
private ImageView ivBack;
|
||||
private CallKitSearchBarView searchBar;
|
||||
/**
|
||||
* true:只能选择n个人同时进行音视频通话,>n选择无效; false:>n个人同时音视频通话之后,其他人视为观察者加入到本次通话中; n :NORMAL_VIDEO_NUMBER 和
|
||||
* NORMAL_AUDIO_NUMBER
|
||||
*/
|
||||
private boolean ctrlTag = true;
|
||||
|
||||
private static final int NORMAL_VIDEO_NUMBER = 7;
|
||||
private static final int NORMAL_AUDIO_NUMBER = 20;
|
||||
private ArrayList<UserInfo> userInfoArrayList = new ArrayList<>();
|
||||
/** 用于存储获取不到userInfo的用户在列表中的位置 */
|
||||
private HashMap<String, Integer> userInfoIndex = new HashMap<>();
|
||||
|
||||
private final Object listLock = new Object();
|
||||
//
|
||||
private Handler uiHandler =
|
||||
new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
if (msg.what == 2) {
|
||||
Bundle bundle = msg.getData();
|
||||
if (bundle != null) {
|
||||
CallSelectMemberSerializable callSelectMemberSerializable =
|
||||
(CallSelectMemberSerializable)
|
||||
bundle.getSerializable(
|
||||
CALLSELECTMEMBERSERIALIZABLE_KEY);
|
||||
if (callSelectMemberSerializable != null) {
|
||||
tempNickmembers = callSelectMemberSerializable.getHashMap();
|
||||
}
|
||||
}
|
||||
if (userInfoArrayList.isEmpty()
|
||||
&& invitedMembers != null
|
||||
&& invitedMembers.size() > 0) {
|
||||
String tmpUserID = "";
|
||||
for (int i = 0; i < invitedMembers.size(); i++) {
|
||||
tmpUserID = invitedMembers.get(i);
|
||||
fillInUserInfoList(tmpUserID, i);
|
||||
}
|
||||
}
|
||||
RLog.i(TAG, "setAdapter");
|
||||
mAdapter = new ListAdapter(userInfoArrayList, invitedMembers);
|
||||
mList.setAdapter(mAdapter);
|
||||
callkit_conference_selected_number.setText(
|
||||
getString(
|
||||
R.string.callkit_selected_contacts_count,
|
||||
getTotalSelectedNumber()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private void fillInUserInfoList(String userid, int index) {
|
||||
synchronized (listLock) {
|
||||
if (!TextUtils.isEmpty(userid)) {
|
||||
if (getUserInfo(userid) == null) {
|
||||
userInfoIndex.put(userid, index);
|
||||
}
|
||||
// 当获取到的userInfo为空时,记录下当前index,并给userInfoArrayList增加一个空元素,等到异步结果(onHeadsetPlugUpdate)回来后,根据index,把正确的数据插入回原来位置
|
||||
userInfoArrayList.add(getUserInfo(userid));
|
||||
|
||||
} else {
|
||||
RLog.e(TAG, "uiHandler->userid null.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Handler mHandler;
|
||||
private static final String GROUPMEMBERSRESULT_KEY = "GROUPMEMBERSRESULTKEY";
|
||||
private static final String CALLSELECTMEMBERSERIALIZABLE_KEY =
|
||||
"CALLSELECTMEMBERSERIALIZABLEKEY";
|
||||
|
||||
private BroadcastReceiver disconnectBroadcastReceiver =
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (TextUtils.equals(intent.getAction(), DISCONNECT_ACTION)) {
|
||||
if (!isFinishing()) {
|
||||
setActivityResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
getWindow()
|
||||
.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
setContentView(R.layout.activity_call_select_member2);
|
||||
RongUserInfoManager.getInstance().addUserDataObserver(this);
|
||||
|
||||
initTopBar();
|
||||
|
||||
selectedMember = new ArrayList<>();
|
||||
observerMember = new ArrayList<>();
|
||||
|
||||
Intent intent = getIntent();
|
||||
int type = intent.getIntExtra("mediaType", RongCallCommon.CallMediaType.VIDEO.getValue());
|
||||
mMediaType = RongCallCommon.CallMediaType.valueOf(type);
|
||||
int conType = intent.getIntExtra("conversationType", 0);
|
||||
conversationType = Conversation.ConversationType.setValue(conType);
|
||||
invitedMembers = intent.getStringArrayListExtra("invitedMembers");
|
||||
groupId = intent.getStringExtra("groupId");
|
||||
callId = intent.getStringExtra("callId");
|
||||
allObserver = intent.getStringArrayListExtra("allObserver");
|
||||
|
||||
ArrayList<String> list = intent.getStringArrayListExtra("allMembers");
|
||||
if (list != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
fillInUserInfoList(list.get(i), i);
|
||||
}
|
||||
}
|
||||
HandlerThread handlerThread = new HandlerThread(TAG);
|
||||
handlerThread.start();
|
||||
mHandler =
|
||||
new Handler(handlerThread.getLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
super.handleMessage(msg);
|
||||
String key = (String) msg.obj;
|
||||
if (GROUPMEMBERSRESULT_KEY.equals(key)) {
|
||||
Bundle bundle = msg.getData();
|
||||
HashMap<String, String> hashMap = new HashMap<>();
|
||||
if (bundle != null) {
|
||||
ArrayList<UserInfo> arrayList =
|
||||
bundle.getParcelableArrayList(GROUPMEMBERSRESULT_KEY);
|
||||
Conversation.ConversationType conversationType =
|
||||
Conversation.ConversationType.setValue(
|
||||
bundle.getInt("conversationType"));
|
||||
if (arrayList != null) {
|
||||
RLog.i(TAG, "onGetGroupMembersResult : " + arrayList.size());
|
||||
UserInfo userInfo = null;
|
||||
String userNickName = "";
|
||||
GroupUserInfo groupUserInfo = null;
|
||||
/** 转换昵称** */
|
||||
for (int i = 0; i < arrayList.size(); i++) {
|
||||
userInfo = arrayList.get(i);
|
||||
if (userInfo != null
|
||||
&& !TextUtils.isEmpty(userInfo.getUserId())) {
|
||||
if (conversationType != null
|
||||
&& conversationType.equals(
|
||||
Conversation.ConversationType.GROUP)) {
|
||||
groupUserInfo =
|
||||
RongUserInfoManager.getInstance()
|
||||
.getGroupUserInfo(
|
||||
groupId,
|
||||
userInfo.getUserId());
|
||||
if (groupUserInfo != null
|
||||
&& !TextUtils.isEmpty(
|
||||
groupUserInfo.getNickname())) {
|
||||
userNickName = groupUserInfo.getNickname();
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(userNickName)) {
|
||||
userNickName = userInfo.getName();
|
||||
} else {
|
||||
userInfo.setName(userNickName);
|
||||
}
|
||||
hashMap.put(userInfo.getUserId(), userNickName);
|
||||
userNickName = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CallSelectMemberSerializable callSelectMemberSerializable =
|
||||
new CallSelectMemberSerializable(hashMap);
|
||||
Message message = new Message();
|
||||
message.what = 2;
|
||||
Bundle bundle1 = new Bundle();
|
||||
bundle1.putSerializable(
|
||||
CALLSELECTMEMBERSERIALIZABLE_KEY, callSelectMemberSerializable);
|
||||
message.setData(bundle1);
|
||||
uiHandler.sendMessage(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RongCallKit.GroupMembersProvider provider = RongCallKit.getGroupMemberProvider();
|
||||
if (TextUtils.isEmpty(groupId)) {
|
||||
return;
|
||||
}
|
||||
if (provider != null) {
|
||||
provider.getMemberList(
|
||||
groupId,
|
||||
new RongCallKit.OnGroupMembersResult() {
|
||||
@Override
|
||||
public void onGotMemberList(ArrayList<String> members) {
|
||||
for (int i = 0; i < members.size(); i++) {
|
||||
fillInUserInfoList(members.get(i), i);
|
||||
}
|
||||
Message message = new Message();
|
||||
message.obj = GROUPMEMBERSRESULT_KEY;
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(
|
||||
GROUPMEMBERSRESULT_KEY, userInfoArrayList);
|
||||
bundle.putInt("conversationType", conversationType.getValue());
|
||||
message.setData(bundle);
|
||||
mHandler.sendMessage(message);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (RongMentionManager.getInstance().getGroupMembersProvider() != null) {
|
||||
RongMentionManager.getInstance()
|
||||
.getGroupMembersProvider()
|
||||
.getGroupMembers(
|
||||
groupId,
|
||||
new RongMentionManager.IGroupMemberCallback() {
|
||||
@Override
|
||||
public void onGetGroupMembersResult(List<UserInfo> userInfos) {
|
||||
if (userInfos == null || userInfos.size() == 0) {
|
||||
RLog.e(
|
||||
TAG,
|
||||
"onGetGroupMembersResult userInfos is null!");
|
||||
return;
|
||||
}
|
||||
userInfoArrayList.addAll(userInfos);
|
||||
|
||||
Message message = new Message();
|
||||
message.obj = GROUPMEMBERSRESULT_KEY;
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(
|
||||
GROUPMEMBERSRESULT_KEY, userInfoArrayList);
|
||||
bundle.putInt(
|
||||
"conversationType", conversationType.getValue());
|
||||
message.setData(bundle);
|
||||
mHandler.sendMessage(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
callkit_conference_selected_number =
|
||||
(TextView) findViewById(R.id.callkit_conference_selected_number);
|
||||
txtvStart = (TextView) findViewById(R.id.callkit_btn_ok);
|
||||
txtvStart.setText(getString(R.string.callkit_voip_ok));
|
||||
txtvStart.setEnabled(false);
|
||||
txtvStart.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setActivityResult(false);
|
||||
}
|
||||
});
|
||||
|
||||
mList = (ListView) findViewById(R.id.calkit_list_view_select_member);
|
||||
mList.setOnItemClickListener(adapterOnItemClickListener);
|
||||
rlSearchTop = (RelativeLayout) findViewById(R.id.rl_search_top);
|
||||
ivBack = (ImageView) findViewById(R.id.iv_back);
|
||||
searchBar = (CallKitSearchBarView) findViewById(R.id.search_bar);
|
||||
if (CallKitUtils.findConfigurationLanguage(CallSelectMemberActivity.this, "ar")) {
|
||||
rlSearchTop.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
|
||||
searchBar.setLayoutDirection(View.LAYOUT_DIRECTION_RTL);
|
||||
}
|
||||
|
||||
ivBack.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
rlSearchTop.setVisibility(View.GONE);
|
||||
rlActionBar.setVisibility(View.VISIBLE);
|
||||
mAdapter.setAllMembers(userInfoArrayList);
|
||||
mAdapter.notifyDataSetChanged();
|
||||
CallKitUtils.closeKeyBoard(CallSelectMemberActivity.this, null);
|
||||
}
|
||||
});
|
||||
searchBar.setSearchBarListener(
|
||||
new CallKitSearchBarListener() {
|
||||
@Override
|
||||
public void onSearchStart(String content) {
|
||||
if (userInfoArrayList != null && userInfoArrayList.size() > 0) {
|
||||
startSearchMember(content);
|
||||
} else {
|
||||
Toast.makeText(
|
||||
CallSelectMemberActivity.this,
|
||||
getString(R.string.rc_voip_search_no_member),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSoftSearchKeyClick() {}
|
||||
|
||||
@Override
|
||||
public void onClearButtonClick() {
|
||||
if (invitedMembers != null) {
|
||||
mAdapter = new ListAdapter(userInfoArrayList, invitedMembers);
|
||||
mList.setAdapter(mAdapter);
|
||||
mList.setOnItemClickListener(adapterOnItemClickListener);
|
||||
}
|
||||
}
|
||||
});
|
||||
registerDisconnectBroadcastReceiver();
|
||||
}
|
||||
|
||||
private void registerDisconnectBroadcastReceiver() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(DISCONNECT_ACTION);
|
||||
registerReceiver(
|
||||
disconnectBroadcastReceiver,
|
||||
intentFilter,
|
||||
this.getApplicationInfo().packageName + ".permission.RONG_ACCESS_RECEIVER",
|
||||
null);
|
||||
}
|
||||
|
||||
private void startSearchMember(String searchEditContent) {
|
||||
try {
|
||||
searchMembers.clear();
|
||||
tempMembers.clear();
|
||||
if (!TextUtils.isEmpty(searchEditContent)) {
|
||||
for (UserInfo info : userInfoArrayList) {
|
||||
if (info != null && !TextUtils.isEmpty(info.getUserId())) {
|
||||
if (((String) tempNickmembers.get(info.getUserId()))
|
||||
.indexOf(searchEditContent)
|
||||
!= -1) {
|
||||
tempMembers.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
tempMembers.addAll(userInfoArrayList);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
tempMembers.addAll(userInfoArrayList);
|
||||
}
|
||||
// closeKeyBoard(this, searchBar);
|
||||
setData();
|
||||
}
|
||||
|
||||
private void setData() {
|
||||
if (null != tempMembers) {
|
||||
ListAdapter adapter = new ListAdapter(tempMembers, invitedMembers);
|
||||
mList.setAdapter(adapter);
|
||||
mList.setOnItemClickListener(adapterOnItemClickListener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
RongUserInfoManager.getInstance().removeUserDataObserver(this);
|
||||
unregisterReceiver(disconnectBroadcastReceiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserUpdate(UserInfo userInfo) {
|
||||
if (mList != null && userInfo != null) {
|
||||
if (userInfo.getUserId() == null
|
||||
|| !userInfoIndex.containsKey(userInfo.getUserId())
|
||||
|| userInfoIndex.get(userInfo.getUserId()) == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (listLock) {
|
||||
int index = userInfoIndex.get(userInfo.getUserId());
|
||||
if (index >= 0 && index < userInfoArrayList.size()) {
|
||||
userInfoIndex.remove(userInfo.getUserId());
|
||||
userInfoArrayList.remove(index);
|
||||
userInfoArrayList.add(index, userInfo);
|
||||
}
|
||||
Message message = new Message();
|
||||
message.obj = GROUPMEMBERSRESULT_KEY;
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelableArrayList(GROUPMEMBERSRESULT_KEY, userInfoArrayList);
|
||||
bundle.putInt("conversationType", conversationType.getValue());
|
||||
message.setData(bundle);
|
||||
mHandler.sendMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupUpdate(Group group) {}
|
||||
|
||||
@Override
|
||||
public void onGroupUserInfoUpdate(GroupUserInfo groupUserInfo) {}
|
||||
|
||||
class ListAdapter extends BaseAdapter {
|
||||
List<UserInfo> mallMembers;
|
||||
List<String> invitedMembers;
|
||||
|
||||
public ListAdapter(List<UserInfo> allMembers, List<String> invitedMembers) {
|
||||
this.mallMembers = allMembers;
|
||||
this.invitedMembers = invitedMembers;
|
||||
}
|
||||
|
||||
public void setAllMembers(List<UserInfo> allMembers) {
|
||||
this.mallMembers = allMembers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mallMembers.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mallMembers.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
holder = new ViewHolder();
|
||||
convertView =
|
||||
LayoutInflater.from(CallSelectMemberActivity.this)
|
||||
.inflate(R.layout.rc_voip_listitem_select_member, null);
|
||||
holder.checkbox = (ImageView) convertView.findViewById(R.id.rc_checkbox);
|
||||
holder.portrait = (ImageView) convertView.findViewById(R.id.rc_user_portrait);
|
||||
holder.name = (TextView) convertView.findViewById(R.id.rc_user_name);
|
||||
convertView.setTag(holder);
|
||||
}
|
||||
|
||||
UserInfo mUserInfo = mallMembers.get(position);
|
||||
if (mUserInfo == null || TextUtils.isEmpty(mUserInfo.getUserId())) {
|
||||
// userInfo为空前,把所有值都设置为默认
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
holder.checkbox.setImageResource(R.drawable.rc_voip_checkbox);
|
||||
holder.checkbox.setClickable(false);
|
||||
holder.checkbox.setEnabled(true);
|
||||
holder.name.setText("");
|
||||
RongCallKit.getKitImageEngine()
|
||||
.loadPortrait(
|
||||
getApplicationContext(),
|
||||
null,
|
||||
R.drawable.rc_default_portrait,
|
||||
holder.portrait);
|
||||
// Glide.with(holder.portrait)
|
||||
// .load(R.drawable.rc_default_portrait)
|
||||
// .apply(RequestOptions.bitmapTransform(new CenterCrop()))
|
||||
// .into(holder.portrait);
|
||||
holder.checkbox.setTag("");
|
||||
return convertView;
|
||||
}
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
holder.checkbox.setTag(mUserInfo.getUserId());
|
||||
if (invitedMembers.contains(mUserInfo.getUserId())) {
|
||||
holder.checkbox.setClickable(false);
|
||||
holder.checkbox.setEnabled(false);
|
||||
holder.checkbox.setImageResource(R.drawable.rc_voip_icon_checkbox_checked);
|
||||
} else {
|
||||
if (selectedMember.contains(mUserInfo.getUserId())) {
|
||||
holder.checkbox.setImageResource(R.drawable.rc_voip_checkbox);
|
||||
holder.checkbox.setSelected(true);
|
||||
} else {
|
||||
holder.checkbox.setImageResource(R.drawable.rc_voip_checkbox);
|
||||
holder.checkbox.setSelected(false);
|
||||
}
|
||||
holder.checkbox.setClickable(false);
|
||||
holder.checkbox.setEnabled(true);
|
||||
}
|
||||
|
||||
String displayName = "";
|
||||
if (conversationType != null
|
||||
&& conversationType.equals(Conversation.ConversationType.GROUP)) {
|
||||
GroupUserInfo groupUserInfo =
|
||||
RongUserInfoManager.getInstance()
|
||||
.getGroupUserInfo(groupId, mUserInfo.getUserId());
|
||||
if (groupUserInfo != null && !TextUtils.isEmpty(groupUserInfo.getNickname())) {
|
||||
displayName = groupUserInfo.getNickname();
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(displayName)) {
|
||||
holder.name.setText(mUserInfo.getName());
|
||||
} else {
|
||||
holder.name.setText(displayName);
|
||||
}
|
||||
RongCallKit.getKitImageEngine()
|
||||
.loadPortrait(
|
||||
getBaseContext(),
|
||||
mUserInfo.getPortraitUri(),
|
||||
R.drawable.rc_default_portrait,
|
||||
holder.portrait);
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结束页面前设置值
|
||||
*
|
||||
* @param val 是否是远端挂断,如果是则关闭该页面
|
||||
*/
|
||||
private void setActivityResult(boolean val) {
|
||||
RongCallSession profile = RongCallClient.getInstance().getCallSession();
|
||||
if (profile != null) {
|
||||
// callid由多聊页面传入,如果先启动群组选择人员页面再启动多聊页面,不允许启动多聊页面
|
||||
if (null != profile.getCallId() && (!profile.getCallId().equals(callId))) {
|
||||
String msg =
|
||||
profile.getMediaType() == RongCallCommon.CallMediaType.AUDIO
|
||||
? getString(io.rong.callkit.R.string.rc_voip_call_audio_start_fail)
|
||||
: getString(io.rong.callkit.R.string.rc_voip_call_video_start_fail);
|
||||
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
CallKitUtils.closeKeyBoard(CallSelectMemberActivity.this, null);
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("remote_hangup", val);
|
||||
intent.setPackage(getPackageName());
|
||||
intent.putStringArrayListExtra("invited", selectedMember);
|
||||
intent.putStringArrayListExtra("observers", observerMember);
|
||||
setResult(RESULT_OK, intent);
|
||||
CallSelectMemberActivity.this.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
class ViewHolder {
|
||||
|
||||
ImageView checkbox;
|
||||
ImageView portrait;
|
||||
TextView name;
|
||||
}
|
||||
|
||||
public void initTopBar() {
|
||||
rlActionBar = (RelativeLayout) findViewById(R.id.rl_actionbar);
|
||||
ImageButton backImgBtn = (ImageButton) findViewById(R.id.imgbtn_custom_nav_back);
|
||||
backImgBtn.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
TextView titleTextView = (TextView) findViewById(R.id.tv_custom_nav_title);
|
||||
titleTextView.setText(getString(R.string.rc_select_contact));
|
||||
titleTextView.setTextSize(18);
|
||||
titleTextView.setTextColor(getResources().getColor(R.color.callkit_normal_text));
|
||||
|
||||
findViewById(R.id.imgbtn_custom_nav_option).setVisibility(View.VISIBLE);
|
||||
((ImageButton) findViewById(R.id.imgbtn_custom_nav_option))
|
||||
.setImageResource(R.drawable.callkit_ic_search_focused_x);
|
||||
findViewById(R.id.imgbtn_custom_nav_option)
|
||||
.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
rlSearchTop.setVisibility(View.VISIBLE);
|
||||
rlActionBar.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private AdapterView.OnItemClickListener adapterOnItemClickListener =
|
||||
new AdapterView.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
View v = view.findViewById(R.id.rc_checkbox);
|
||||
String userId = (String) v.getTag();
|
||||
if (!TextUtils.isEmpty(userId) && !invitedMembers.contains(userId)) {
|
||||
if (v.isSelected()) {
|
||||
if (selectedMember.contains(userId)) {
|
||||
selectedMember.remove(userId);
|
||||
}
|
||||
if (observerMember.contains(userId)) {
|
||||
observerMember.remove(userId);
|
||||
}
|
||||
v.setSelected(false);
|
||||
if (selectedMember.size() == 0 && observerMember.size() == 0) {
|
||||
txtvStart.setEnabled(false);
|
||||
txtvStart.setTextColor(
|
||||
getResources()
|
||||
.getColor(
|
||||
R.color
|
||||
.callkit_color_text_operation_disable));
|
||||
callkit_conference_selected_number.setTextColor(
|
||||
getResources()
|
||||
.getColor(
|
||||
R.color
|
||||
.callkit_color_text_operation_disable));
|
||||
}
|
||||
if (searchMembers != null) {
|
||||
callkit_conference_selected_number.setText(
|
||||
getString(
|
||||
R.string.callkit_selected_contacts_count,
|
||||
getTotalSelectedNumber()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
int totalNumber = getTotalSelectedNumber();
|
||||
boolean videoObserverState =
|
||||
totalNumber
|
||||
>= (mMediaType.equals(RongCallCommon.CallMediaType.AUDIO)
|
||||
? NORMAL_AUDIO_NUMBER
|
||||
: NORMAL_VIDEO_NUMBER);
|
||||
if (ctrlTag) {
|
||||
if (videoObserverState) {
|
||||
Toast.makeText(
|
||||
CallSelectMemberActivity.this,
|
||||
String.format(
|
||||
getString(
|
||||
mMediaType.equals(
|
||||
RongCallCommon
|
||||
.CallMediaType
|
||||
.AUDIO)
|
||||
? R.string
|
||||
.rc_voip_audio_numberofobservers
|
||||
: R.string
|
||||
.rc_voip_video_numberofobservers),
|
||||
totalNumber),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
if (selectedMember.contains(userId)) {
|
||||
selectedMember.remove(userId);
|
||||
}
|
||||
v.setSelected(!v.isSelected()); // 1 false
|
||||
if (v.isSelected()) {
|
||||
selectedMember.add(userId);
|
||||
}
|
||||
if (selectedMember.size() > 0 || observerMember.size() > 0) {
|
||||
txtvStart.setEnabled(true);
|
||||
txtvStart.setTextColor(
|
||||
getResources().getColor(R.color.rc_voip_check_enable));
|
||||
callkit_conference_selected_number.setTextColor(
|
||||
getResources().getColor(R.color.rc_voip_check_enable));
|
||||
} else {
|
||||
txtvStart.setEnabled(false);
|
||||
txtvStart.setTextColor(
|
||||
getResources()
|
||||
.getColor(
|
||||
R.color
|
||||
.callkit_color_text_operation_disable));
|
||||
callkit_conference_selected_number.setTextColor(
|
||||
getResources()
|
||||
.getColor(
|
||||
R.color
|
||||
.callkit_color_text_operation_disable));
|
||||
}
|
||||
} else {
|
||||
if (videoObserverState && isFirstDialog) {
|
||||
CallPromptDialog dialog =
|
||||
CallPromptDialog.newInstance(
|
||||
CallSelectMemberActivity.this,
|
||||
getString(R.string.rc_voip_video_observer));
|
||||
dialog.setPromptButtonClickedListener(
|
||||
new CallPromptDialog.OnPromptButtonClickedListener() {
|
||||
@Override
|
||||
public void onPositiveButtonClicked() {}
|
||||
|
||||
@Override
|
||||
public void onNegativeButtonClicked() {}
|
||||
});
|
||||
dialog.disableCancel();
|
||||
dialog.setCancelable(false);
|
||||
dialog.show();
|
||||
isFirstDialog = false;
|
||||
}
|
||||
v.setSelected(!v.isSelected()); // 1 false
|
||||
if (videoObserverState) {
|
||||
if (observerMember.contains(userId)) {
|
||||
observerMember.remove(userId);
|
||||
}
|
||||
observerMember.add(userId);
|
||||
}
|
||||
if (selectedMember.contains(userId)) {
|
||||
selectedMember.remove(userId);
|
||||
}
|
||||
if (v.isSelected()) {
|
||||
selectedMember.add(userId);
|
||||
}
|
||||
if (selectedMember.size() > 0 || observerMember.size() > 0) {
|
||||
txtvStart.setEnabled(true);
|
||||
txtvStart.setTextColor(
|
||||
getResources().getColor(R.color.rc_voip_check_enable));
|
||||
callkit_conference_selected_number.setTextColor(
|
||||
getResources().getColor(R.color.rc_voip_check_enable));
|
||||
} else {
|
||||
txtvStart.setEnabled(false);
|
||||
txtvStart.setTextColor(
|
||||
getResources()
|
||||
.getColor(
|
||||
R.color
|
||||
.callkit_color_text_operation_disable));
|
||||
callkit_conference_selected_number.setTextColor(
|
||||
getResources()
|
||||
.getColor(
|
||||
R.color
|
||||
.callkit_color_text_operation_disable));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (searchMembers != null) {
|
||||
callkit_conference_selected_number.setText(
|
||||
getString(
|
||||
R.string.callkit_selected_contacts_count,
|
||||
getTotalSelectedNumber()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 关闭软键盘
|
||||
*
|
||||
* @param activity
|
||||
* @param view
|
||||
*/
|
||||
private void closeKeyBoard(Activity activity, View view) {
|
||||
IBinder token;
|
||||
if (view == null || view.getWindowToken() == null) {
|
||||
if (null == activity) {
|
||||
return;
|
||||
}
|
||||
Window window = activity.getWindow();
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
View v = window.peekDecorView();
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
token = v.getWindowToken();
|
||||
} else {
|
||||
token = view.getWindowToken();
|
||||
}
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(token, 0);
|
||||
}
|
||||
|
||||
private UserInfo getUserInfo(String userid) {
|
||||
if (TextUtils.isEmpty(userid)) {
|
||||
return null;
|
||||
}
|
||||
return RongUserInfoManager.getInstance().getUserInfo(userid);
|
||||
}
|
||||
|
||||
private int getTotalSelectedNumber() {
|
||||
return (selectedMember == null ? 0 : selectedMember.size())
|
||||
+ (invitedMembers == null ? 0 : invitedMembers.size());
|
||||
}
|
||||
}
|
264
callkit/src/main/java/io/rong/callkit/CallUserGridView.java
Normal file
@ -0,0 +1,264 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import io.rong.callkit.util.ICallScrollView;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Created by weiqinxiao on 16/3/25. coming 横向显示 多人语音_被叫 */
|
||||
public class CallUserGridView extends HorizontalScrollView implements ICallScrollView {
|
||||
|
||||
private Context context;
|
||||
private boolean enableTitle;
|
||||
private LinearLayout linearLayout;
|
||||
|
||||
private static int CHILDREN_PER_LINE = 5;
|
||||
private static final int CHILDREN_SPACE = 13;
|
||||
|
||||
private int portraitSize;
|
||||
private boolean isHorizontal = true;
|
||||
|
||||
@Override
|
||||
public int getChildrenSpace() {
|
||||
return CHILDREN_SPACE;
|
||||
}
|
||||
|
||||
public CallUserGridView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CallUserGridView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CallUserGridView);
|
||||
isHorizontal = a.getBoolean(R.styleable.CallUserGridView_CallGridViewOrientation, true);
|
||||
CHILDREN_PER_LINE =
|
||||
a.getInteger(R.styleable.CallUserGridView_CallGridViewChildrenPerLine, 4);
|
||||
init(context);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
this.context = context;
|
||||
linearLayout = new LinearLayout(context);
|
||||
linearLayout.setLayoutParams(
|
||||
new LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
// linearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
addView(linearLayout);
|
||||
}
|
||||
|
||||
public int dip2pix(int dipValue) {
|
||||
float scale = getResources().getDisplayMetrics().density;
|
||||
return (int) (dipValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
public int getScreenWidth() {
|
||||
return getResources().getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
public void setChildPortraitSize(int size) {
|
||||
portraitSize = size;
|
||||
}
|
||||
|
||||
public void enableShowState(boolean enable) {
|
||||
enableTitle = enable;
|
||||
}
|
||||
|
||||
public void addChild(String childId, UserInfo userInfo) {
|
||||
addChild(childId, userInfo, null);
|
||||
}
|
||||
|
||||
public void addChild(String childId, UserInfo userInfo, String state) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
LinearLayout lastContainer = null;
|
||||
int i;
|
||||
for (i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
if (container.getChildCount() < CHILDREN_PER_LINE) {
|
||||
lastContainer = container;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastContainer == null) {
|
||||
lastContainer = new LinearLayout(context);
|
||||
lastContainer.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
lastContainer.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
lastContainer.setPadding(0, dip2pix(CHILDREN_SPACE), 0, 0);
|
||||
linearLayout.addView(lastContainer);
|
||||
}
|
||||
|
||||
LinearLayout child =
|
||||
(LinearLayout)
|
||||
LayoutInflater.from(context).inflate(R.layout.rc_voip_user_info, null);
|
||||
child.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
if (containerCount == 0) {
|
||||
child.setPadding(dip2pix(15), 0, dip2pix(CHILDREN_SPACE), 0);
|
||||
} else {
|
||||
child.setPadding(0, 0, dip2pix(CHILDREN_SPACE), 0);
|
||||
}
|
||||
child.setTag(childId);
|
||||
if (portraitSize > 0) {
|
||||
child.findViewById(R.id.rc_user_portrait_layout)
|
||||
.setLayoutParams(new LinearLayout.LayoutParams(portraitSize, portraitSize));
|
||||
}
|
||||
ImageView imageView = (ImageView) child.findViewById(R.id.rc_user_portrait);
|
||||
TextView name = (TextView) child.findViewById(R.id.rc_user_name);
|
||||
name.setVisibility(enableTitle ? VISIBLE : GONE);
|
||||
TextView stateV = (TextView) child.findViewById(R.id.rc_voip_member_state);
|
||||
stateV.setVisibility(enableTitle ? VISIBLE : GONE);
|
||||
if (state != null) {
|
||||
stateV.setText(state);
|
||||
} else {
|
||||
stateV.setVisibility(GONE);
|
||||
}
|
||||
|
||||
if (userInfo != null) {
|
||||
RongCallKit.getKitImageEngine()
|
||||
.loadPortrait(
|
||||
this.getContext(),
|
||||
userInfo.getPortraitUri(),
|
||||
R.drawable.rc_default_portrait,
|
||||
imageView);
|
||||
name.setText(userInfo.getName() == null ? userInfo.getUserId() : userInfo.getName());
|
||||
} else {
|
||||
name.setText(childId);
|
||||
}
|
||||
lastContainer.addView(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScrollViewOverScrollMode(int mode) {
|
||||
this.setOverScrollMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllChild() {
|
||||
linearLayout.removeAllViews();
|
||||
}
|
||||
|
||||
public void removeChild(String childId) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
LinearLayout lastContainer = null;
|
||||
List<LinearLayout> containerList = new ArrayList<>();
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
containerList.add(container);
|
||||
}
|
||||
for (LinearLayout resultContainer : containerList) {
|
||||
if (lastContainer == null) {
|
||||
LinearLayout child = (LinearLayout) resultContainer.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
resultContainer.removeView(child);
|
||||
if (resultContainer.getChildCount() == 0) {
|
||||
linearLayout.removeView(resultContainer);
|
||||
break;
|
||||
} else {
|
||||
lastContainer = resultContainer;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
View view = resultContainer.getChildAt(0);
|
||||
resultContainer.removeView(view);
|
||||
lastContainer.addView(view);
|
||||
if (resultContainer.getChildCount() == 0) {
|
||||
linearLayout.removeView(resultContainer);
|
||||
break;
|
||||
} else {
|
||||
lastContainer = resultContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public View findChildById(String childId) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateChildInfo(String childId, UserInfo userInfo) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
LinearLayout lastContainer = null;
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
ImageView imageView = (ImageView) child.findViewById(R.id.rc_user_portrait);
|
||||
Glide.with(this)
|
||||
.load(userInfo.getPortraitUri())
|
||||
.placeholder(R.drawable.rc_default_portrait)
|
||||
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
|
||||
.into(imageView);
|
||||
if (enableTitle) {
|
||||
TextView textView = (TextView) child.findViewById(R.id.rc_user_name);
|
||||
textView.setLines(1);
|
||||
textView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
textView.setText(userInfo.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChildState(String childId, String state) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
TextView textView = (TextView) child.findViewById(R.id.rc_voip_member_state);
|
||||
textView.setText(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChildState(String childId, boolean visible) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
TextView textView = (TextView) child.findViewById(R.id.rc_voip_member_state);
|
||||
textView.setVisibility(visible ? VISIBLE : GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildAtIndex(int index) {
|
||||
return linearLayout.getChildAt(index);
|
||||
}
|
||||
}
|
111
callkit/src/main/java/io/rong/callkit/ContainerLayout.java
Normal file
@ -0,0 +1,111 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.RelativeLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import cn.rongcloud.rtc.api.stream.RCRTCVideoView;
|
||||
import cn.rongcloud.rtc.utils.FinLog;
|
||||
|
||||
/** Created by Administrator on 2017/3/30. */
|
||||
public class ContainerLayout extends RelativeLayout {
|
||||
private final String TAG = ContainerLayout.class.getSimpleName();
|
||||
private Context context;
|
||||
private static boolean isNeedFillScrren = true;
|
||||
SurfaceView currentView;
|
||||
|
||||
public ContainerLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void addView(final SurfaceView videoView) {
|
||||
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
this.screenWidth = wm.getDefaultDisplay().getWidth();
|
||||
this.screenHeight = wm.getDefaultDisplay().getHeight();
|
||||
FinLog.d(
|
||||
TAG,
|
||||
"---xx-- add view "
|
||||
+ videoView.toString()
|
||||
+ " Height: "
|
||||
+ ((RCRTCVideoView) videoView).rotatedFrameHeight
|
||||
+ " Width: "
|
||||
+ ((RCRTCVideoView) videoView).rotatedFrameWidth);
|
||||
super.addView(videoView, getBigContainerParams((RCRTCVideoView) videoView));
|
||||
currentView = videoView;
|
||||
((RCRTCVideoView) videoView)
|
||||
.setOnSizeChangedListener(
|
||||
new RCRTCVideoView.OnSizeChangedListener() {
|
||||
@Override
|
||||
public void onChanged(RCRTCVideoView.Size size) {
|
||||
try {
|
||||
ContainerLayout.this.removeAllViews();
|
||||
FinLog.d(
|
||||
TAG,
|
||||
"---xx-- change view "
|
||||
+ videoView.toString()
|
||||
+ " Height: "
|
||||
+ ((RCRTCVideoView) videoView)
|
||||
.rotatedFrameHeight
|
||||
+ " Width: "
|
||||
+ ((RCRTCVideoView) videoView)
|
||||
.rotatedFrameWidth);
|
||||
ContainerLayout.this.addView(
|
||||
videoView,
|
||||
getBigContainerParams((RCRTCVideoView) videoView));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private LayoutParams getBigContainerParams(RCRTCVideoView videoView) {
|
||||
LayoutParams layoutParams = null;
|
||||
if (!isNeedFillScrren) {
|
||||
if (screenHeight > screenWidth) { // V
|
||||
int layoutParamsHeight =
|
||||
(videoView.rotatedFrameHeight == 0 || videoView.rotatedFrameWidth == 0)
|
||||
? ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
: screenWidth
|
||||
* videoView.rotatedFrameHeight
|
||||
/ videoView.rotatedFrameWidth;
|
||||
layoutParams = new LayoutParams(screenWidth, layoutParamsHeight);
|
||||
} else {
|
||||
int layoutParamsWidth =
|
||||
(videoView.rotatedFrameWidth == 0 || videoView.rotatedFrameHeight == 0)
|
||||
? ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
: (screenWidth
|
||||
* videoView.rotatedFrameWidth
|
||||
/ videoView.rotatedFrameHeight
|
||||
> screenWidth
|
||||
? screenWidth
|
||||
: screenHeight
|
||||
* videoView.rotatedFrameWidth
|
||||
/ videoView.rotatedFrameHeight);
|
||||
layoutParams = new LayoutParams(layoutParamsWidth, screenHeight);
|
||||
}
|
||||
} else {
|
||||
layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||
return layoutParams;
|
||||
}
|
||||
|
||||
public void setIsNeedFillScrren(boolean isNeed) {
|
||||
isNeedFillScrren = isNeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllViews() {
|
||||
if (currentView != null) ((RCRTCVideoView) currentView).setOnSizeChangedListener(null);
|
||||
super.removeAllViews();
|
||||
}
|
||||
|
||||
private int screenWidth;
|
||||
private int screenHeight;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.widget.ImageView;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
|
||||
public class GlideCallKitImageEngine {
|
||||
/**
|
||||
* 设置头像加载样式
|
||||
*
|
||||
* @param context
|
||||
* @param url
|
||||
* @param replaceRes
|
||||
* @param imageView
|
||||
*/
|
||||
public void loadPortrait(
|
||||
Context context, Uri url, @DrawableRes int replaceRes, ImageView imageView) {
|
||||
Glide.with(context)
|
||||
.load(url)
|
||||
.error(replaceRes)
|
||||
.placeholder(replaceRes)
|
||||
.apply(RequestOptions.circleCropTransform())
|
||||
.into(imageView);
|
||||
}
|
||||
}
|
@ -0,0 +1,989 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import static io.rong.callkit.CallSelectMemberActivity.DISCONNECT_ACTION;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import cn.rongcloud.rtc.api.RCRTCEngine;
|
||||
import cn.rongcloud.rtc.audioroute.RCAudioRouteType;
|
||||
import cn.rongcloud.rtc.utils.FinLog;
|
||||
import io.rong.callkit.util.BluetoothUtil;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.callkit.util.CallVerticalScrollView;
|
||||
import io.rong.callkit.util.DefaultPushConfig;
|
||||
import io.rong.callkit.util.HeadsetInfo;
|
||||
import io.rong.callkit.util.ICallScrollView;
|
||||
import io.rong.callkit.util.RingingMode;
|
||||
import io.rong.callkit.util.RongCallPermissionUtil;
|
||||
import io.rong.callkit.util.SPUtils;
|
||||
import io.rong.calllib.CallUserProfile;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.calllib.message.MultiCallEndMessage;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.IMCenter;
|
||||
import io.rong.imkit.userinfo.RongUserInfoManager;
|
||||
import io.rong.imlib.IRongCoreCallback;
|
||||
import io.rong.imlib.IRongCoreEnum;
|
||||
import io.rong.imlib.RongIMClient;
|
||||
import io.rong.imlib.discussion.base.RongDiscussionClient;
|
||||
import io.rong.imlib.discussion.model.Discussion;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import io.rong.imlib.model.Group;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/** <a href="http://support.rongcloud.cn/kb/Njcy">如何实现不基于于群组的voip</a> */
|
||||
public class MultiAudioCallActivity extends BaseCallActivity {
|
||||
private static final String TAG = "VoIPMultiAudioCallActivity";
|
||||
LinearLayout audioContainer;
|
||||
ICallScrollView memberContainer;
|
||||
|
||||
RelativeLayout incomingLayout;
|
||||
RelativeLayout outgoingLayout;
|
||||
RelativeLayout outgoingController;
|
||||
RelativeLayout incomingController;
|
||||
RongCallAction callAction;
|
||||
RongCallSession callSession;
|
||||
|
||||
boolean shouldShowFloat = true;
|
||||
boolean startForCheckPermissions = false;
|
||||
private boolean handFree = false;
|
||||
private boolean muted = false;
|
||||
private final String KEY_MUTED = "muted";
|
||||
private final String KEY_HAND_FREE = "handFree";
|
||||
|
||||
@Override
|
||||
@TargetApi(23)
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (savedInstanceState != null && RongCallClient.getInstance() == null) {
|
||||
// 音视频请求权限时,用户在设置页面取消权限,导致应用重启,退出当前activity.
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
setContentView(R.layout.rc_voip_ac_muti_audio);
|
||||
audioContainer = (LinearLayout) findViewById(R.id.rc_voip_container);
|
||||
incomingLayout =
|
||||
(RelativeLayout)
|
||||
LayoutInflater.from(this)
|
||||
.inflate(R.layout.rc_voip_item_incoming_maudio, null);
|
||||
TextView tv_invite_incoming_audio =
|
||||
incomingLayout.findViewById(R.id.tv_invite_incoming_audio);
|
||||
CallKitUtils.textViewShadowLayer(tv_invite_incoming_audio, MultiAudioCallActivity.this);
|
||||
|
||||
outgoingLayout =
|
||||
(RelativeLayout)
|
||||
LayoutInflater.from(this)
|
||||
.inflate(R.layout.rc_voip_item_outgoing_maudio, null);
|
||||
TextView rc_voip_remind = incomingLayout.findViewById(R.id.rc_voip_remind);
|
||||
CallKitUtils.textViewShadowLayer(rc_voip_remind, MultiAudioCallActivity.this);
|
||||
|
||||
outgoingController =
|
||||
(RelativeLayout)
|
||||
LayoutInflater.from(this)
|
||||
.inflate(
|
||||
R.layout.rc_voip_call_bottom_connected_button_layout, null);
|
||||
ImageView button = outgoingController.findViewById(R.id.rc_voip_call_mute_btn);
|
||||
button.setEnabled(true);
|
||||
incomingController =
|
||||
(RelativeLayout)
|
||||
LayoutInflater.from(this)
|
||||
.inflate(R.layout.rc_voip_call_bottom_incoming_button_layout, null);
|
||||
|
||||
startForCheckPermissions = getIntent().getBooleanExtra("checkPermissions", false);
|
||||
if (requestCallPermissions(
|
||||
RongCallCommon.CallMediaType.AUDIO, REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)) {
|
||||
initView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
startForCheckPermissions = getIntent().getBooleanExtra("checkPermissions", false);
|
||||
super.onNewIntent(intent);
|
||||
if (requestCallPermissions(
|
||||
RongCallCommon.CallMediaType.AUDIO, REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS)) {
|
||||
initView();
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(23)
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, int[] grantResults) {
|
||||
switch (requestCode) {
|
||||
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
|
||||
if (RongCallPermissionUtil.checkAudioCallNeedPermission(this)) {
|
||||
if (startForCheckPermissions) {
|
||||
startForCheckPermissions = false;
|
||||
RongCallClient.getInstance().onPermissionGranted();
|
||||
} else {
|
||||
initView();
|
||||
}
|
||||
} else {
|
||||
if (startForCheckPermissions) {
|
||||
startForCheckPermissions = false;
|
||||
Toast.makeText(
|
||||
this,
|
||||
getString(R.string.rc_voip_relevant_permissions),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
RongCallClient.getInstance().onPermissionDenied();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreFloatBox(Bundle bundle) {
|
||||
super.onRestoreFloatBox(bundle);
|
||||
if (bundle != null) {
|
||||
handFree = bundle.getBoolean(KEY_HAND_FREE);
|
||||
muted = bundle.getBoolean(KEY_MUTED);
|
||||
audioContainer.removeAllViews();
|
||||
audioContainer.addView(outgoingLayout);
|
||||
String str = (String) SPUtils.get(MultiAudioCallActivity.this, "ICallScrollView", "");
|
||||
|
||||
FrameLayout controller =
|
||||
(FrameLayout) audioContainer.findViewById(R.id.rc_voip_control_layout);
|
||||
controller.removeAllViews();
|
||||
controller.addView(outgoingController);
|
||||
callSession = RongCallClient.getInstance().getCallSession();
|
||||
if (callSession == null) {
|
||||
setShouldShowFloat(false);
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
List<CallUserProfile> participantProfiles = callSession.getParticipantProfileList();
|
||||
|
||||
/** 初始化列表* */
|
||||
if (str.equals("CallVerticalScrollView")) {
|
||||
memberContainer =
|
||||
(CallVerticalScrollView)
|
||||
audioContainer.findViewById(R.id.rc_voip_members_container);
|
||||
} else {
|
||||
memberContainer =
|
||||
(CallUserGridView)
|
||||
audioContainer.findViewById(
|
||||
R.id.rc_voip_members_container_gridView);
|
||||
}
|
||||
memberContainer.enableShowState(true);
|
||||
LinearLayout linear_scrollviewTag =
|
||||
(LinearLayout) outgoingLayout.findViewById(R.id.linear_scrollviewTag);
|
||||
if (participantProfiles.size() > 4) {
|
||||
ViewGroup.LayoutParams params = linear_scrollviewTag.getLayoutParams();
|
||||
params.height = CallKitUtils.dp2px(200, MultiAudioCallActivity.this);
|
||||
linear_scrollviewTag.setLayoutParams(params);
|
||||
}
|
||||
// 添加数据
|
||||
for (CallUserProfile item : participantProfiles) {
|
||||
if (!item.getUserId().equals(callSession.getSelfUserId())
|
||||
&& memberContainer.findChildById(item.getUserId()) == null) {
|
||||
if (item.getCallStatus().equals(RongCallCommon.CallStatus.CONNECTED)) {
|
||||
memberContainer.addChild(
|
||||
item.getUserId(),
|
||||
RongUserInfoManager.getInstance().getUserInfo(item.getUserId()));
|
||||
memberContainer.updateChildState(item.getUserId(), false);
|
||||
} else {
|
||||
String state = getString(R.string.rc_voip_call_connecting);
|
||||
memberContainer.addChild(
|
||||
item.getUserId(),
|
||||
RongUserInfoManager.getInstance().getUserInfo(item.getUserId()),
|
||||
state);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!(boolean) bundle.get("isDial")) {
|
||||
onCallConnected(callSession, null); // 接听
|
||||
} else {
|
||||
onCallOutgoing(callSession, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void initView() {
|
||||
Intent intent = getIntent();
|
||||
callAction = RongCallAction.valueOf(intent.getStringExtra("callAction"));
|
||||
if (callAction == null || callAction.equals(RongCallAction.ACTION_RESUME_CALL)) {
|
||||
RelativeLayout relativeLayout =
|
||||
(RelativeLayout)
|
||||
outgoingLayout.findViewById(R.id.reltive_voip_outgoing_audio_title);
|
||||
relativeLayout.setVisibility(View.VISIBLE);
|
||||
return;
|
||||
}
|
||||
ArrayList<String> invitedList = new ArrayList<>();
|
||||
if (callAction.equals(RongCallAction.ACTION_INCOMING_CALL)) {
|
||||
// 正常在收到呼叫后,RongCallClient 和 CallSession均不会为空
|
||||
if (RongCallClient.getInstance() == null
|
||||
|| RongCallClient.getInstance().getCallSession() == null) {
|
||||
// 如果为空 表示通话已经结束 但依然启动了本页面,这样会导致页面无法销毁问题
|
||||
// 所以 需要在这里 finish 结束当前页面 推荐开发者在结束当前页面前跳转至APP主页或者其他页面
|
||||
RLog.e(
|
||||
TAG,
|
||||
"MultiAudioCallActivity#initView()->RongCallClient or CallSession is empty---->finish()");
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
audioContainer.removeAllViews();
|
||||
callSession = RongCallClient.getInstance().getCallSession();
|
||||
UserInfo userInfo =
|
||||
RongUserInfoManager.getInstance().getUserInfo(callSession.getCallerUserId());
|
||||
setTopContainerUserView(callSession.getCallerUserId());
|
||||
|
||||
audioContainer.addView(incomingLayout);
|
||||
memberContainer =
|
||||
(CallUserGridView)
|
||||
audioContainer.findViewById(R.id.rc_voip_members_container_gridView);
|
||||
SPUtils.put(MultiAudioCallActivity.this, "ICallScrollView", "CallUserGridView");
|
||||
memberContainer.removeAllChild();
|
||||
memberContainer.setChildPortraitSize(memberContainer.dip2pix(55));
|
||||
List<CallUserProfile> list = callSession.getParticipantProfileList();
|
||||
for (CallUserProfile profile : list) {
|
||||
if (!profile.getUserId().equals(callSession.getCallerUserId())) {
|
||||
invitedList.add(profile.getUserId());
|
||||
userInfo = RongUserInfoManager.getInstance().getUserInfo(profile.getUserId());
|
||||
memberContainer.addChild(profile.getUserId(), userInfo);
|
||||
}
|
||||
}
|
||||
FrameLayout controller =
|
||||
(FrameLayout) audioContainer.findViewById(R.id.rc_voip_control_layout);
|
||||
controller.removeAllViews();
|
||||
controller.addView(incomingController);
|
||||
|
||||
ImageView iv_answerBtn =
|
||||
(ImageView) incomingController.findViewById(R.id.rc_voip_call_answer_btn);
|
||||
iv_answerBtn.setBackground(
|
||||
CallKitUtils.BackgroundDrawable(
|
||||
R.drawable.rc_voip_audio_answer_selector_new,
|
||||
MultiAudioCallActivity.this));
|
||||
|
||||
onIncomingCallRinging(callSession);
|
||||
} else if (callAction.equals(RongCallAction.ACTION_OUTGOING_CALL)) {
|
||||
audioContainer.removeAllViews();
|
||||
Conversation.ConversationType conversationType =
|
||||
Conversation.ConversationType.valueOf(
|
||||
intent.getStringExtra("conversationType").toUpperCase(Locale.US));
|
||||
String targetId = intent.getStringExtra("targetId");
|
||||
ArrayList<String> userIds = intent.getStringArrayListExtra("invitedUsers");
|
||||
ArrayList<String> observers = intent.getStringArrayListExtra("observers");
|
||||
audioContainer.addView(outgoingLayout);
|
||||
|
||||
LinearLayout linear_scrollviewTag =
|
||||
(LinearLayout) outgoingLayout.findViewById(R.id.linear_scrollviewTag);
|
||||
|
||||
// 多人语音主叫方顶部布局
|
||||
RelativeLayout relativeLayout =
|
||||
(RelativeLayout)
|
||||
outgoingLayout.findViewById(R.id.reltive_voip_outgoing_audio_title);
|
||||
relativeLayout.setVisibility(View.VISIBLE);
|
||||
|
||||
memberContainer =
|
||||
(CallVerticalScrollView)
|
||||
audioContainer.findViewById(R.id.rc_voip_members_container);
|
||||
SPUtils.put(MultiAudioCallActivity.this, "ICallScrollView", "CallVerticalScrollView");
|
||||
memberContainer.enableShowState(true);
|
||||
FrameLayout controller =
|
||||
(FrameLayout) audioContainer.findViewById(R.id.rc_voip_control_layout);
|
||||
controller.removeAllViews();
|
||||
controller.addView(outgoingController);
|
||||
|
||||
ImageView iv_answerBtn =
|
||||
(ImageView) incomingController.findViewById(R.id.rc_voip_call_answer_btn);
|
||||
iv_answerBtn.setBackground(
|
||||
CallKitUtils.BackgroundDrawable(
|
||||
R.drawable.rc_voip_audio_answer_selector_new,
|
||||
MultiAudioCallActivity.this));
|
||||
|
||||
ImageView button = outgoingController.findViewById(R.id.rc_voip_call_mute_btn);
|
||||
button.setEnabled(true);
|
||||
for (int i = 0; i < userIds.size(); i++) {
|
||||
if (!userIds.get(i).equals(RongIMClient.getInstance().getCurrentUserId())) {
|
||||
invitedList.add(userIds.get(i));
|
||||
UserInfo userInfo =
|
||||
RongUserInfoManager.getInstance().getUserInfo(userIds.get(i));
|
||||
memberContainer.addChild(
|
||||
userIds.get(i), userInfo, getString(R.string.rc_voip_call_connecting));
|
||||
}
|
||||
}
|
||||
//
|
||||
if (userIds.size() > 4) {
|
||||
ViewGroup.LayoutParams params = linear_scrollviewTag.getLayoutParams();
|
||||
params.height = CallKitUtils.dp2px(200, MultiAudioCallActivity.this);
|
||||
linear_scrollviewTag.setLayoutParams(params);
|
||||
}
|
||||
|
||||
String groupName = "";
|
||||
Group group = RongUserInfoManager.getInstance().getGroupInfo(targetId);
|
||||
if (group != null && !TextUtils.isEmpty(group.getName())) {
|
||||
groupName = group.getName();
|
||||
}
|
||||
RongCallClient.getInstance()
|
||||
.setPushConfig(
|
||||
DefaultPushConfig.getInviteConfig(this, true, false, groupName),
|
||||
DefaultPushConfig.getHangupConfig(this, false, groupName));
|
||||
|
||||
RongCallClient.getInstance()
|
||||
.startCall(
|
||||
conversationType,
|
||||
targetId,
|
||||
invitedList,
|
||||
observers,
|
||||
RongCallCommon.CallMediaType.AUDIO,
|
||||
"multi");
|
||||
}
|
||||
memberContainer.setScrollViewOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
createPickupDetector();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (pickupDetector != null) {
|
||||
pickupDetector.unRegister();
|
||||
}
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
if (pickupDetector == null) createPickupDetector();
|
||||
if (pickupDetector != null) {
|
||||
pickupDetector.register(this);
|
||||
}
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
public void onHangupBtnClick(View view) {
|
||||
// unRegisterHeadsetplugReceiver();
|
||||
if (callSession == null || isFinishing) {
|
||||
FinLog.e(
|
||||
TAG,
|
||||
"_挂断多人语音出错 callSession="
|
||||
+ (callSession == null)
|
||||
+ ",isFinishing="
|
||||
+ isFinishing);
|
||||
return;
|
||||
}
|
||||
RongCallClient.getInstance().hangUpCall(callSession.getCallId());
|
||||
}
|
||||
|
||||
public void onReceiveBtnClick(View view) {
|
||||
if (callSession == null || isFinishing) {
|
||||
FinLog.e(
|
||||
TAG,
|
||||
"_接听多人语音出错 callSession="
|
||||
+ (callSession == null)
|
||||
+ ",isFinishing="
|
||||
+ isFinishing);
|
||||
return;
|
||||
}
|
||||
RongCallClient.getInstance().acceptCall(callSession.getCallId());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAddMember(List<String> newMemberIds) {
|
||||
if (newMemberIds == null || newMemberIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ArrayList<String> added = new ArrayList<>();
|
||||
List<String> participants = new ArrayList<>();
|
||||
List<CallUserProfile> list =
|
||||
RongCallClient.getInstance().getCallSession().getParticipantProfileList();
|
||||
for (CallUserProfile profile : list) {
|
||||
participants.add(profile.getUserId());
|
||||
}
|
||||
for (String id : newMemberIds) {
|
||||
if (participants.contains(id)) {
|
||||
continue;
|
||||
} else {
|
||||
added.add(id);
|
||||
}
|
||||
}
|
||||
if (added.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RongCallClient.getInstance().addParticipants(callSession.getCallId(), added, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserRinging(String userId) {}
|
||||
|
||||
@Override
|
||||
public void onCallOutgoing(RongCallSession callSession, SurfaceView localVideo) {
|
||||
super.onCallOutgoing(callSession, localVideo);
|
||||
this.callSession = callSession;
|
||||
callRinging(RingingMode.Outgoing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserInvited(String userId, RongCallCommon.CallMediaType mediaType) {
|
||||
super.onRemoteUserInvited(userId, mediaType);
|
||||
memberContainer.addChild(
|
||||
userId,
|
||||
RongUserInfoManager.getInstance().getUserInfo(userId),
|
||||
getString(R.string.rc_voip_call_connecting));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserJoined(
|
||||
String userId,
|
||||
RongCallCommon.CallMediaType mediaType,
|
||||
int userType,
|
||||
SurfaceView remoteVideo) {
|
||||
View view = memberContainer.findChildById(userId);
|
||||
if (view != null) {
|
||||
memberContainer.updateChildState(userId, false);
|
||||
} else {
|
||||
memberContainer.addChild(userId, RongUserInfoManager.getInstance().getUserInfo(userId));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserLeft(
|
||||
final String userId, RongCallCommon.CallDisconnectedReason reason) {
|
||||
if (isTopContainerUserExit(userId)) {
|
||||
return;
|
||||
}
|
||||
String text = null;
|
||||
switch (reason) {
|
||||
case REMOTE_BUSY_LINE:
|
||||
text = getString(R.string.rc_voip_mt_busy_toast);
|
||||
break;
|
||||
case REMOTE_CANCEL:
|
||||
text = getString(R.string.rc_voip_mt_cancel);
|
||||
break;
|
||||
case REMOTE_REJECT:
|
||||
text = getString(R.string.rc_voip_mt_reject);
|
||||
break;
|
||||
case NO_RESPONSE:
|
||||
text = getString(R.string.rc_voip_mt_no_response);
|
||||
break;
|
||||
case NETWORK_ERROR:
|
||||
case HANGUP:
|
||||
case REMOTE_HANGUP:
|
||||
break;
|
||||
}
|
||||
if (text != null && memberContainer != null) {
|
||||
memberContainer.updateChildState(userId, text);
|
||||
}
|
||||
if (memberContainer != null) {
|
||||
memberContainer.removeChild(userId);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isTopContainerUserExit(String userId) {
|
||||
if (CallKitUtils.callConnected) {
|
||||
return false;
|
||||
}
|
||||
if (callSession != null
|
||||
&& TextUtils.equals(callSession.getInviterUserId(), userId)
|
||||
&& memberContainer != null) {
|
||||
if (((LinearLayout) memberContainer.getChildAtIndex(0)) != null
|
||||
&& ((LinearLayout) memberContainer.getChildAtIndex(0)).getChildAt(0) != null
|
||||
&& ((LinearLayout) memberContainer.getChildAtIndex(0)).getChildAt(0).getTag()
|
||||
!= null) {
|
||||
LinearLayout firstView =
|
||||
(LinearLayout)
|
||||
((LinearLayout) memberContainer.getChildAtIndex(0)).getChildAt(0);
|
||||
String firstUserId = (String) firstView.getTag();
|
||||
setTopContainerUserView(firstUserId);
|
||||
|
||||
memberContainer.removeChild(firstUserId);
|
||||
|
||||
LinearLayout linearLayout =
|
||||
(LinearLayout)
|
||||
((LinearLayout) memberContainer.getChildAtIndex(0)).getChildAt(0);
|
||||
linearLayout.setPadding(
|
||||
memberContainer.dip2pix(15),
|
||||
0,
|
||||
memberContainer.dip2pix(memberContainer.getChildrenSpace()),
|
||||
0);
|
||||
linearLayout.requestLayout();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setTopContainerUserView(String userId) {
|
||||
TextView name = (TextView) incomingLayout.findViewById(R.id.rc_user_name);
|
||||
ImageView userPortrait =
|
||||
(ImageView) incomingLayout.findViewById(R.id.rc_voip_user_portrait);
|
||||
UserInfo userInfo = RongUserInfoManager.getInstance().getUserInfo(userId);
|
||||
if (userInfo != null && userInfo.getName() != null) {
|
||||
name.setText(userInfo.getName());
|
||||
} else {
|
||||
name.setText(userId);
|
||||
}
|
||||
if (userInfo != null && userInfo.getPortraitUri() != null) {
|
||||
RongCallKit.getKitImageEngine()
|
||||
.loadPortrait(
|
||||
getBaseContext(),
|
||||
userInfo.getPortraitUri(),
|
||||
R.drawable.rc_default_portrait,
|
||||
userPortrait);
|
||||
userPortrait.setVisibility(View.VISIBLE);
|
||||
}
|
||||
name.setTag(userId + "callerName");
|
||||
}
|
||||
|
||||
/**
|
||||
* 已建立通话。 通话接通时,通过回调 onCallConnected 通知当前 call 的详细信息。
|
||||
*
|
||||
* @param callSession 通话实体。
|
||||
* @param localVideo 本地 camera 信息。
|
||||
*/
|
||||
@Override
|
||||
public void onCallConnected(final RongCallSession callSession, SurfaceView localVideo) {
|
||||
super.onCallConnected(callSession, localVideo);
|
||||
RongCallClient.getInstance().setEnableLocalVideo(false);
|
||||
this.callSession = callSession;
|
||||
if (callAction.equals(RongCallAction.ACTION_INCOMING_CALL)) {
|
||||
audioContainer.removeAllViews();
|
||||
FrameLayout controller =
|
||||
(FrameLayout) outgoingLayout.findViewById(R.id.rc_voip_control_layout);
|
||||
controller.addView(outgoingController);
|
||||
audioContainer.addView(outgoingLayout);
|
||||
SPUtils.put(MultiAudioCallActivity.this, "ICallScrollView", "CallVerticalScrollView");
|
||||
// 多人语音通话中竖向滑动
|
||||
memberContainer =
|
||||
(CallVerticalScrollView)
|
||||
outgoingLayout.findViewById(R.id.rc_voip_members_container);
|
||||
memberContainer.enableShowState(true);
|
||||
LinearLayout linear_scrollviewTag =
|
||||
(LinearLayout) outgoingLayout.findViewById(R.id.linear_scrollviewTag);
|
||||
if (callSession.getParticipantProfileList().size() > 4) {
|
||||
ViewGroup.LayoutParams params = linear_scrollviewTag.getLayoutParams();
|
||||
params.height = CallKitUtils.dp2px(200, MultiAudioCallActivity.this);
|
||||
linear_scrollviewTag.setLayoutParams(params);
|
||||
}
|
||||
for (CallUserProfile profile : callSession.getParticipantProfileList()) {
|
||||
if (!profile.getUserId().equals(callSession.getSelfUserId())) {
|
||||
UserInfo userInfo =
|
||||
RongUserInfoManager.getInstance().getUserInfo(profile.getUserId());
|
||||
String state =
|
||||
profile.getCallStatus().equals(RongCallCommon.CallStatus.CONNECTED)
|
||||
? null
|
||||
: getString(R.string.rc_voip_call_connecting);
|
||||
memberContainer.addChild(profile.getUserId(), userInfo, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outgoingLayout.findViewById(R.id.rc_voip_remind).setVisibility(View.GONE);
|
||||
outgoingLayout.findViewById(R.id.rc_voip_handfree).setVisibility(View.VISIBLE);
|
||||
ImageView button = outgoingController.findViewById(R.id.rc_voip_call_mute_btn);
|
||||
button.setEnabled(true);
|
||||
outgoingLayout.findViewById(R.id.rc_voip_call_mute).setVisibility(View.VISIBLE);
|
||||
// 多人语音主叫方顶部布局
|
||||
RelativeLayout relativeLayout =
|
||||
(RelativeLayout)
|
||||
outgoingLayout.findViewById(R.id.reltive_voip_outgoing_audio_title);
|
||||
relativeLayout.setVisibility(View.GONE);
|
||||
|
||||
View muteV = outgoingLayout.findViewById(R.id.rc_voip_call_mute_btn);
|
||||
muteV.setVisibility(View.VISIBLE);
|
||||
muteV.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onMuteButtonClick(v);
|
||||
}
|
||||
});
|
||||
|
||||
View handfreeV = outgoingLayout.findViewById(R.id.rc_voip_handfree_btn);
|
||||
handfreeV.setVisibility(View.VISIBLE);
|
||||
handfreeV.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onHandFreeButtonClick(v);
|
||||
}
|
||||
});
|
||||
|
||||
outgoingLayout.findViewById(R.id.rc_voip_title).setVisibility(View.VISIBLE);
|
||||
TextView timeV = (TextView) outgoingLayout.findViewById(R.id.rc_voip_time);
|
||||
setupTime(timeV);
|
||||
|
||||
View imgvAdd = outgoingLayout.findViewById(R.id.rc_voip_add_btn);
|
||||
imgvAdd.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
setShouldShowFloat(false);
|
||||
if (callSession
|
||||
.getConversationType()
|
||||
.equals(Conversation.ConversationType.DISCUSSION)) {
|
||||
RongDiscussionClient.getInstance()
|
||||
.getDiscussion(
|
||||
callSession.getTargetId(),
|
||||
new IRongCoreCallback.ResultCallback<Discussion>() {
|
||||
@Override
|
||||
public void onSuccess(Discussion discussion) {
|
||||
Intent intent =
|
||||
new Intent(
|
||||
MultiAudioCallActivity.this,
|
||||
CallSelectMemberActivity.class);
|
||||
ArrayList<String> added =
|
||||
new ArrayList<String>();
|
||||
List<CallUserProfile> list =
|
||||
RongCallClient.getInstance()
|
||||
.getCallSession()
|
||||
.getParticipantProfileList();
|
||||
for (CallUserProfile profile : list) {
|
||||
added.add(profile.getUserId());
|
||||
}
|
||||
List<String> allObserver =
|
||||
RongCallClient.getInstance()
|
||||
.getCallSession()
|
||||
.getObserverUserList();
|
||||
intent.putStringArrayListExtra(
|
||||
"allObserver",
|
||||
new ArrayList<>(allObserver));
|
||||
intent.putStringArrayListExtra(
|
||||
"allMembers",
|
||||
(ArrayList<String>)
|
||||
discussion.getMemberIdList());
|
||||
intent.putStringArrayListExtra(
|
||||
"invitedMembers", added);
|
||||
intent.putExtra(
|
||||
"conversationType",
|
||||
callSession
|
||||
.getConversationType()
|
||||
.getValue());
|
||||
intent.putExtra(
|
||||
"mediaType",
|
||||
RongCallCommon.CallMediaType.AUDIO
|
||||
.getValue());
|
||||
startActivityForResult(
|
||||
intent, REQUEST_CODE_ADD_MEMBER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(
|
||||
IRongCoreEnum.CoreErrorCode e) {}
|
||||
});
|
||||
} else if (callSession
|
||||
.getConversationType()
|
||||
.equals(Conversation.ConversationType.GROUP)) {
|
||||
Intent intent =
|
||||
new Intent(
|
||||
MultiAudioCallActivity.this,
|
||||
CallSelectMemberActivity.class);
|
||||
ArrayList<String> added = new ArrayList<>();
|
||||
List<CallUserProfile> list =
|
||||
RongCallClient.getInstance()
|
||||
.getCallSession()
|
||||
.getParticipantProfileList();
|
||||
for (CallUserProfile profile : list) {
|
||||
added.add(profile.getUserId());
|
||||
}
|
||||
List<String> allObserver =
|
||||
RongCallClient.getInstance()
|
||||
.getCallSession()
|
||||
.getObserverUserList();
|
||||
intent.putExtra("callId", callSession.getCallId());
|
||||
intent.putStringArrayListExtra(
|
||||
"allObserver", new ArrayList<>(allObserver));
|
||||
intent.putStringArrayListExtra("invitedMembers", added);
|
||||
intent.putExtra(
|
||||
"conversationType",
|
||||
callSession.getConversationType().getValue());
|
||||
intent.putExtra("groupId", callSession.getTargetId());
|
||||
intent.putExtra(
|
||||
"mediaType", RongCallCommon.CallMediaType.AUDIO.getValue());
|
||||
startActivityForResult(intent, REQUEST_CODE_ADD_MEMBER);
|
||||
} else {
|
||||
ArrayList<String> added = new ArrayList<>();
|
||||
List<CallUserProfile> list =
|
||||
RongCallClient.getInstance()
|
||||
.getCallSession()
|
||||
.getParticipantProfileList();
|
||||
for (CallUserProfile profile : list) {
|
||||
added.add(profile.getUserId());
|
||||
}
|
||||
addMember(added);
|
||||
}
|
||||
}
|
||||
});
|
||||
outgoingLayout.findViewById(R.id.rc_voip_minimize_outgoing).setVisibility(View.VISIBLE);
|
||||
View minimizeV = outgoingLayout.findViewById(R.id.rc_voip_minimize);
|
||||
minimizeV.setVisibility(View.VISIBLE);
|
||||
minimizeV.setOnClickListener(
|
||||
new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Log.i(
|
||||
"audioTag",
|
||||
"************ outgoingLayout.findViewById(R.id.rc_voip_minimize)*****************");
|
||||
MultiAudioCallActivity.super.onMinimizeClick(v);
|
||||
}
|
||||
});
|
||||
|
||||
RongCallClient.getInstance().setEnableLocalAudio(!muted);
|
||||
if (muteV != null) {
|
||||
muteV.setSelected(muted);
|
||||
}
|
||||
RCRTCEngine.getInstance().enableSpeaker(false);
|
||||
|
||||
stopRing();
|
||||
}
|
||||
|
||||
protected void resetHandFreeStatus(RCAudioRouteType type) {
|
||||
ImageView handFreeV = null;
|
||||
if (null != outgoingLayout) {
|
||||
handFreeV = outgoingLayout.findViewById(R.id.rc_voip_handfree_btn);
|
||||
}
|
||||
if (handFreeV != null) {
|
||||
// 耳机状态
|
||||
if (type == RCAudioRouteType.HEADSET || type == RCAudioRouteType.HEADSET_BLUETOOTH) {
|
||||
// handFreeV.setSelected(false);
|
||||
} else {
|
||||
// 非耳机状态
|
||||
handFreeV.setSelected(type == RCAudioRouteType.SPEAKER_PHONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallDisconnected(
|
||||
RongCallSession callSession, RongCallCommon.CallDisconnectedReason reason) {
|
||||
super.onCallDisconnected(callSession, reason);
|
||||
|
||||
isFinishing = true;
|
||||
if (reason == null || callSession == null) {
|
||||
RLog.e(TAG, "onCallDisconnected. callSession is null!");
|
||||
postRunnableDelay(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
MultiCallEndMessage multiCallEndMessage = new MultiCallEndMessage();
|
||||
multiCallEndMessage.setReason(reason);
|
||||
multiCallEndMessage.setMediaType(IRongCoreEnum.MediaType.AUDIO);
|
||||
long serverTime = System.currentTimeMillis() - RongIMClient.getInstance().getDeltaTime();
|
||||
IMCenter.getInstance()
|
||||
.insertIncomingMessage(
|
||||
callSession.getConversationType(),
|
||||
callSession.getTargetId(),
|
||||
callSession.getCallerUserId(),
|
||||
CallKitUtils.getReceivedStatus(reason),
|
||||
multiCallEndMessage,
|
||||
serverTime,
|
||||
null);
|
||||
cancelTime();
|
||||
stopRing();
|
||||
postRunnableDelay(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
sendBroadcast(new Intent(DISCONNECT_ACTION).setPackage(getPackageName()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
callSession = RongCallClient.getInstance().getCallSession();
|
||||
if (requestCode == REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS) {
|
||||
if (RongCallPermissionUtil.checkAudioCallNeedPermission(this)) {
|
||||
if (startForCheckPermissions) {
|
||||
startForCheckPermissions = false;
|
||||
RongCallClient.getInstance().onPermissionGranted();
|
||||
} else {
|
||||
initView();
|
||||
}
|
||||
} else {
|
||||
if (startForCheckPermissions) {
|
||||
startForCheckPermissions = false;
|
||||
RongCallClient.getInstance().onPermissionDenied();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
} else if (requestCode == REQUEST_CODE_ADD_MEMBER) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (data.getBooleanExtra("remote_hangup", false)) {
|
||||
RLog.d(TAG, "Remote exit, end the call.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (callSession.getEndTime() != 0) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
shouldShowFloat = true;
|
||||
if (resultCode == RESULT_OK) {
|
||||
ArrayList<String> invited = data.getStringArrayListExtra("invited");
|
||||
ArrayList<String> observers = data.getStringArrayListExtra("observers");
|
||||
List<CallUserProfile> callUserProfiles = callSession.getParticipantProfileList();
|
||||
Iterator<String> iterator = invited.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
String id = iterator.next();
|
||||
for (CallUserProfile profile : callUserProfiles) {
|
||||
if (profile.getUserId().equals(id)) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
RongCallClient.getInstance()
|
||||
.addParticipants(callSession.getCallId(), invited, observers);
|
||||
}
|
||||
} else if (requestCode == REQUEST_CODE_ADD_MEMBER_NONE) {
|
||||
try {
|
||||
if (callSession.getEndTime() != 0) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
setShouldShowFloat(true);
|
||||
if (resultCode == RESULT_OK) {
|
||||
ArrayList<String> invited = data.getStringArrayListExtra("pickedIds");
|
||||
RongCallClient.getInstance()
|
||||
.addParticipants(callSession.getCallId(), invited, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onHandFreeButtonClick(View view) {
|
||||
RongCallClient.getInstance().setEnableSpeakerphone(!view.isSelected());
|
||||
view.setSelected(!view.isSelected());
|
||||
handFree = view.isSelected();
|
||||
}
|
||||
|
||||
public void onMuteButtonClick(View view) {
|
||||
RongCallClient.getInstance().setEnableLocalAudio(view.isSelected());
|
||||
view.setSelected(!view.isSelected());
|
||||
muted = view.isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String onSaveFloatBoxState(Bundle bundle) {
|
||||
super.onSaveFloatBoxState(bundle);
|
||||
String intentAction = null;
|
||||
Log.i("audioTag", "onSaveFloatBoxState shouldShowFloat=" + shouldShowFloat);
|
||||
if (shouldShowFloat) {
|
||||
intentAction = getIntent().getAction();
|
||||
bundle.putInt("mediaType", RongCallCommon.CallMediaType.AUDIO.getValue());
|
||||
bundle.putBoolean(KEY_HAND_FREE, handFree);
|
||||
bundle.putBoolean(KEY_MUTED, muted);
|
||||
}
|
||||
return intentAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
return;
|
||||
}
|
||||
|
||||
public void onMinimizeClick(View view) {
|
||||
super.onMinimizeClick(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUserUpdate(UserInfo userInfo) {
|
||||
if (isFinishing()) {
|
||||
return;
|
||||
}
|
||||
TextView callerName =
|
||||
(TextView) audioContainer.findViewWithTag(userInfo.getUserId() + "callerName");
|
||||
|
||||
if (callerName != null && userInfo.getName() != null) {
|
||||
callerName.setLines(1);
|
||||
callerName.setEllipsize(TextUtils.TruncateAt.END);
|
||||
callerName.setText(userInfo.getName());
|
||||
}
|
||||
if (memberContainer != null
|
||||
&& memberContainer.findChildById(userInfo.getUserId()) != null) {
|
||||
memberContainer.updateChildInfo(userInfo.getUserId(), userInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void onHeadsetPlugUpdate(HeadsetInfo headsetInfo) {
|
||||
if (headsetInfo == null || !BluetoothUtil.isForground(MultiAudioCallActivity.this)) {
|
||||
FinLog.v("bugtags", "MultiAudioCallActivity 不在前台!");
|
||||
return;
|
||||
}
|
||||
Log.i(
|
||||
"bugtags",
|
||||
"Insert="
|
||||
+ headsetInfo.isInsert()
|
||||
+ ",headsetInfo.getType="
|
||||
+ headsetInfo.getType().getValue());
|
||||
try {
|
||||
if (headsetInfo.isInsert()) {
|
||||
RongCallClient.getInstance().setEnableSpeakerphone(false);
|
||||
ImageView handFreeV = null;
|
||||
if (null != outgoingLayout) {
|
||||
handFreeV = outgoingLayout.findViewById(R.id.rc_voip_handfree_btn);
|
||||
}
|
||||
if (handFreeV != null) {
|
||||
handFreeV.setSelected(false);
|
||||
handFreeV.setEnabled(false);
|
||||
handFreeV.setClickable(false);
|
||||
}
|
||||
if (headsetInfo.getType() == HeadsetInfo.HeadsetType.BluetoothA2dp) {
|
||||
AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
am.startBluetoothSco();
|
||||
am.setBluetoothScoOn(true);
|
||||
am.setSpeakerphoneOn(false);
|
||||
}
|
||||
} else {
|
||||
if (headsetInfo.getType() == HeadsetInfo.HeadsetType.WiredHeadset
|
||||
&& BluetoothUtil.hasBluetoothA2dpConnected()) {
|
||||
return;
|
||||
}
|
||||
RongCallClient.getInstance().setEnableSpeakerphone(true);
|
||||
ImageView handFreeV = null;
|
||||
if (null != outgoingLayout) {
|
||||
handFreeV = outgoingLayout.findViewById(R.id.rc_voip_handfree_btn);
|
||||
}
|
||||
if (handFreeV != null) {
|
||||
handFreeV.setSelected(true);
|
||||
handFreeV.setEnabled(true);
|
||||
handFreeV.setClickable(true);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
Log.i("bugtags", "MultiAudioCallActivity->onHeadsetPlugUpdate Error=" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.message.MultiCallEndMessage;
|
||||
import io.rong.imkit.conversation.messgelist.provider.BaseNotificationMessageItemProvider;
|
||||
import io.rong.imkit.model.UiMessage;
|
||||
import io.rong.imkit.widget.adapter.IViewProviderListener;
|
||||
import io.rong.imkit.widget.adapter.ViewHolder;
|
||||
import io.rong.imlib.IRongCoreEnum;
|
||||
import io.rong.imlib.model.MessageContent;
|
||||
import java.util.List;
|
||||
|
||||
public class MultiCallEndMessageProvider
|
||||
extends BaseNotificationMessageItemProvider<MultiCallEndMessage> {
|
||||
|
||||
@Override
|
||||
protected ViewHolder onCreateMessageContentViewHolder(ViewGroup parent, int viewType) {
|
||||
View v =
|
||||
LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.rc_voip_msg_multi_call_end, parent, false);
|
||||
return new ViewHolder(parent.getContext(), v);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindMessageContentViewHolder(
|
||||
ViewHolder holder,
|
||||
ViewHolder parentHolder,
|
||||
MultiCallEndMessage multiCallEndMessage,
|
||||
UiMessage uiMessage,
|
||||
int position,
|
||||
List<UiMessage> list,
|
||||
IViewProviderListener<UiMessage> listener) {
|
||||
Context context = holder.getContext();
|
||||
String msg = "";
|
||||
RongCallCommon.CallDisconnectedReason reason = multiCallEndMessage.getReason();
|
||||
IRongCoreEnum.MediaType mediaType = multiCallEndMessage.getMediaType();
|
||||
if (reason == RongCallCommon.CallDisconnectedReason.OTHER_DEVICE_HAD_ACCEPTED) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_call_other);
|
||||
} else if (reason == RongCallCommon.CallDisconnectedReason.REMOTE_HANGUP
|
||||
|| reason == RongCallCommon.CallDisconnectedReason.HANGUP) {
|
||||
if (mediaType == IRongCoreEnum.MediaType.AUDIO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_audio_ended);
|
||||
} else if (mediaType == IRongCoreEnum.MediaType.VIDEO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_video_ended);
|
||||
}
|
||||
} else if (reason == RongCallCommon.CallDisconnectedReason.REMOTE_REJECT
|
||||
|| reason == RongCallCommon.CallDisconnectedReason.REJECT) {
|
||||
if (mediaType == IRongCoreEnum.MediaType.AUDIO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_audio_refuse);
|
||||
} else if (mediaType == IRongCoreEnum.MediaType.VIDEO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_video_refuse);
|
||||
}
|
||||
} else if (reason == RongCallCommon.CallDisconnectedReason.SERVICE_NOT_OPENED
|
||||
|| reason == RongCallCommon.CallDisconnectedReason.REMOTE_ENGINE_UNSUPPORTED) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_engine_notfound);
|
||||
} else if (reason == RongCallCommon.CallDisconnectedReason.CANCEL) {
|
||||
if (mediaType == IRongCoreEnum.MediaType.AUDIO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_audio_cancel);
|
||||
} else if (mediaType == IRongCoreEnum.MediaType.VIDEO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_video_cancel);
|
||||
}
|
||||
} else {
|
||||
if (mediaType == IRongCoreEnum.MediaType.AUDIO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_audio_no_response);
|
||||
} else if (mediaType == IRongCoreEnum.MediaType.VIDEO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_video_no_response);
|
||||
}
|
||||
}
|
||||
TextView tv = holder.getView(R.id.rc_msg);
|
||||
tv.setText(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isMessageViewType(MessageContent messageContent) {
|
||||
return messageContent instanceof MultiCallEndMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spannable getSummarySpannable(Context context, MultiCallEndMessage multiCallEndMessage) {
|
||||
String msg = "";
|
||||
if (multiCallEndMessage.getReason() == RongCallCommon.CallDisconnectedReason.NO_RESPONSE) {
|
||||
if (multiCallEndMessage.getMediaType() == IRongCoreEnum.MediaType.AUDIO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_audio_no_response);
|
||||
} else if (multiCallEndMessage.getMediaType() == IRongCoreEnum.MediaType.VIDEO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_video_no_response);
|
||||
}
|
||||
} else {
|
||||
if (multiCallEndMessage.getMediaType() == IRongCoreEnum.MediaType.AUDIO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_message_audio);
|
||||
} else if (multiCallEndMessage.getMediaType() == IRongCoreEnum.MediaType.VIDEO) {
|
||||
msg = context.getResources().getString(R.string.rc_voip_message_video);
|
||||
}
|
||||
}
|
||||
return new SpannableString(msg);
|
||||
}
|
||||
}
|
1943
callkit/src/main/java/io/rong/callkit/MultiVideoCallActivity.java
Normal file
62
callkit/src/main/java/io/rong/callkit/PickupDetector.java
Normal file
@ -0,0 +1,62 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
|
||||
public class PickupDetector {
|
||||
|
||||
private SensorManager manager;
|
||||
private Sensor mProximitysensor;
|
||||
|
||||
private boolean isPickUp;
|
||||
private PickupDetectListener listener;
|
||||
|
||||
public PickupDetector(Context context) {
|
||||
|
||||
manager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
|
||||
if (manager != null) {
|
||||
mProximitysensor = manager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
|
||||
}
|
||||
}
|
||||
|
||||
SensorEventListener sensorEventListener =
|
||||
new SensorEventListener() {
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent sensorEvent) {
|
||||
|
||||
if (mProximitysensor == null) return;
|
||||
|
||||
float value = sensorEvent.values[0];
|
||||
isPickUp = value < sensorEvent.sensor.getMaximumRange();
|
||||
// 打开或者关闭屏幕
|
||||
if (listener != null) {
|
||||
listener.onPickupDetected(isPickUp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int i) {}
|
||||
};
|
||||
|
||||
public void register(PickupDetectListener listener) {
|
||||
this.listener = listener;
|
||||
if (manager != null) {
|
||||
manager.registerListener(
|
||||
sensorEventListener, mProximitysensor, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
}
|
||||
}
|
||||
|
||||
public void unRegister() {
|
||||
if (manager != null) {
|
||||
manager.unregisterListener(sensorEventListener);
|
||||
}
|
||||
listener = null; // 释放引用。
|
||||
}
|
||||
|
||||
public interface PickupDetectListener {
|
||||
void onPickupDetected(boolean isPickingUp);
|
||||
}
|
||||
}
|
25
callkit/src/main/java/io/rong/callkit/RongCallAction.java
Normal file
@ -0,0 +1,25 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
/** Created by weiqinxiao on 16/3/15. */
|
||||
public enum RongCallAction {
|
||||
ACTION_OUTGOING_CALL(1, "ACTION_OUTGOING_CALL"),
|
||||
ACTION_INCOMING_CALL(2, "ACTION_INCOMING_CALL"),
|
||||
ACTION_ADD_MEMBER(3, "ACTION_ADD_MEMBER"),
|
||||
ACTION_RESUME_CALL(4, "ACTION_RESUME_CALL");
|
||||
|
||||
int value;
|
||||
String msg;
|
||||
|
||||
RongCallAction(int v, String msg) {
|
||||
this.value = v;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return msg;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.SurfaceView;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public interface RongCallCustomerHandlerListener {
|
||||
|
||||
List<String> handleActivityResult(int requestCode, int resultCode, Intent data);
|
||||
|
||||
void addMember(Context context, ArrayList<String> currentMemberIds);
|
||||
|
||||
void onRemoteUserInvited(String userId, RongCallCommon.CallMediaType mediaType);
|
||||
|
||||
void onCallConnected(RongCallSession callSession, SurfaceView localVideo);
|
||||
|
||||
void onCallDisconnected(
|
||||
RongCallSession callSession, RongCallCommon.CallDisconnectedReason reason);
|
||||
|
||||
void onCallMissed(RongCallSession callSession, RongCallCommon.CallDisconnectedReason reason);
|
||||
}
|
379
callkit/src/main/java/io/rong/callkit/RongCallKit.java
Normal file
@ -0,0 +1,379 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
import io.rong.callkit.util.RongCallPermissionUtil;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallMissedListener;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.imlib.RongIMClient;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class RongCallKit {
|
||||
|
||||
public enum CallMediaType {
|
||||
CALL_MEDIA_TYPE_AUDIO,
|
||||
CALL_MEDIA_TYPE_VIDEO
|
||||
}
|
||||
|
||||
public interface ICallUsersProvider {
|
||||
void onGotUserList(ArrayList<String> userIds);
|
||||
}
|
||||
|
||||
private static GroupMembersProvider mGroupMembersProvider;
|
||||
|
||||
private static RongCallCustomerHandlerListener customerHandlerListener;
|
||||
private static GlideCallKitImageEngine kitImageEngine = new GlideCallKitImageEngine();
|
||||
|
||||
/**
|
||||
* 发起单人通话。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param targetId 目标会话 id ,单人通话为对方 UserId ,群组通话为 GroupId ,如果实现的是不基于群组的通话,那此参数无意义,传 null 即可
|
||||
* @param mediaType 会话媒体类型
|
||||
*/
|
||||
public static void startSingleCall(Context context, String targetId, CallMediaType mediaType) {
|
||||
startSingleCallInternal(context, targetId, mediaType, RongCallCommon.RoomType.NORMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起单人跨APP通话。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param targetId 目标会话 id ,单人通话为对方 UserId
|
||||
* @param mediaType 会话媒体类型
|
||||
*/
|
||||
public static void startSingleCrossCall(
|
||||
Context context, String targetId, CallMediaType mediaType) {
|
||||
startSingleCallInternal(context, targetId, mediaType, RongCallCommon.RoomType.CROSS);
|
||||
}
|
||||
|
||||
private static void startSingleCallInternal(
|
||||
Context context,
|
||||
String targetId,
|
||||
CallMediaType mediaType,
|
||||
RongCallCommon.RoomType roomType) {
|
||||
String action;
|
||||
if (mediaType.equals(CallMediaType.CALL_MEDIA_TYPE_AUDIO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEAUDIO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEVIDEO;
|
||||
}
|
||||
Intent intent = new Intent(action);
|
||||
intent.putExtra(
|
||||
"conversationType", Conversation.ConversationType.PRIVATE.getName().toLowerCase());
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("roomType", roomType);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起多人通话
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param conversationType 会话类型
|
||||
* @param targetId 目标会话 id ,单人通话为对方 UserId ,群组通话为 GroupId ,如果实现的是不基于群组的通话,那此参数无意义,传 null 即可
|
||||
* @param mediaType 会话媒体类型
|
||||
* @param userIds 参与者 id 列表
|
||||
*/
|
||||
public static void startMultiCall(
|
||||
Context context,
|
||||
Conversation.ConversationType conversationType,
|
||||
String targetId,
|
||||
CallMediaType mediaType,
|
||||
ArrayList<String> userIds) {
|
||||
if (checkEnvironment(context, mediaType)) {
|
||||
String action;
|
||||
if (mediaType.equals(CallMediaType.CALL_MEDIA_TYPE_AUDIO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO;
|
||||
}
|
||||
|
||||
Intent intent = new Intent(action);
|
||||
userIds.add(RongIMClient.getInstance().getCurrentUserId());
|
||||
intent.putExtra("conversationType", conversationType.getName().toLowerCase());
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.putStringArrayListExtra("invitedUsers", userIds);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始多人通话。 返回当前会话用户列表提供者对象,用户拿到该对象后,异步从服务器取出当前会话用户列表后, 调用提供者中的 onGotUserList 方法,填充
|
||||
* ArrayList<String> userIds 后,就会自动发起多人通话。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param conversationType 会话类型
|
||||
* @param targetId 目标会话 id ,单人通话为对方 UserId ,群组通话为 GroupId ,如果实现的是不基于群组的通话,那此参数无意义,传 null 即可
|
||||
* @param mediaType 通话的媒体类型:CALL_MEDIA_TYPE_AUDIO, CALL_MEDIA_TYPE_VIDEO
|
||||
* @return 返回当前会话用户列表提供者对象
|
||||
*/
|
||||
public static ICallUsersProvider startMultiCall(
|
||||
final Context context,
|
||||
final Conversation.ConversationType conversationType,
|
||||
final String targetId,
|
||||
final CallMediaType mediaType) {
|
||||
return new ICallUsersProvider() {
|
||||
@Override
|
||||
public void onGotUserList(ArrayList<String> userIds) {
|
||||
String action;
|
||||
if (mediaType.equals(CallMediaType.CALL_MEDIA_TYPE_AUDIO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO;
|
||||
}
|
||||
Intent intent = new Intent(action);
|
||||
userIds.add(RongIMClient.getInstance().getCurrentUserId());
|
||||
intent.putExtra("conversationType", conversationType.getName().toLowerCase());
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.putStringArrayListExtra("invitedUsers", userIds);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起的多人通话,不依赖群、讨论组等
|
||||
*
|
||||
* @param context
|
||||
* @param userIds 邀请的成员
|
||||
* @param oberverIds 邀请的以观察者身份加入房间的成员
|
||||
* @param mediaType
|
||||
*/
|
||||
public static void startMultiCall(
|
||||
final Context context,
|
||||
ArrayList<String> userIds,
|
||||
ArrayList<String> oberverIds,
|
||||
final CallMediaType mediaType) {
|
||||
String action;
|
||||
if (mediaType.equals(CallMediaType.CALL_MEDIA_TYPE_AUDIO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO;
|
||||
}
|
||||
Intent intent = new Intent(action);
|
||||
userIds.add(RongIMClient.getInstance().getCurrentUserId());
|
||||
intent.putExtra(
|
||||
"conversationType", Conversation.ConversationType.NONE.getName().toLowerCase());
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.putStringArrayListExtra("invitedUsers", userIds);
|
||||
intent.putStringArrayListExtra("observerUsers", oberverIds);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发起的多人通话,不依赖群、讨论组等
|
||||
*
|
||||
* <p><a href="http://support.rongcloud.cn/kb/Njcy">如何实现不基于于群组的voip</a>
|
||||
*
|
||||
* @param context
|
||||
* @param mediaType
|
||||
* @return
|
||||
*/
|
||||
public static void startMultiCall(
|
||||
final Context context, ArrayList<String> userIds, final CallMediaType mediaType) {
|
||||
String action;
|
||||
if (mediaType.equals(CallMediaType.CALL_MEDIA_TYPE_AUDIO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO;
|
||||
}
|
||||
Intent intent = new Intent(action);
|
||||
userIds.add(RongIMClient.getInstance().getCurrentUserId());
|
||||
intent.putExtra(
|
||||
"conversationType", Conversation.ConversationType.NONE.getName().toLowerCase());
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.putStringArrayListExtra("invitedUsers", userIds);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查应用音视频授权信息 检查网络连接状态 检查是否在通话中
|
||||
*
|
||||
* @param context 启动的 activity
|
||||
* @param mediaType 启动音视频的媒体类型
|
||||
* @return 是否允许启动通话界面
|
||||
*/
|
||||
private static boolean checkEnvironment(Context context, CallMediaType mediaType) {
|
||||
if (context instanceof Activity) {
|
||||
boolean result =
|
||||
RongCallPermissionUtil.checkPermissionByType(
|
||||
context,
|
||||
mediaType == CallMediaType.CALL_MEDIA_TYPE_AUDIO
|
||||
? RongCallCommon.CallMediaType.AUDIO
|
||||
: RongCallCommon.CallMediaType.VIDEO);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInVoipCall(context)) {
|
||||
return false;
|
||||
}
|
||||
if (!RongIMClient.getInstance()
|
||||
.getCurrentConnectionStatus()
|
||||
.equals(RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED)) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getResources().getString(R.string.rc_voip_call_network_error),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在VOIP通话中
|
||||
*
|
||||
* @param context
|
||||
* @return 是否在VOIP通话中
|
||||
*/
|
||||
public static boolean isInVoipCall(Context context) {
|
||||
RongCallSession callSession = RongCallClient.getInstance().getCallSession();
|
||||
if (callSession != null && callSession.getStartTime() > 0) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
callSession.getMediaType() == RongCallCommon.CallMediaType.AUDIO
|
||||
? context.getResources()
|
||||
.getString(R.string.rc_voip_call_audio_start_fail)
|
||||
: context.getResources()
|
||||
.getString(R.string.rc_voip_call_video_start_fail),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 群组成员提供者。 CallKit 本身不保存群组成员,如果在聊天中需要使用群组成员,CallKit 将调用此 Provider 获取群组成员。 */
|
||||
public interface GroupMembersProvider {
|
||||
/**
|
||||
* 获取群组成员列表,用户根据groupId返回对应的群组成员列表。
|
||||
*
|
||||
* @param groupId 群组id
|
||||
* @param result getMemberList可以同步返回,也可以异步返回。 同步返回的情况下,直接返回成员列表。 异步返回的情况下,需要在异步返回的时候调用{@link
|
||||
* OnGroupMembersResult#onGotMemberList(ArrayList)} 来通知CallKit刷新列表。
|
||||
* @return 同步返回的时候返回列表,异步返回直接返回null。
|
||||
*/
|
||||
ArrayList<String> getMemberList(String groupId, OnGroupMembersResult result);
|
||||
}
|
||||
|
||||
/** 群组成员提供者的异步回调接口。 */
|
||||
public interface OnGroupMembersResult {
|
||||
/**
|
||||
* 群组成员提供者的异步回调接口。
|
||||
*
|
||||
* @param members 成员列表。
|
||||
*/
|
||||
void onGotMemberList(ArrayList<String> members);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置群组成员的提供者。
|
||||
*
|
||||
* <p>设置后,当 {@link CallSelectMemberActivity} 界面展示群组成员时,会回调 {@link
|
||||
* GroupMembersProvider#getMemberList(String, OnGroupMembersResult)}, 使用者只需要根据对应的 groupId
|
||||
* 提供对应的群组成员。 如果需要异步从服务器获取群组成员,使用者可以在此方法中发起异步请求,然后返回 null 信息。 在异步请求结果返回后,根据返回的结果调用 {@link
|
||||
* OnGroupMembersResult#onGotMemberList(ArrayList)} 刷新信息。
|
||||
*
|
||||
* @param groupMembersProvider 群组成员提供者。
|
||||
*/
|
||||
public static void setGroupMemberProvider(GroupMembersProvider groupMembersProvider) {
|
||||
mGroupMembersProvider = groupMembersProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群组成员提供者。
|
||||
*
|
||||
* @return 群组成员提供者。
|
||||
*/
|
||||
public static GroupMembersProvider getGroupMemberProvider() {
|
||||
return mGroupMembersProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置通话时用户自定义操作监听。
|
||||
*
|
||||
* <p>CallKit中的Activity是通过action隐式启动,如果用户想继承现有的Activity自定义操作,子类Activity在
|
||||
* AndroidManifest.xml声明后启动该Activity时会弹出提示框让用户选择,这个问题解决方式开发者可以直接把
|
||||
* callKit/AndroidManifest.xml中对应的Activity声明去掉,此Listener提供了另一种实现方案,
|
||||
* RongCallCustomerHandlerListener中并没有定义很多方法,开发者如果需要,可以新增自己的方法
|
||||
*/
|
||||
public static void setCustomerHandlerListener(
|
||||
RongCallCustomerHandlerListener callCustomerHandlerListener) {
|
||||
customerHandlerListener = callCustomerHandlerListener;
|
||||
}
|
||||
|
||||
/** 通话过程中用户自定义操作。 */
|
||||
public static RongCallCustomerHandlerListener getCustomerHandlerListener() {
|
||||
return customerHandlerListener;
|
||||
}
|
||||
|
||||
public static void setRongCallMissedListener(
|
||||
final RongCallMissedListener rongCallMissedListener) {
|
||||
RongCallModule.setMissedCallListener(rongCallMissedListener);
|
||||
}
|
||||
|
||||
// TODO 由于最新CallKit中已经将 RongCallModule#mViewLoaded 默认值改为true,所以不在需要此方法
|
||||
// /**
|
||||
// * 防止 voip 通话页面被会话列表、会话页面或者开发者 app 层页面覆盖。 使用 maven 接入 callkit 的开发者在 app 层主页面的 onCreate
|
||||
// 调用此方法即可。
|
||||
// * 针对导入 callkit 源码的开发者,不使用会话列表和会话页面我们建议在 {@link RongCallModule#onCreate(Context)}方法中设置
|
||||
// * mViewLoaded 为 true 即可。
|
||||
// */
|
||||
// public static void onViewCreated() {
|
||||
// }
|
||||
|
||||
/**
|
||||
* 忽略 voip 来电,不弹出来电界面,直接挂断。
|
||||
*
|
||||
* @param ignore true 时忽略来电,false 恢复默认值接收来电,弹出来电界面。 此接口针对音视频会议过程中不能被 voip 打断等的细分场景
|
||||
*/
|
||||
public static void ignoreIncomingCall(boolean ignore) {
|
||||
RongCallModule.ignoreIncomingCall(ignore);
|
||||
}
|
||||
|
||||
public static void setMainPageActivityClass(String[] className) {
|
||||
RongCallModule.setMainPageActivity(className);
|
||||
}
|
||||
|
||||
public static String getVersion() {
|
||||
return BuildConfig.VERSION_NAME;
|
||||
}
|
||||
|
||||
public static void setKitImageEngine(GlideCallKitImageEngine kitImageEngine) {
|
||||
RongCallKit.kitImageEngine = kitImageEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义头像engine
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static GlideCallKitImageEngine getKitImageEngine() {
|
||||
return kitImageEngine;
|
||||
}
|
||||
|
||||
public static GroupMembersProvider getmGroupMembersProvider() {
|
||||
return mGroupMembersProvider;
|
||||
}
|
||||
}
|
485
callkit/src/main/java/io/rong/callkit/RongCallModule.java
Normal file
@ -0,0 +1,485 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Application;
|
||||
import android.app.Application.ActivityLifecycleCallbacks;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import cn.rongcloud.rtc.api.RCRTCAudioRouteManager;
|
||||
import io.rong.callkit.util.ActivityStartCheckUtils;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.calllib.IRongReceivedCallListener;
|
||||
import io.rong.calllib.ReportUtil;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallMissedListener;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.calllib.message.CallSTerminateMessage;
|
||||
import io.rong.calllib.message.MultiCallEndMessage;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.IMCenter;
|
||||
import io.rong.imkit.config.RongConfigCenter;
|
||||
import io.rong.imkit.conversation.extension.IExtensionModule;
|
||||
import io.rong.imkit.conversation.extension.RongExtension;
|
||||
import io.rong.imkit.conversation.extension.component.emoticon.IEmoticonTab;
|
||||
import io.rong.imkit.conversation.extension.component.plugin.IPluginModule;
|
||||
import io.rong.imlib.IRongCoreEnum;
|
||||
import io.rong.imlib.RongIMClient;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import io.rong.imlib.model.Message;
|
||||
import io.rong.push.RongPushClient;
|
||||
import io.rong.push.notification.PushNotificationMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Created by weiqinxiao on 16/8/15. */
|
||||
public class RongCallModule implements IExtensionModule {
|
||||
private static final String TAG = "RongCallModule";
|
||||
|
||||
private RongCallSession mCallSession;
|
||||
private boolean mStartForCheckPermissions;
|
||||
private static boolean mViewLoaded = true;
|
||||
private Context mContext;
|
||||
private static RongCallMissedListener missedListener;
|
||||
private static boolean ignoreIncomingCall;
|
||||
private Application mApplication;
|
||||
|
||||
public RongCallModule() {
|
||||
RLog.i(TAG, "Constructor");
|
||||
}
|
||||
|
||||
private void initMissedCallListener() {
|
||||
RongCallClient.setMissedCallListener(
|
||||
new RongCallMissedListener() {
|
||||
@Override
|
||||
public void onRongCallMissed(
|
||||
RongCallSession callSession,
|
||||
RongCallCommon.CallDisconnectedReason reason) {
|
||||
if (!TextUtils.isEmpty(callSession.getInviterUserId())) {
|
||||
long insertTime = callSession.getEndTime();
|
||||
if (insertTime == 0) {
|
||||
insertTime = callSession.getStartTime();
|
||||
}
|
||||
if (callSession.getConversationType()
|
||||
== Conversation.ConversationType.PRIVATE) {
|
||||
CallSTerminateMessage message = new CallSTerminateMessage();
|
||||
message.setReason(reason);
|
||||
message.setMediaType(callSession.getMediaType());
|
||||
|
||||
String extra;
|
||||
long time =
|
||||
(callSession.getEndTime() - callSession.getStartTime())
|
||||
/ 1000;
|
||||
if (time >= 3600) {
|
||||
extra =
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"%d:%02d:%02d",
|
||||
time / 3600,
|
||||
(time % 3600) / 60,
|
||||
(time % 60));
|
||||
} else {
|
||||
extra =
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"%02d:%02d",
|
||||
(time % 3600) / 60,
|
||||
(time % 60));
|
||||
}
|
||||
message.setExtra(extra);
|
||||
|
||||
String senderId = callSession.getInviterUserId();
|
||||
if (senderId.equals(callSession.getSelfUserId())) {
|
||||
message.setDirection("MO");
|
||||
IMCenter.getInstance()
|
||||
.insertOutgoingMessage(
|
||||
Conversation.ConversationType.PRIVATE,
|
||||
callSession.getTargetId(),
|
||||
io.rong.imlib.model.Message.SentStatus.SENT,
|
||||
message,
|
||||
insertTime,
|
||||
null);
|
||||
} else {
|
||||
message.setDirection("MT");
|
||||
io.rong.imlib.model.Message.ReceivedStatus receivedStatus =
|
||||
new io.rong.imlib.model.Message.ReceivedStatus(0);
|
||||
IMCenter.getInstance()
|
||||
.insertIncomingMessage(
|
||||
Conversation.ConversationType.PRIVATE,
|
||||
callSession.getTargetId(),
|
||||
senderId,
|
||||
CallKitUtils.getReceivedStatus(reason),
|
||||
message,
|
||||
insertTime,
|
||||
null);
|
||||
}
|
||||
} else if (callSession.getConversationType()
|
||||
== Conversation.ConversationType.GROUP) {
|
||||
MultiCallEndMessage multiCallEndMessage = new MultiCallEndMessage();
|
||||
multiCallEndMessage.setReason(reason);
|
||||
if (callSession.getMediaType()
|
||||
== RongCallCommon.CallMediaType.AUDIO) {
|
||||
multiCallEndMessage.setMediaType(IRongCoreEnum.MediaType.AUDIO);
|
||||
} else if (callSession.getMediaType()
|
||||
== RongCallCommon.CallMediaType.VIDEO) {
|
||||
multiCallEndMessage.setMediaType(IRongCoreEnum.MediaType.VIDEO);
|
||||
}
|
||||
IMCenter.getInstance()
|
||||
.insertIncomingMessage(
|
||||
callSession.getConversationType(),
|
||||
callSession.getTargetId(),
|
||||
callSession.getCallerUserId(),
|
||||
CallKitUtils.getReceivedStatus(reason),
|
||||
multiCallEndMessage,
|
||||
insertTime,
|
||||
null);
|
||||
}
|
||||
}
|
||||
if (missedListener != null) {
|
||||
missedListener.onRongCallMissed(callSession, reason);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void setMissedCallListener(RongCallMissedListener listener) {
|
||||
missedListener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动通话界面
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param callSession 通话实体
|
||||
* @param startForCheckPermissions android6.0需要实时获取应用权限。
|
||||
* 当需要实时获取权限时,设置startForCheckPermissions为true, 其它情况下设置为false。
|
||||
*/
|
||||
private void startVoIPActivity(
|
||||
Context context, final RongCallSession callSession, boolean startForCheckPermissions) {
|
||||
RLog.d(
|
||||
TAG,
|
||||
"startVoIPActivity.ignoreIncomingCall : "
|
||||
+ ignoreIncomingCall
|
||||
+ " , AndroidVersion :"
|
||||
+ Build.VERSION.SDK_INT
|
||||
+ " ,startForCheckPermissions : "
|
||||
+ startForCheckPermissions);
|
||||
if (ignoreIncomingCall) {
|
||||
RongCallClient.getInstance().hangUpCall();
|
||||
return;
|
||||
}
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.RECEIVE_CALL_LISTENER,
|
||||
callSession,
|
||||
"state|desc",
|
||||
"startVoIPActivity",
|
||||
Build.VERSION.SDK_INT);
|
||||
// 在 Android 10 以上版本不再允许后台运行 Activity
|
||||
if (Build.VERSION.SDK_INT < 29 || isAppOnForeground(context)) {
|
||||
context.startActivity(createVoIPIntent(context, callSession, startForCheckPermissions));
|
||||
} else {
|
||||
onSendBroadcast(context, callSession, startForCheckPermissions);
|
||||
}
|
||||
mCallSession = null;
|
||||
}
|
||||
|
||||
private void onSendBroadcast(
|
||||
Context context, RongCallSession callSession, boolean startForCheckPermissions) {
|
||||
RLog.d(TAG, "onSendBroadcast");
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
// intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
|
||||
intent.putExtra("message", transformToPushMessage(context, callSession));
|
||||
intent.putExtra("callsession", callSession);
|
||||
intent.putExtra("checkPermissions", startForCheckPermissions);
|
||||
intent.setAction(VoIPBroadcastReceiver.ACTION_CALLINVITEMESSAGE);
|
||||
context.sendBroadcast(intent);
|
||||
}
|
||||
|
||||
public static Intent createVoIPIntent(
|
||||
Context context, RongCallSession callSession, boolean startForCheckPermissions) {
|
||||
Intent intent;
|
||||
String action;
|
||||
if (callSession.getConversationType().equals(Conversation.ConversationType.DISCUSSION)
|
||||
|| callSession.getConversationType().equals(Conversation.ConversationType.GROUP)
|
||||
|| callSession.getConversationType().equals(Conversation.ConversationType.NONE)) {
|
||||
if (callSession.getMediaType().equals(RongCallCommon.CallMediaType.VIDEO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO;
|
||||
}
|
||||
intent = new Intent(action);
|
||||
intent.putExtra("callSession", callSession);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_INCOMING_CALL.getName());
|
||||
if (startForCheckPermissions) {
|
||||
intent.putExtra("checkPermissions", true);
|
||||
} else {
|
||||
intent.putExtra("checkPermissions", false);
|
||||
}
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
} else {
|
||||
if (callSession.getMediaType().equals(RongCallCommon.CallMediaType.VIDEO)) {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEVIDEO;
|
||||
} else {
|
||||
action = RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEAUDIO;
|
||||
}
|
||||
intent = new Intent(action);
|
||||
intent.putExtra("callSession", callSession);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_INCOMING_CALL.getName());
|
||||
if (startForCheckPermissions) {
|
||||
intent.putExtra("checkPermissions", true);
|
||||
} else {
|
||||
intent.putExtra("checkPermissions", false);
|
||||
}
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
}
|
||||
return intent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 RongCallSession 转换为 PushNotificationMessage
|
||||
*
|
||||
* @param session
|
||||
* @return
|
||||
*/
|
||||
private PushNotificationMessage transformToPushMessage(
|
||||
Context context, RongCallSession session) {
|
||||
PushNotificationMessage pushMsg = new PushNotificationMessage();
|
||||
// pushMsg.setPushContent(session.getMediaType() ==
|
||||
// RongCallCommon.CallMediaType.AUDIO ? "音频电话呼叫" : "视频电话呼叫");
|
||||
pushMsg.setPushTitle(
|
||||
(String)
|
||||
context.getPackageManager()
|
||||
.getApplicationLabel(context.getApplicationInfo()));
|
||||
pushMsg.setConversationType(
|
||||
RongPushClient.ConversationType.setValue(session.getConversationType().getValue()));
|
||||
pushMsg.setTargetId(session.getTargetId());
|
||||
pushMsg.setTargetUserName("");
|
||||
pushMsg.setSenderId(session.getCallerUserId());
|
||||
pushMsg.setSenderName("");
|
||||
pushMsg.setObjectName("RC:VCInvite");
|
||||
pushMsg.setPushFlag("false");
|
||||
pushMsg.setToId(RongIMClient.getInstance().getCurrentUserId());
|
||||
pushMsg.setSourceType(PushNotificationMessage.PushSourceType.LOCAL_MESSAGE);
|
||||
// pushMsg.setPushId(session.getUId());
|
||||
return pushMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断应用是否处于前台
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
private boolean isAppOnForeground(Context context) {
|
||||
if (context == null) return false;
|
||||
ActivityManager activityManager =
|
||||
(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> appProcesses =
|
||||
activityManager.getRunningAppProcesses();
|
||||
if (appProcesses == null) return false;
|
||||
String apkName = context.getPackageName();
|
||||
|
||||
for (ActivityManager.RunningAppProcessInfo app : appProcesses) {
|
||||
if (TextUtils.equals(apkName, app.processName)
|
||||
&& ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
|
||||
== app.importance) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void ignoreIncomingCall(boolean ignore) {
|
||||
ignoreIncomingCall = ignore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInit(Context context, String appKey) {
|
||||
RLog.d(TAG, "onInit");
|
||||
mContext = context.getApplicationContext();
|
||||
registerLifecycleCallbacks(mContext);
|
||||
RongConfigCenter.conversationConfig().addMessageProvider(new CallEndMessageItemProvider());
|
||||
RongConfigCenter.conversationConfig().addMessageProvider(new MultiCallEndMessageProvider());
|
||||
initMissedCallListener();
|
||||
|
||||
IRongReceivedCallListener callListener =
|
||||
new IRongReceivedCallListener() {
|
||||
@Override
|
||||
public void onReceivedCall(final RongCallSession callSession) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.RECEIVED_CALL,
|
||||
"mViewLoaded|session",
|
||||
mViewLoaded,
|
||||
callSession);
|
||||
RLog.d(TAG, "onReceivedCall.mViewLoaded :" + mViewLoaded);
|
||||
if (mViewLoaded) {
|
||||
startVoIPActivity(mContext, callSession, false);
|
||||
} else {
|
||||
mCallSession = callSession;
|
||||
}
|
||||
RCRTCAudioRouteManager.getInstance().init(mContext.getApplicationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckPermission(RongCallSession callSession) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CHECK_PERMISSION,
|
||||
"mViewLoaded|session",
|
||||
mViewLoaded,
|
||||
callSession);
|
||||
RLog.d(TAG, "onCheckPermissions.mViewLoaded : " + mViewLoaded);
|
||||
mCallSession = callSession;
|
||||
if (mViewLoaded) {
|
||||
startVoIPActivity(mContext, callSession, true);
|
||||
} else {
|
||||
mStartForCheckPermissions = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
RongCallClient.setReceivedCallListener(callListener);
|
||||
ActivityStartCheckUtils.getInstance().registerActivityLifecycleCallbacks(context);
|
||||
IMCenter.getInstance()
|
||||
.addConnectStatusListener(
|
||||
new RongIMClient.ConnectCallback() {
|
||||
@Override
|
||||
public void onSuccess(String t) {
|
||||
if (RongCallClient.getInstance() != null) {
|
||||
RongCallClient.getInstance()
|
||||
.setVoIPCallListener(RongCallProxy.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(RongIMClient.ConnectionErrorCode e) {
|
||||
if (RongCallClient.getInstance() != null) {
|
||||
RongCallClient.getInstance()
|
||||
.setVoIPCallListener(RongCallProxy.getInstance());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDatabaseOpened(RongIMClient.DatabaseOpenStatus code) {}
|
||||
});
|
||||
}
|
||||
|
||||
private void registerLifecycleCallbacks(Context context) {
|
||||
RLog.d(TAG, "registerLifecycleCallbacks");
|
||||
mApplication = (Application) context;
|
||||
|
||||
if (mApplication == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mApplication.registerActivityLifecycleCallbacks(myActivityLifecycleCallbacks);
|
||||
}
|
||||
|
||||
private ActivityLifecycleCallbacks myActivityLifecycleCallbacks =
|
||||
new ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
|
||||
RLog.d(TAG, "onActivityCreated ---- : " + activity);
|
||||
|
||||
if (mActivities == null || mActivities.size() == 0) {
|
||||
RLog.d(TAG, "onActivityCreated . mainPageClass is empty.");
|
||||
return;
|
||||
}
|
||||
String className1 = activity.getClass().getName();
|
||||
mActivities.remove(className1);
|
||||
if (mActivities.size() == 0) {
|
||||
retryStartVoIPActivity();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {}
|
||||
};
|
||||
|
||||
private void retryStartVoIPActivity() {
|
||||
RLog.i(
|
||||
TAG,
|
||||
"Find the exact class, change mViewLoaded as true . mCallSession ==null ?"
|
||||
+ (mCallSession == null));
|
||||
mViewLoaded = true;
|
||||
if (mCallSession != null) {
|
||||
startVoIPActivity(mContext, mCallSession, mStartForCheckPermissions);
|
||||
mStartForCheckPermissions = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToExtension(Fragment fragment, RongExtension extension) {
|
||||
RLog.d(TAG, "onAttachedToExtension");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromExtension() {
|
||||
RLog.d(TAG, "onDetachedFromExtension");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceivedMessage(Message message) {}
|
||||
|
||||
@Override
|
||||
public List<IPluginModule> getPluginModules(Conversation.ConversationType conversationType) {
|
||||
RLog.d(TAG, "getPluginModules");
|
||||
List<IPluginModule> pluginModules = new ArrayList<>();
|
||||
try {
|
||||
if (RongCallClient.getInstance().isVoIPEnabled(mContext)) {
|
||||
pluginModules.add(new AudioPlugin());
|
||||
pluginModules.add(new VideoPlugin());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
RLog.i(TAG, "getPlugins()->Error :" + e.getMessage());
|
||||
}
|
||||
return pluginModules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IEmoticonTab> getEmoticonTabs() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect() {
|
||||
RLog.d(TAG, "onDisconnect");
|
||||
}
|
||||
|
||||
private static ArrayList<String> mActivities;
|
||||
|
||||
/** 设置可能会覆盖音视频通话页面的类,比如主页面。设置后,如果此页面尚未打开,即使收到音视频呼叫,也会暂缓唤起页面,会再设置的页面启动成功后,再尝试启动音视频通话页面。 */
|
||||
public static void setMainPageActivity(String[] className) {
|
||||
if (className != null && className.length > 0) {
|
||||
int length = className.length;
|
||||
RLog.i(TAG, "setMainPageActivity.length :" + length);
|
||||
mActivities = new ArrayList<>();
|
||||
mViewLoaded = false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
mActivities.add(className[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
422
callkit/src/main/java/io/rong/callkit/RongCallProxy.java
Normal file
@ -0,0 +1,422 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.view.SurfaceView;
|
||||
import cn.rongcloud.rtc.api.RCRTCAudioRouteManager;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.callkit.util.IncomingCallExtraHandleUtil;
|
||||
import io.rong.calllib.IRongCallListener;
|
||||
import io.rong.calllib.ReportUtil;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallCommon.CallMediaType;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.calllib.message.CallSTerminateMessage;
|
||||
import io.rong.calllib.message.MultiCallEndMessage;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.IMCenter;
|
||||
import io.rong.imlib.IRongCoreEnum;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/** Created by jiangecho on 2016/10/27. */
|
||||
public class RongCallProxy implements IRongCallListener {
|
||||
|
||||
private static final String TAG = "RongCallProxy";
|
||||
private IRongCallListener mCallListener;
|
||||
private Queue<CallDisconnectedInfo> mCachedCallQueue;
|
||||
private static RongCallProxy mInstance;
|
||||
|
||||
private RongCallProxy() {
|
||||
mCachedCallQueue = new LinkedBlockingQueue<>();
|
||||
}
|
||||
|
||||
public static synchronized RongCallProxy getInstance() {
|
||||
if (mInstance == null) {
|
||||
mInstance = new RongCallProxy();
|
||||
}
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
public void setCallListener(IRongCallListener listener) {
|
||||
RLog.d(TAG, "setCallListener listener = " + listener);
|
||||
this.mCallListener = listener;
|
||||
// if (listener != null) {
|
||||
// CallDisconnectedInfo callDisconnectedInfo = mCachedCallQueue.poll();
|
||||
// if (callDisconnectedInfo != null) {
|
||||
// listener.onCallDisconnected(callDisconnectedInfo.mCallSession,
|
||||
// callDisconnectedInfo.mReason);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallIncoming(RongCallSession callSession, SurfaceView localVideo) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
callSession,
|
||||
"state|desc",
|
||||
"onCallIncoming",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onCallIncoming(callSession, localVideo);
|
||||
}
|
||||
if (RongCallClient.getInstance().getContext() != null) {
|
||||
RCRTCAudioRouteManager.getInstance()
|
||||
.init(RongCallClient.getInstance().getContext().getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallOutgoing(RongCallSession callSession, SurfaceView localVideo) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
callSession,
|
||||
"state|desc",
|
||||
"onCallOutgoing",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onCallOutgoing(callSession, localVideo);
|
||||
}
|
||||
if (RongCallClient.getInstance().getContext() != null) {
|
||||
RCRTCAudioRouteManager.getInstance()
|
||||
.init(RongCallClient.getInstance().getContext().getApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallConnected(RongCallSession callSession, SurfaceView localVideo) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
callSession,
|
||||
"state|desc",
|
||||
"onCallConnected",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onCallConnected(callSession, localVideo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCallDisconnected(
|
||||
RongCallSession callSession, RongCallCommon.CallDisconnectedReason reason) {
|
||||
RLog.d(TAG, "RongCallProxy onCallDisconnected mCallListener = " + mCallListener);
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
callSession,
|
||||
"state|reason|desc",
|
||||
"onCallDisconnected",
|
||||
reason.getValue(),
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onCallDisconnected(callSession, reason);
|
||||
} else if (!IncomingCallExtraHandleUtil.needNotify()) {
|
||||
mCachedCallQueue.offer(new CallDisconnectedInfo(callSession, reason));
|
||||
} else { // android 10 后台来电,被叫端不响应,主叫挂断时 mCallListener 为空 ,需要生成通话记录
|
||||
insertCallLogMessage(callSession, reason);
|
||||
}
|
||||
// 取消耳机监听
|
||||
RCRTCAudioRouteManager.getInstance().unInit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserRinging(String userId) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|desc",
|
||||
userId,
|
||||
"onRemoteUserRinging",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserRinging(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserAccept(String userId, CallMediaType mediaType) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|desc",
|
||||
userId,
|
||||
"onRemoteUserAccept",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserAccept(userId, mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserJoined(
|
||||
String userId,
|
||||
RongCallCommon.CallMediaType mediaType,
|
||||
int userType,
|
||||
SurfaceView remoteVideo) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|desc",
|
||||
userId,
|
||||
"onRemoteUserJoined",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserJoined(userId, mediaType, userType, remoteVideo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserInvited(String userId, RongCallCommon.CallMediaType mediaType) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|desc",
|
||||
userId,
|
||||
"onRemoteUserInvited",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserInvited(userId, mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteUserLeft(String userId, RongCallCommon.CallDisconnectedReason reason) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|reason|desc",
|
||||
userId,
|
||||
"onRemoteUserLeft",
|
||||
reason.getValue(),
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserLeft(userId, reason);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaTypeChanged(
|
||||
String userId, RongCallCommon.CallMediaType mediaType, SurfaceView video) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|mediaType|desc",
|
||||
userId,
|
||||
"onMediaTypeChanged",
|
||||
mediaType.getValue(),
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onMediaTypeChanged(userId, mediaType, video);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(RongCallCommon.CallErrorCode errorCode) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"state|code|desc",
|
||||
"onError",
|
||||
errorCode.getValue(),
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onError(errorCode);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteCameraDisabled(String userId, boolean disabled) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|disabled|desc",
|
||||
userId,
|
||||
"onRemoteCameraDisabled",
|
||||
disabled,
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteCameraDisabled(userId, disabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoteMicrophoneDisabled(String userId, boolean disabled) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|disabled|desc",
|
||||
userId,
|
||||
"onRemoteMicrophoneDisabled",
|
||||
disabled,
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteMicrophoneDisabled(userId, disabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkSendLost(int lossRate, int delay) {
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onNetworkSendLost(lossRate, delay);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstRemoteVideoFrame(String userId, int height, int width) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|desc",
|
||||
userId,
|
||||
"onFirstRemoteVideoFrame",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onFirstRemoteVideoFrame(userId, height, width);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstRemoteAudioFrame(String userId) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|desc",
|
||||
userId,
|
||||
"onFirstRemoteAudioFrame",
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onFirstRemoteAudioFrame(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioLevelSend(String audioLevel) {
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onAudioLevelSend(audioLevel);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRemoteUserPublishVideoStream(
|
||||
String userId, String streamId, String tag, SurfaceView surfaceView) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|streamId|desc",
|
||||
userId,
|
||||
"onRemoteUserPublishVideoStream",
|
||||
streamId,
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserPublishVideoStream(userId, streamId, tag, surfaceView);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAudioLevelReceive(HashMap<String, String> audioLevel) {
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onAudioLevelReceive(audioLevel);
|
||||
}
|
||||
}
|
||||
|
||||
public void onRemoteUserUnpublishVideoStream(String userId, String streamId, String tag) {
|
||||
ReportUtil.appStatus(
|
||||
ReportUtil.TAG.CALL_LISTENER,
|
||||
"userId|state|streamId|desc",
|
||||
userId,
|
||||
"onRemoteUserUnpublishVideoStream",
|
||||
streamId,
|
||||
getDescription());
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onRemoteUserUnpublishVideoStream(userId, streamId, tag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkReceiveLost(String userId, int lossRate) {
|
||||
if (mCallListener != null) {
|
||||
mCallListener.onNetworkReceiveLost(userId, lossRate);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CallDisconnectedInfo {
|
||||
RongCallSession mCallSession;
|
||||
RongCallCommon.CallDisconnectedReason mReason;
|
||||
|
||||
public CallDisconnectedInfo(
|
||||
RongCallSession callSession, RongCallCommon.CallDisconnectedReason reason) {
|
||||
this.mCallSession = callSession;
|
||||
this.mReason = reason;
|
||||
}
|
||||
}
|
||||
|
||||
private String getDescription() {
|
||||
if (mCallListener != null) {
|
||||
return mCallListener.getClass().getSimpleName();
|
||||
}
|
||||
return "no callListener set";
|
||||
}
|
||||
|
||||
private void insertCallLogMessage(
|
||||
RongCallSession callSession, RongCallCommon.CallDisconnectedReason reason) {
|
||||
if (!TextUtils.isEmpty(callSession.getInviterUserId())) {
|
||||
long insertTime = callSession.getEndTime();
|
||||
if (insertTime == 0) {
|
||||
insertTime = callSession.getStartTime();
|
||||
}
|
||||
if (callSession.getConversationType() == Conversation.ConversationType.PRIVATE) {
|
||||
CallSTerminateMessage message = new CallSTerminateMessage();
|
||||
message.setReason(reason);
|
||||
message.setMediaType(callSession.getMediaType());
|
||||
|
||||
String extra;
|
||||
long time = (callSession.getEndTime() - callSession.getStartTime()) / 1000;
|
||||
if (time >= 3600) {
|
||||
extra =
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"%d:%02d:%02d",
|
||||
time / 3600,
|
||||
(time % 3600) / 60,
|
||||
(time % 60));
|
||||
} else {
|
||||
extra =
|
||||
String.format(
|
||||
Locale.ROOT, "%02d:%02d", (time % 3600) / 60, (time % 60));
|
||||
}
|
||||
message.setExtra(extra);
|
||||
|
||||
String senderId = callSession.getInviterUserId();
|
||||
if (senderId.equals(callSession.getSelfUserId())) {
|
||||
message.setDirection("MO");
|
||||
IMCenter.getInstance()
|
||||
.insertOutgoingMessage(
|
||||
Conversation.ConversationType.PRIVATE,
|
||||
callSession.getTargetId(),
|
||||
io.rong.imlib.model.Message.SentStatus.SENT,
|
||||
message,
|
||||
insertTime,
|
||||
null);
|
||||
} else {
|
||||
message.setDirection("MT");
|
||||
IMCenter.getInstance()
|
||||
.insertIncomingMessage(
|
||||
Conversation.ConversationType.PRIVATE,
|
||||
callSession.getTargetId(),
|
||||
senderId,
|
||||
CallKitUtils.getReceivedStatus(reason),
|
||||
message,
|
||||
insertTime,
|
||||
null);
|
||||
}
|
||||
} else if (callSession.getConversationType() == Conversation.ConversationType.GROUP) {
|
||||
MultiCallEndMessage multiCallEndMessage = new MultiCallEndMessage();
|
||||
multiCallEndMessage.setReason(reason);
|
||||
if (callSession.getMediaType() == RongCallCommon.CallMediaType.AUDIO) {
|
||||
multiCallEndMessage.setMediaType(IRongCoreEnum.MediaType.AUDIO);
|
||||
} else if (callSession.getMediaType() == RongCallCommon.CallMediaType.VIDEO) {
|
||||
multiCallEndMessage.setMediaType(IRongCoreEnum.MediaType.VIDEO);
|
||||
}
|
||||
IMCenter.getInstance()
|
||||
.insertIncomingMessage(
|
||||
callSession.getConversationType(),
|
||||
callSession.getTargetId(),
|
||||
callSession.getCallerUserId(),
|
||||
CallKitUtils.getReceivedStatus(reason),
|
||||
multiCallEndMessage,
|
||||
insertTime,
|
||||
null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,317 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.view.View;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import io.rong.callkit.util.CallRingingUtil;
|
||||
import io.rong.callkit.util.IncomingCallExtraHandleUtil;
|
||||
import io.rong.callkit.util.RingingMode;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.push.notification.PushNotificationMessage;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/** @author gusd @Date 2021/09/01 */
|
||||
public class RongIncomingCallService {
|
||||
private static final String TAG = "IncomingCallService";
|
||||
public static final int ACCEPT_REQUEST_CODE = 145679;
|
||||
public static final int HANGUP_REQUEST_CODE = 145678;
|
||||
private static int notificationId = 4000;
|
||||
|
||||
public static final String KEY_MESSAGE = "message";
|
||||
public static final String KEY_CALL_SESSION = "callsession";
|
||||
public static final String KEY_CHECK_PERMISSIONS = "checkPermissions";
|
||||
public static final String KEY_NEED_AUTO_ANSWER = "needAutoAnswer";
|
||||
|
||||
/** 该服务最长存活时间,60 秒 */
|
||||
private static final long SERVICE_MAX_ALIVE_TIME = 60 * 1000L;
|
||||
|
||||
private static final String TAG_KILL_INCOMING_SERVICE = "TAG_KILL_INCOMING_SERVICE";
|
||||
|
||||
public static final String ACTION_CALLINVITEMESSAGE_CLICKED =
|
||||
"action.push.CallInviteMessage.CLICKED";
|
||||
|
||||
private Handler mHandler;
|
||||
private AtomicBoolean isRinging = new AtomicBoolean(false);
|
||||
|
||||
private RongIncomingCallService() {}
|
||||
|
||||
public static RongIncomingCallService getInstance() {
|
||||
return RongIncomingCallHolder.instance;
|
||||
}
|
||||
|
||||
private static class RongIncomingCallHolder {
|
||||
static RongIncomingCallService instance = new RongIncomingCallService();
|
||||
}
|
||||
|
||||
public boolean isRinging() {
|
||||
return isRinging.get();
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
public void startRing(final Context context, Intent intent) {
|
||||
RLog.d(TAG, "onStartCommand: ");
|
||||
if (isRinging.get()) {
|
||||
return;
|
||||
}
|
||||
|
||||
PushNotificationMessage message = intent.getParcelableExtra(KEY_MESSAGE);
|
||||
RongCallSession callSession = intent.getParcelableExtra(KEY_CALL_SESSION);
|
||||
boolean checkPermission = intent.getBooleanExtra(KEY_CHECK_PERMISSIONS, false);
|
||||
if (message == null || callSession == null) {
|
||||
return;
|
||||
}
|
||||
RLog.d(TAG, "onStartCommand : " + "callId = " + callSession.getCallId());
|
||||
|
||||
mHandler = new Handler(Looper.myLooper());
|
||||
mHandler.postAtTime(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
stopRinging(context);
|
||||
}
|
||||
},
|
||||
TAG_KILL_INCOMING_SERVICE,
|
||||
SystemClock.uptimeMillis() + SERVICE_MAX_ALIVE_TIME);
|
||||
|
||||
wakeUpAndUnlock(context);
|
||||
|
||||
try {
|
||||
PendingIntent answerPendingIntent =
|
||||
createAnswerIntent(
|
||||
context,
|
||||
message,
|
||||
callSession,
|
||||
checkPermission,
|
||||
IncomingCallExtraHandleUtil.VOIP_REQUEST_CODE,
|
||||
false);
|
||||
PendingIntent hangupPendingIntent = createHangupIntent(context, callSession);
|
||||
PendingIntent openAppIntent =
|
||||
createOpenAppPendingIntent(
|
||||
context,
|
||||
message,
|
||||
callSession,
|
||||
checkPermission,
|
||||
IncomingCallExtraHandleUtil.VOIP_REQUEST_CODE,
|
||||
false);
|
||||
CallRingingUtil.getInstance().createNotificationChannel(context);
|
||||
|
||||
int smallIcon =
|
||||
context.getResources()
|
||||
.getIdentifier(
|
||||
"notification_small_icon",
|
||||
"drawable",
|
||||
context.getPackageName());
|
||||
if (smallIcon <= 0) {
|
||||
smallIcon = context.getApplicationInfo().icon;
|
||||
}
|
||||
androidx.media.app.NotificationCompat.MediaStyle mediaStyle =
|
||||
new androidx.media.app.NotificationCompat.MediaStyle();
|
||||
mediaStyle.setShowCancelButton(false);
|
||||
mediaStyle.setCancelButtonIntent(hangupPendingIntent);
|
||||
mediaStyle.setShowActionsInCompactView(0, 1);
|
||||
|
||||
NotificationCompat.Builder notificationBuilder =
|
||||
new NotificationCompat.Builder(
|
||||
context,
|
||||
CallRingingUtil.getInstance().getNotificationChannelId())
|
||||
.setContentText(message.getPushContent())
|
||||
.setContentTitle(message.getPushTitle())
|
||||
.setSmallIcon(smallIcon)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setAutoCancel(true)
|
||||
.setOngoing(true)
|
||||
.setStyle(mediaStyle)
|
||||
.setContentIntent(openAppIntent)
|
||||
.setFullScreenIntent(openAppIntent, true);
|
||||
|
||||
int notificationHangupIcon = CallRingingUtil.getInstance().getNotificationHangupIcon();
|
||||
if (notificationHangupIcon != View.NO_ID && notificationHangupIcon != 0) {
|
||||
notificationBuilder.addAction(
|
||||
R.drawable.rc_voip_notification_hangup,
|
||||
context.getString(R.string.rc_voip_hangup),
|
||||
hangupPendingIntent);
|
||||
}
|
||||
|
||||
int notificationAnswerIcon = CallRingingUtil.getInstance().getNotificationAnswerIcon();
|
||||
if (notificationAnswerIcon != View.NO_ID && notificationAnswerIcon != 0) {
|
||||
notificationBuilder.addAction(
|
||||
R.drawable.rc_voip_notification_answer,
|
||||
context.getString(R.string.rc_voip_answer),
|
||||
answerPendingIntent);
|
||||
}
|
||||
|
||||
Notification notification = notificationBuilder.build();
|
||||
|
||||
NotificationManager notificationManager =
|
||||
context.getSystemService(NotificationManager.class);
|
||||
if (notificationManager != null) {
|
||||
notificationManager.notify(++notificationId, notification);
|
||||
}
|
||||
CallRingingUtil.getInstance().startRinging(context, RingingMode.Incoming);
|
||||
isRinging.set(true);
|
||||
} catch (Exception e) {
|
||||
RLog.e(TAG, "onStartCommand = " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopRinging(Context context) {
|
||||
isRinging.set(false);
|
||||
CallRingingUtil.getInstance().stopRinging();
|
||||
try {
|
||||
NotificationManager notificationManager = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
notificationManager = context.getSystemService(NotificationManager.class);
|
||||
}
|
||||
if (notificationManager != null) {
|
||||
notificationManager.cancel(notificationId);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Handler handler = mHandler;
|
||||
if (handler != null) {
|
||||
handler.removeCallbacksAndMessages(TAG_KILL_INCOMING_SERVICE);
|
||||
}
|
||||
}
|
||||
|
||||
private PendingIntent createOpenAppPendingIntent(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions,
|
||||
int requestCode,
|
||||
boolean isMulti) {
|
||||
Intent intent =
|
||||
createOpenAppIntent(
|
||||
context, message, callSession, checkPermissions, requestCode, isMulti);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return PendingIntent.getActivity(
|
||||
context, 2314412, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
return PendingIntent.getBroadcast(
|
||||
context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
private Intent createOpenAppIntent(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions,
|
||||
int requestCode,
|
||||
boolean isMulti) {
|
||||
return createIntentForAndroidS(context, callSession, checkPermissions);
|
||||
}
|
||||
|
||||
private PendingIntent createAnswerIntent(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions,
|
||||
int requestCode,
|
||||
boolean isMulti) {
|
||||
Intent intent =
|
||||
createOpenAppIntent(
|
||||
context, message, callSession, checkPermissions, requestCode, isMulti);
|
||||
intent.putExtra(KEY_NEED_AUTO_ANSWER, callSession.getCallId());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return PendingIntent.getActivity(
|
||||
context, 12345664, intent, PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
intent.setClass(context, VoIPBroadcastReceiver.class);
|
||||
return PendingIntent.getBroadcast(
|
||||
context, ACCEPT_REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
private Intent createIntentForAndroidS(
|
||||
Context context, RongCallSession callSession, boolean checkPermissions) {
|
||||
Intent intent;
|
||||
// 如果进程被杀 RongCallClient.getInstance() 返回Null
|
||||
if (RongCallClient.getInstance() != null
|
||||
&& RongCallClient.getInstance().getCallSession() != null
|
||||
&& callSession != null) {
|
||||
intent = RongCallModule.createVoIPIntent(context, callSession, checkPermissions);
|
||||
io.rong.push.common.RLog.d(TAG, "handleNotificationClickEvent: start call activity");
|
||||
} else {
|
||||
intent = createConversationListIntent(context);
|
||||
io.rong.push.common.RLog.d(
|
||||
TAG, "handleNotificationClickEvent: start conversation activity");
|
||||
}
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
return intent;
|
||||
}
|
||||
|
||||
private static Intent createConversationListIntent(Context context) {
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Uri uri =
|
||||
Uri.parse("rong://" + context.getPackageName())
|
||||
.buildUpon()
|
||||
.appendPath("conversationlist")
|
||||
.build();
|
||||
intent.setData(uri);
|
||||
intent.setPackage(context.getPackageName());
|
||||
return intent;
|
||||
}
|
||||
|
||||
private PendingIntent createHangupIntent(Context context, RongCallSession callSession) {
|
||||
Intent hangupIntent = new Intent();
|
||||
hangupIntent.setAction(VoIPBroadcastReceiver.ACTION_CALL_HANGUP_CLICKED);
|
||||
hangupIntent.putExtra(KEY_CALL_SESSION, callSession);
|
||||
hangupIntent.setPackage(context.getPackageName());
|
||||
hangupIntent.setClass(context, VoIPBroadcastReceiver.class);
|
||||
// KNOTE: 2021/9/29 PendingIntent
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return PendingIntent.getBroadcast(
|
||||
context,
|
||||
HANGUP_REQUEST_CODE,
|
||||
hangupIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
return PendingIntent.getBroadcast(
|
||||
context, HANGUP_REQUEST_CODE, hangupIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
// 唤醒屏幕并解锁
|
||||
public void wakeUpAndUnlock(Context context) {
|
||||
try {
|
||||
KeyguardManager km =
|
||||
(KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
|
||||
KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock");
|
||||
// 获取电源管理器对象
|
||||
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
|
||||
// 获取PowerManager.WakeLock对象,后面的参数|表示同时传入两个值,最后的是LogCat里用的Tag
|
||||
@SuppressLint("InvalidWakeLockTag")
|
||||
PowerManager.WakeLock wl =
|
||||
pm.newWakeLock(
|
||||
PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK,
|
||||
"bright");
|
||||
// 点亮屏幕
|
||||
wl.acquire(5000);
|
||||
// 释放
|
||||
wl.release();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
19
callkit/src/main/java/io/rong/callkit/RongVoIPIntent.java
Normal file
@ -0,0 +1,19 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
/** Created by weiqinxiao on 16/3/18. */
|
||||
public class RongVoIPIntent {
|
||||
public static final String RONG_INTENT_VOIP_CATEGORY = "io.rong.intent.category.voip";
|
||||
|
||||
public static final String RONG_INTENT_ACTION_VOIP_MULTIAUDIO =
|
||||
"io.rong.intent.action.voip.MULTIAUDIO";
|
||||
public static final String RONG_INTENT_ACTION_VOIP_MULTIVIDEO =
|
||||
"io.rong.intent.action.voip.MULTIVIDEO";
|
||||
public static final String RONG_INTENT_ACTION_VOIP_SINGLEAUDIO =
|
||||
"io.rong.intent.action.voip.SINGLEAUDIO";
|
||||
public static final String RONG_INTENT_ACTION_VOIP_SINGLEVIDEO =
|
||||
"io.rong.intent.action.voip.SINGLEVIDEO";
|
||||
public static final String RONG_INTENT_ACTION_VOIP_INIT = "io.rong.intent.action.SDK_INIT";
|
||||
public static final String RONG_INTENT_ACTION_VOIP_UI_READY = "io.rong.intent.action.UI_READY";
|
||||
public static final String RONG_INTENT_ACTION_VOIP_CONNECTED =
|
||||
"io.rong.intent.action.SDK_CONNECTED";
|
||||
}
|
1336
callkit/src/main/java/io/rong/callkit/SingleCallActivity.java
Normal file
189
callkit/src/main/java/io/rong/callkit/VideoPlugin.java
Normal file
@ -0,0 +1,189 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import static io.rong.callkit.BaseCallActivity.REQUEST_CODE_ADD_MEMBER;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import io.rong.callkit.util.CallKitUtils;
|
||||
import io.rong.callkit.util.RongCallPermissionUtil;
|
||||
import io.rong.callkit.util.permission.PermissionType;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.conversation.extension.RongExtension;
|
||||
import io.rong.imkit.conversation.extension.component.plugin.IPluginModule;
|
||||
import io.rong.imkit.conversation.extension.component.plugin.IPluginRequestPermissionResultCallback;
|
||||
import io.rong.imlib.IRongCoreCallback;
|
||||
import io.rong.imlib.IRongCoreEnum;
|
||||
import io.rong.imlib.RongIMClient;
|
||||
import io.rong.imlib.discussion.base.RongDiscussionClient;
|
||||
import io.rong.imlib.discussion.model.Discussion;
|
||||
import io.rong.imlib.model.Conversation;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
/** Created by weiqinxiao on 16/8/16. */
|
||||
public class VideoPlugin implements IPluginModule, IPluginRequestPermissionResultCallback {
|
||||
private static final String TAG = "VideoPlugin";
|
||||
private ArrayList<String> allMembers;
|
||||
private Context context;
|
||||
|
||||
private Conversation.ConversationType conversationType;
|
||||
private String targetId;
|
||||
|
||||
@Override
|
||||
public Drawable obtainDrawable(Context context) {
|
||||
return context.getResources().getDrawable(R.drawable.rc_ic_video_selector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String obtainTitle(Context context) {
|
||||
return context.getString(R.string.rc_voip_video);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(Fragment currentFragment, RongExtension extension, int index) {
|
||||
context = currentFragment.getActivity().getApplicationContext();
|
||||
conversationType = extension.getConversationType();
|
||||
targetId = extension.getTargetId();
|
||||
|
||||
PermissionType[] audioCallPermissions =
|
||||
RongCallPermissionUtil.getVideoCallPermissions(context);
|
||||
String[] permissions = new String[audioCallPermissions.length];
|
||||
for (int i = 0; i < audioCallPermissions.length; i++) {
|
||||
permissions[i] = audioCallPermissions[i].getPermissionName();
|
||||
}
|
||||
if (RongCallPermissionUtil.checkPermissions(currentFragment.getActivity(), permissions)) {
|
||||
startVideoActivity(extension);
|
||||
} else {
|
||||
extension.requestPermissionForPluginResult(
|
||||
permissions,
|
||||
IPluginRequestPermissionResultCallback.REQUEST_CODE_PERMISSION_PLUGIN,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
private void startVideoActivity(final RongExtension extension) {
|
||||
|
||||
RongCallSession profile = RongCallClient.getInstance().getCallSession();
|
||||
if (profile != null && profile.getStartTime() > 0) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
profile.getMediaType() == RongCallCommon.CallMediaType.AUDIO
|
||||
? context.getString(R.string.rc_voip_call_audio_start_fail)
|
||||
: context.getString(R.string.rc_voip_call_video_start_fail),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
if (!CallKitUtils.isNetworkAvailable(context)) {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.rc_voip_call_network_error),
|
||||
Toast.LENGTH_SHORT)
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
if (conversationType.equals(Conversation.ConversationType.PRIVATE)) {
|
||||
Intent intent = new Intent(RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEVIDEO);
|
||||
intent.putExtra("conversationType", conversationType.getName().toLowerCase(Locale.US));
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.getApplicationContext().startActivity(intent);
|
||||
} else if (conversationType.equals(Conversation.ConversationType.DISCUSSION)) {
|
||||
RongDiscussionClient.getInstance()
|
||||
.getDiscussion(
|
||||
targetId,
|
||||
new IRongCoreCallback.ResultCallback<Discussion>() {
|
||||
@Override
|
||||
public void onSuccess(Discussion discussion) {
|
||||
|
||||
Intent intent =
|
||||
new Intent(context, CallSelectMemberActivity.class);
|
||||
allMembers = (ArrayList<String>) discussion.getMemberIdList();
|
||||
intent.putStringArrayListExtra("allMembers", allMembers);
|
||||
String myId = RongIMClient.getInstance().getCurrentUserId();
|
||||
ArrayList<String> invited = new ArrayList<>();
|
||||
invited.add(myId);
|
||||
intent.putStringArrayListExtra("invitedMembers", invited);
|
||||
intent.putExtra(
|
||||
"conversationType", conversationType.getValue());
|
||||
intent.putExtra(
|
||||
"mediaType",
|
||||
RongCallCommon.CallMediaType.VIDEO.getValue());
|
||||
extension.startActivityForPluginResult(
|
||||
intent, 110, VideoPlugin.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(IRongCoreEnum.CoreErrorCode e) {
|
||||
RLog.d(TAG, "get discussion errorCode = " + e.getValue());
|
||||
}
|
||||
});
|
||||
} else if (conversationType.equals(Conversation.ConversationType.GROUP)) {
|
||||
Intent intent = new Intent(context, CallSelectMemberActivity.class);
|
||||
String myId = RongIMClient.getInstance().getCurrentUserId();
|
||||
ArrayList<String> invited = new ArrayList<>();
|
||||
invited.add(myId);
|
||||
intent.putStringArrayListExtra("invitedMembers", invited);
|
||||
intent.putExtra("groupId", targetId);
|
||||
intent.putExtra("conversationType", conversationType.getValue());
|
||||
intent.putExtra("mediaType", RongCallCommon.CallMediaType.VIDEO.getValue());
|
||||
extension.startActivityForPluginResult(intent, 110, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode != Activity.RESULT_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_CODE_ADD_MEMBER) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (data.getBooleanExtra("remote_hangup", false)) {
|
||||
RLog.d(TAG, "Remote exit, end the call.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Intent intent = new Intent(RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO);
|
||||
ArrayList<String> userIds = data.getStringArrayListExtra("invited");
|
||||
ArrayList<String> observerIds = data.getStringArrayListExtra("observers");
|
||||
userIds.add(RongIMClient.getInstance().getCurrentUserId());
|
||||
intent.putExtra("conversationType", conversationType.getName().toLowerCase(Locale.US));
|
||||
intent.putExtra("targetId", targetId);
|
||||
intent.putExtra("callAction", RongCallAction.ACTION_OUTGOING_CALL.getName());
|
||||
intent.putStringArrayListExtra("invitedUsers", userIds);
|
||||
intent.putStringArrayListExtra("observerUsers", observerIds);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.getApplicationContext().startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRequestPermissionResult(
|
||||
Fragment fragment,
|
||||
RongExtension extension,
|
||||
int requestCode,
|
||||
@NonNull String[] permissions,
|
||||
@NonNull int[] grantResults) {
|
||||
Context context = fragment.getContext();
|
||||
if (RongCallPermissionUtil.checkPermissions(context, permissions)) {
|
||||
startVideoActivity(extension);
|
||||
} else {
|
||||
RongCallPermissionUtil.showRequestPermissionFailedAlter(
|
||||
context, permissions, grantResults);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
526
callkit/src/main/java/io/rong/callkit/VoIPBroadcastReceiver.java
Normal file
@ -0,0 +1,526 @@
|
||||
package io.rong.callkit;
|
||||
|
||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import io.rong.callkit.util.CallRingingUtil;
|
||||
import io.rong.callkit.util.IncomingCallExtraHandleUtil;
|
||||
import io.rong.callkit.util.RingingMode;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.common.fwlog.FwLog;
|
||||
import io.rong.imkit.userinfo.RongUserInfoManager;
|
||||
import io.rong.imlib.model.AndroidConfig;
|
||||
import io.rong.imlib.model.Conversation.ConversationType;
|
||||
import io.rong.imlib.model.Group;
|
||||
import io.rong.imlib.model.MessagePushConfig;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
import io.rong.push.common.PushConst;
|
||||
import io.rong.push.common.RLog;
|
||||
import io.rong.push.notification.PushNotificationMessage;
|
||||
import io.rong.push.notification.RongNotificationInterface;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 为解决在 Android 10 以上版本不再允许后台运行 Activity,音视频的离线推送呼叫消息将由通知栏的形式展示给用户 Created by wangw on 2019-12-09.
|
||||
*/
|
||||
public class VoIPBroadcastReceiver extends BroadcastReceiver {
|
||||
|
||||
public static final int DEFAULT_FCM_NOTIFICATION_ID = 5000;
|
||||
private static final String HANGUP = "RC:VCHangup";
|
||||
private static final String INVITE = "RC:VCInvite";
|
||||
public static final String ACTION_CALLINVITEMESSAGE = "action.push.CallInviteMessage";
|
||||
public static final String ACTION_CALLINVITEMESSAGE_CLICKED =
|
||||
"action.push.CallInviteMessage.CLICKED";
|
||||
public static final String ACTION_CALL_HANGUP_CLICKED = "action.push.voip.hangup.click";
|
||||
private static final String TAG = "VoIPBroadcastReceiver";
|
||||
public static final String ACTION_CLEAR_VOIP_NOTIFICATION = "action.voip.notification.clear";
|
||||
private static Map<String, Integer> notificationCache = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
RLog.d(TAG, "onReceive.action:" + action);
|
||||
|
||||
// 通知栏挂断按钮事件响应
|
||||
if (ACTION_CALL_HANGUP_CLICKED.equals(action)) {
|
||||
RongCallSession session =
|
||||
intent.getParcelableExtra(RongIncomingCallService.KEY_CALL_SESSION);
|
||||
stopIncomingService(context);
|
||||
if (session == null) {
|
||||
RongCallClient.getInstance().hangUpCall();
|
||||
} else {
|
||||
RongCallClient.getInstance().hangUpCall(session.getCallId());
|
||||
}
|
||||
return;
|
||||
} else if (ACTION_CLEAR_VOIP_NOTIFICATION.equals(action)) {
|
||||
// 针对 Android 12 的业务逻辑
|
||||
IncomingCallExtraHandleUtil.removeNotification(context);
|
||||
IncomingCallExtraHandleUtil.clear();
|
||||
clearNotificationCache();
|
||||
return;
|
||||
}
|
||||
|
||||
PushNotificationMessage message = intent.getParcelableExtra(PushConst.MESSAGE);
|
||||
// bug fixed : https://rc-jira.rongcloud.net/browse/AC-903
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
RongCallSession callSession = null;
|
||||
boolean checkPermissions = false;
|
||||
if (intent.hasExtra("callsession")) {
|
||||
callSession = intent.getParcelableExtra("callsession");
|
||||
checkPermissions = intent.getBooleanExtra("checkPermissions", false);
|
||||
}
|
||||
|
||||
if (!needShowNotification(context, message)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(ACTION_CALLINVITEMESSAGE, action)) {
|
||||
if (callSession == null) {
|
||||
RLog.d(TAG, "callSession is null: " + message);
|
||||
// fcm voip 走的是透传,处理一下这种情况下,单独弹起一个通知拉起应用
|
||||
fcmShowNotification(context, message);
|
||||
return;
|
||||
}
|
||||
clearFcmNotification(context);
|
||||
String objName = message.getObjectName();
|
||||
if (TextUtils.equals(objName, INVITE)) {
|
||||
IncomingCallExtraHandleUtil.cacheCallSession(callSession, checkPermissions);
|
||||
UserInfo userInfo =
|
||||
RongUserInfoManager.getInstance()
|
||||
.getUserInfo(callSession.getCallerUserId());
|
||||
sendNotification(context, message, callSession, checkPermissions, userInfo);
|
||||
} else {
|
||||
IncomingCallExtraHandleUtil.clear();
|
||||
UserInfo userInfo =
|
||||
RongUserInfoManager.getInstance()
|
||||
.getUserInfo(callSession.getCallerUserId());
|
||||
sendNotification(context, message, callSession, checkPermissions, userInfo);
|
||||
}
|
||||
} else if (TextUtils.equals(ACTION_CALLINVITEMESSAGE_CLICKED, action)) {
|
||||
IncomingCallExtraHandleUtil.removeNotification(context);
|
||||
IncomingCallExtraHandleUtil.clear();
|
||||
clearNotificationCache();
|
||||
handleNotificationClickEvent(
|
||||
context,
|
||||
message,
|
||||
callSession,
|
||||
checkPermissions,
|
||||
intent.getStringExtra(RongIncomingCallService.KEY_NEED_AUTO_ANSWER));
|
||||
}
|
||||
}
|
||||
|
||||
private void clearFcmNotification(Context context) {
|
||||
RLog.d(TAG, "clearFcmNotification");
|
||||
NotificationManager nm =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (nm != null) {
|
||||
nm.cancel(DEFAULT_FCM_NOTIFICATION_ID);
|
||||
}
|
||||
}
|
||||
|
||||
public void fcmShowNotification(final Context context, PushNotificationMessage message) {
|
||||
|
||||
try {
|
||||
CallRingingUtil.getInstance().createNotificationChannel(context);
|
||||
int smallIcon =
|
||||
context.getResources()
|
||||
.getIdentifier(
|
||||
"notification_small_icon",
|
||||
"drawable",
|
||||
context.getPackageName());
|
||||
if (smallIcon <= 0) {
|
||||
smallIcon = context.getApplicationInfo().icon;
|
||||
}
|
||||
Uri uri =
|
||||
Uri.parse("rong://" + context.getPackageName())
|
||||
.buildUpon()
|
||||
.appendPath("conversationlist")
|
||||
.build();
|
||||
Intent intent = new Intent();
|
||||
intent.setData(uri);
|
||||
intent.setPackage(context.getPackageName());
|
||||
// 目标activity的包名和类名
|
||||
PendingIntent pendingIntent;
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context,
|
||||
1000,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
pendingIntent =
|
||||
PendingIntent.getActivity(
|
||||
context, 1000, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
NotificationCompat.Builder notificationBuilder =
|
||||
new NotificationCompat.Builder(
|
||||
context,
|
||||
CallRingingUtil.getInstance().getNotificationChannelId())
|
||||
.setContentText(message.getPushContent())
|
||||
.setContentTitle(message.getPushTitle())
|
||||
.setSmallIcon(smallIcon)
|
||||
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
||||
.setCategory(NotificationCompat.CATEGORY_CALL)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setOngoing(true);
|
||||
Notification notification = notificationBuilder.build();
|
||||
NotificationManager notificationManager = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
notificationManager = context.getSystemService(NotificationManager.class);
|
||||
}
|
||||
if (notificationManager != null) {
|
||||
notificationManager.notify(DEFAULT_FCM_NOTIFICATION_ID, notification);
|
||||
}
|
||||
if (HANGUP.equals(message.getObjectName())) {
|
||||
CallRingingUtil.getInstance().stopRinging();
|
||||
} else {
|
||||
CallRingingUtil.getInstance().startRinging(context, RingingMode.Incoming);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
io.rong.common.RLog.e(TAG, "onStartCommand = " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needShowNotification(Context context, PushNotificationMessage message) {
|
||||
if (message == null || context == null) {
|
||||
return false;
|
||||
}
|
||||
if (INVITE.equals(message.getObjectName())
|
||||
&& Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
|
||||
// Android 10 以下允许后台运行,直接交由会话列表界面拉取消息
|
||||
RLog.d(TAG, "handle VoIP event.");
|
||||
try {
|
||||
Intent newIntent = new Intent();
|
||||
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Uri uri =
|
||||
Uri.parse("rong://" + context.getPackageName())
|
||||
.buildUpon()
|
||||
.appendPath("conversationlist")
|
||||
.appendQueryParameter("isFromPush", "false")
|
||||
.build();
|
||||
newIntent.setData(uri);
|
||||
newIntent.setPackage(context.getPackageName());
|
||||
context.startActivity(newIntent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
private void handleNotificationClickEvent(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions,
|
||||
String needAutoAnswerCallId) {
|
||||
Intent intent;
|
||||
// 如果进程被杀 RongCallClient.getInstance() 返回Null
|
||||
if (RongCallClient.getInstance() != null
|
||||
&& RongCallClient.getInstance().getCallSession() != null
|
||||
&& callSession != null) {
|
||||
intent = RongCallModule.createVoIPIntent(context, callSession, checkPermissions);
|
||||
RLog.d(TAG, "handleNotificationClickEvent: start call activity");
|
||||
} else {
|
||||
intent = createConversationListIntent(context);
|
||||
RLog.d(TAG, "handleNotificationClickEvent: start conversation activity");
|
||||
}
|
||||
if (callSession != null && !TextUtils.isEmpty(needAutoAnswerCallId)) {
|
||||
intent.putExtra(RongIncomingCallService.KEY_NEED_AUTO_ANSWER, callSession.getCallId());
|
||||
}
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setPackage(context.getPackageName());
|
||||
// bug fixed : https://rc-jira.rongcloud.net/browse/AC-864
|
||||
if (intent.resolveActivity(context.getPackageManager()) != null) {
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
StringBuilder builder = new StringBuilder("start activity with scheme : ");
|
||||
if (intent.getData() != null) {
|
||||
builder.append(intent.getData().toString());
|
||||
}
|
||||
FwLog.write(FwLog.I, FwLog.IM, "L-VoIP_notify_scheme", "scheme", builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private void sendNotification(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions,
|
||||
UserInfo userInfo) {
|
||||
RLog.d(TAG, "sendNotification: " + message.getObjectName());
|
||||
String pushContent;
|
||||
boolean isAudio = callSession.getMediaType() == RongCallCommon.CallMediaType.AUDIO;
|
||||
if (HANGUP.equals(message.getObjectName())) {
|
||||
pushContent =
|
||||
context.getResources().getString(R.string.rc_voip_call_terminalted_notify);
|
||||
if (callSession.getConversationType().equals(ConversationType.GROUP)
|
||||
&& RongCallClient.getInstance().getCallSession() != null) {
|
||||
return; // 群组消息,getCallSession不为空,说明收到的hangup并不是最后一个人发出的,此时不需要生成通知
|
||||
}
|
||||
} else {
|
||||
pushContent =
|
||||
context.getResources()
|
||||
.getString(
|
||||
isAudio
|
||||
? R.string.rc_voip_audio_call_inviting
|
||||
: R.string.rc_voip_video_call_inviting);
|
||||
}
|
||||
message.setPushContent(pushContent);
|
||||
if (callSession.getConversationType().equals(ConversationType.PRIVATE)) {
|
||||
if (userInfo != null && !TextUtils.isEmpty(userInfo.getName())) {
|
||||
message.setPushTitle(userInfo.getName());
|
||||
}
|
||||
} else if (callSession.getConversationType().equals(ConversationType.GROUP)) {
|
||||
Group group = RongUserInfoManager.getInstance().getGroupInfo(callSession.getTargetId());
|
||||
if (group != null && !TextUtils.isEmpty(group.getName())) {
|
||||
message.setPushTitle(group.getName());
|
||||
}
|
||||
}
|
||||
if (callSession != null && callSession.getPushConfig() != null) {
|
||||
MessagePushConfig messagePushConfig = callSession.getPushConfig();
|
||||
if (!TextUtils.isEmpty(messagePushConfig.getPushTitle())) {
|
||||
message.setPushTitle(messagePushConfig.getPushTitle());
|
||||
}
|
||||
if (!TextUtils.isEmpty(messagePushConfig.getPushContent())
|
||||
&& !messagePushConfig.getPushContent().equals("voip")) {
|
||||
// message.setPushContent(messagePushConfig.getPushContent());
|
||||
}
|
||||
if (messagePushConfig.isForceShowDetailContent()) {
|
||||
message.setShowDetail(messagePushConfig.isForceShowDetailContent());
|
||||
}
|
||||
AndroidConfig androidConfig = messagePushConfig.getAndroidConfig();
|
||||
if (androidConfig != null) {
|
||||
message.setChannelIdHW(androidConfig.getChannelIdHW());
|
||||
message.setChannelIdMi(androidConfig.getChannelIdMi());
|
||||
message.setChannelIdOPPO(androidConfig.getChannelIdOPPO());
|
||||
message.setNotificationId(androidConfig.getNotificationId());
|
||||
}
|
||||
}
|
||||
sendNotification(context, message, callSession, checkPermissions);
|
||||
}
|
||||
|
||||
private void startIncomingService(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(RongIncomingCallService.KEY_MESSAGE, message);
|
||||
bundle.putParcelable(RongIncomingCallService.KEY_CALL_SESSION, callSession);
|
||||
bundle.putBoolean(RongIncomingCallService.KEY_CHECK_PERMISSIONS, checkPermissions);
|
||||
CallRingingUtil.getInstance().startRingingService(context, bundle);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopIncomingService(Context context) {
|
||||
CallRingingUtil.getInstance().stopService(context);
|
||||
}
|
||||
|
||||
private void sendNotification(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions) {
|
||||
String objName = message.getObjectName();
|
||||
if (TextUtils.isEmpty(objName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
String title;
|
||||
String content;
|
||||
int notificationId = IncomingCallExtraHandleUtil.VOIP_NOTIFICATION_ID;
|
||||
RLog.i(
|
||||
TAG,
|
||||
"sendNotification() messageType: "
|
||||
+ message.getConversationType()
|
||||
+ " messagePushContent: "
|
||||
+ message.getPushContent()
|
||||
+ " messageObjectName: "
|
||||
+ message.getObjectName()
|
||||
+ " notificationId: "
|
||||
+ message.getNotificationId());
|
||||
|
||||
// Android 10 以上走新逻辑,通过服务启动
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
if (INVITE.equals(objName)) {
|
||||
startIncomingService(context, message, callSession, checkPermissions);
|
||||
} else if (HANGUP.equals(objName)) {
|
||||
stopIncomingService(context);
|
||||
sendHangupNotification(context, message, callSession, checkPermissions);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (objName.equals(INVITE) || objName.equals(HANGUP)) {
|
||||
content = message.getPushContent();
|
||||
title = message.getPushTitle();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
Notification notification =
|
||||
RongNotificationInterface.createNotification(
|
||||
context,
|
||||
title,
|
||||
createPendingIntent(
|
||||
context,
|
||||
message,
|
||||
callSession,
|
||||
checkPermissions,
|
||||
IncomingCallExtraHandleUtil.VOIP_REQUEST_CODE,
|
||||
false),
|
||||
content,
|
||||
RongNotificationInterface.SoundType.VOIP,
|
||||
message.isShowDetail());
|
||||
NotificationManager nm =
|
||||
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
int importance = NotificationManager.IMPORTANCE_HIGH;
|
||||
String channelName = CallRingingUtil.getInstance().getNotificationChannelName(context);
|
||||
NotificationChannel notificationChannel =
|
||||
new NotificationChannel(
|
||||
CallRingingUtil.getInstance().getNotificationChannelId(),
|
||||
channelName,
|
||||
importance);
|
||||
notificationChannel.enableLights(true);
|
||||
notificationChannel.setLightColor(Color.GREEN);
|
||||
if (notification != null && notification.sound != null) {
|
||||
notificationChannel.setSound(notification.sound, null);
|
||||
}
|
||||
nm.createNotificationChannel(notificationChannel);
|
||||
}
|
||||
if (notification != null) {
|
||||
RLog.i(
|
||||
TAG,
|
||||
"sendNotification() real notify! notificationId: "
|
||||
+ notificationId
|
||||
+ " notification: "
|
||||
+ notification.toString());
|
||||
if (INVITE.equals(message.getObjectName())) {
|
||||
notificationCache.put(callSession.getCallId(), notificationId);
|
||||
nm.notify(notificationId, notification);
|
||||
IncomingCallExtraHandleUtil.VOIP_NOTIFICATION_ID++;
|
||||
} else if (notificationCache.containsKey(callSession.getCallId())) {
|
||||
notificationId = notificationCache.get(callSession.getCallId());
|
||||
nm.notify(notificationId, notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sendHangupNotification(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions) {
|
||||
try {
|
||||
String content =
|
||||
context.getResources().getString(R.string.rc_voip_call_terminalted_notify);
|
||||
String title = message.getPushTitle();
|
||||
int notificationId = IncomingCallExtraHandleUtil.VOIP_NOTIFICATION_ID;
|
||||
Notification notification =
|
||||
RongNotificationInterface.createNotification(
|
||||
context,
|
||||
title,
|
||||
createPendingIntent(
|
||||
context,
|
||||
message,
|
||||
callSession,
|
||||
checkPermissions,
|
||||
IncomingCallExtraHandleUtil.VOIP_REQUEST_CODE,
|
||||
false),
|
||||
content,
|
||||
RongNotificationInterface.SoundType.VOIP,
|
||||
message.isShowDetail());
|
||||
if (notification == null) {
|
||||
return;
|
||||
}
|
||||
NotificationManager nm =
|
||||
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
CallRingingUtil.getInstance().createNotificationChannel(context);
|
||||
RLog.i(
|
||||
TAG,
|
||||
"sendNotification() real notify! notificationId: "
|
||||
+ notificationId
|
||||
+ " notification: "
|
||||
+ notification.toString());
|
||||
notification.defaults = Notification.DEFAULT_ALL;
|
||||
notification.sound = null;
|
||||
nm.notify(notificationId, notification);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static PendingIntent createPendingIntent(
|
||||
Context context,
|
||||
PushNotificationMessage message,
|
||||
RongCallSession callSession,
|
||||
boolean checkPermissions,
|
||||
int requestCode,
|
||||
boolean isMulti) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(ACTION_CALLINVITEMESSAGE_CLICKED);
|
||||
intent.putExtra(PushConst.MESSAGE, message);
|
||||
intent.putExtra("callsession", callSession);
|
||||
intent.putExtra("checkPermissions", checkPermissions);
|
||||
intent.putExtra(PushConst.IS_MULTI, isMulti);
|
||||
intent.setPackage(context.getPackageName());
|
||||
// KNOTE: 2021/9/29 PendingIntent
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
return PendingIntent.getBroadcast(
|
||||
context,
|
||||
requestCode,
|
||||
intent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
} else {
|
||||
return PendingIntent.getBroadcast(
|
||||
context, requestCode, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
|
||||
private static Intent createConversationListIntent(Context context) {
|
||||
Intent intent = new Intent();
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
Uri uri =
|
||||
Uri.parse("rong://" + context.getPackageName())
|
||||
.buildUpon()
|
||||
.appendPath("conversationlist")
|
||||
.build();
|
||||
intent.setData(uri);
|
||||
intent.setPackage(context.getPackageName());
|
||||
return intent;
|
||||
}
|
||||
|
||||
public static void clearNotificationCache() {
|
||||
notificationCache.clear();
|
||||
}
|
||||
}
|
@ -0,0 +1,156 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import io.rong.callkit.RongCallModule;
|
||||
import io.rong.callkit.RongVoIPIntent;
|
||||
|
||||
public class ActivityStartCheckUtils {
|
||||
public interface ActivityStartResultCallback {
|
||||
void onStartActivityResult(boolean isActivityStarted);
|
||||
}
|
||||
|
||||
private static final int TIME_DELAY = 3000;
|
||||
private boolean mPostDelayIsRunning;
|
||||
private String mClassName;
|
||||
private Handler mHandler = new Handler();
|
||||
private Activity topActivity;
|
||||
private Context mAppContext;
|
||||
private ActivityStartResultCallback activityStartResultCallback;
|
||||
private static final String[] CALL_ACTIONS = {
|
||||
RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIVIDEO,
|
||||
RongVoIPIntent.RONG_INTENT_ACTION_VOIP_MULTIAUDIO,
|
||||
RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEVIDEO,
|
||||
RongVoIPIntent.RONG_INTENT_ACTION_VOIP_SINGLEAUDIO
|
||||
};
|
||||
|
||||
private static class SingletonHolder {
|
||||
|
||||
static ActivityStartCheckUtils sInstance = new ActivityStartCheckUtils();
|
||||
}
|
||||
|
||||
private ActivityStartCheckUtils() {}
|
||||
|
||||
public static ActivityStartCheckUtils getInstance() {
|
||||
return SingletonHolder.sInstance;
|
||||
}
|
||||
|
||||
public void registerActivityLifecycleCallbacks(Context context) {
|
||||
|
||||
mAppContext = context.getApplicationContext();
|
||||
Application application = (Application) mAppContext;
|
||||
|
||||
if (application == null) return;
|
||||
|
||||
application.registerActivityLifecycleCallbacks(
|
||||
new Application.ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {}
|
||||
|
||||
@Override
|
||||
public void onActivityStarted(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityResumed(Activity activity) {
|
||||
topActivity = activity;
|
||||
handleIncomingCallNotify(topActivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityPaused(Activity activity) {}
|
||||
|
||||
@Override
|
||||
public void onActivityStopped(Activity activity) {
|
||||
if (topActivity == activity) {
|
||||
topActivity = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {}
|
||||
|
||||
@Override
|
||||
public void onActivityDestroyed(Activity activity) {}
|
||||
});
|
||||
}
|
||||
|
||||
public String getTopActivity() {
|
||||
if (topActivity == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return topActivity.getClass().getSuperclass().getSimpleName();
|
||||
}
|
||||
|
||||
public void startActivity(
|
||||
Context context,
|
||||
Intent intent,
|
||||
String className,
|
||||
ActivityStartResultCallback callback) {
|
||||
if (context == null || intent == null || TextUtils.isEmpty(className)) {
|
||||
return;
|
||||
}
|
||||
context.startActivity(intent);
|
||||
mClassName = className;
|
||||
if (mPostDelayIsRunning) {
|
||||
mHandler.removeCallbacks(mRunnable);
|
||||
}
|
||||
mPostDelayIsRunning = true;
|
||||
activityStartResultCallback = callback;
|
||||
mHandler.postDelayed(mRunnable, TIME_DELAY);
|
||||
}
|
||||
|
||||
private boolean isActivityOnTop() {
|
||||
boolean result = false;
|
||||
String topActivityName = getTopActivity();
|
||||
if (!TextUtils.isEmpty(topActivityName)) {
|
||||
if (topActivityName.contains(mClassName)) {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Runnable mRunnable =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mPostDelayIsRunning = false;
|
||||
boolean isOnTop = isActivityOnTop();
|
||||
if (activityStartResultCallback != null) {
|
||||
activityStartResultCallback.onStartActivityResult(isOnTop);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Android 10 以上禁止后台启动 Activity callKit 适配方案是后台来电时弹通知栏通知,但是如果用户不点击通知栏,
|
||||
* 通过桌面图标打开应用,需要增加一种补偿机制启动来电界面
|
||||
*/
|
||||
private void handleIncomingCallNotify(Activity activity) {
|
||||
if (activity != null && IncomingCallExtraHandleUtil.needNotify()) {
|
||||
String action = activity.getIntent().getAction();
|
||||
if (!TextUtils.isEmpty(action)) {
|
||||
for (String ac : CALL_ACTIONS) {
|
||||
if (TextUtils.equals(ac, action)) {
|
||||
IncomingCallExtraHandleUtil.clear(); // 解决鸿蒙3.0设备 mate 10 重复启动通话界面问题
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IncomingCallExtraHandleUtil.removeNotification(mAppContext);
|
||||
activity.startActivity(
|
||||
RongCallModule.createVoIPIntent(
|
||||
mAppContext,
|
||||
IncomingCallExtraHandleUtil.getCallSession(),
|
||||
IncomingCallExtraHandleUtil.isCheckPermissions()));
|
||||
IncomingCallExtraHandleUtil.clear();
|
||||
}
|
||||
}
|
||||
}
|
305
callkit/src/main/java/io/rong/callkit/util/BluetoothUtil.java
Normal file
@ -0,0 +1,305 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import static android.content.Context.AUDIO_SERVICE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.text.TextUtils;
|
||||
import io.rong.common.RLog;
|
||||
import java.util.List;
|
||||
|
||||
/** Created by degnxudong on 2018/8/24. */
|
||||
public class BluetoothUtil {
|
||||
|
||||
private static final String TAG = "BluetoothUtil";
|
||||
|
||||
/**
|
||||
* 是否连接了蓝牙耳机
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@SuppressLint("WrongConstant")
|
||||
public static boolean hasBluetoothA2dpConnected() {
|
||||
boolean bool = false;
|
||||
BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (mAdapter != null && mAdapter.isEnabled()) {
|
||||
int a2dp = mAdapter.getProfileConnectionState(BluetoothProfile.A2DP);
|
||||
if (a2dp == BluetoothProfile.STATE_CONNECTED) {
|
||||
bool = true;
|
||||
}
|
||||
}
|
||||
return bool;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否插入了有线耳机
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean isWiredHeadsetOn(Context context) {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(AUDIO_SERVICE);
|
||||
return audioManager.isWiredHeadsetOn();
|
||||
}
|
||||
|
||||
private static String getStyleContent(int styleMajor) {
|
||||
String content = "未知....";
|
||||
switch (styleMajor) {
|
||||
case BluetoothClass.Device.Major.AUDIO_VIDEO: // 音频设备
|
||||
content = "音配设备";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.COMPUTER: // 电脑
|
||||
content = "电脑";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.HEALTH: // 健康状况
|
||||
content = "健康状况";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.IMAGING: // 镜像,映像
|
||||
content = "镜像";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.MISC: // 麦克风
|
||||
content = "麦克风";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.NETWORKING: // 网络
|
||||
content = "网络";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.PERIPHERAL: // 外部设备
|
||||
content = "外部设备";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.PHONE: // 电话
|
||||
content = "电话";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.TOY: // 玩具
|
||||
content = "玩具";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.UNCATEGORIZED: // 未知的
|
||||
content = "未知的";
|
||||
break;
|
||||
case BluetoothClass.Device.Major.WEARABLE: // 穿戴设备
|
||||
content = "穿戴设备";
|
||||
break;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
private static boolean getDeviceClass(int deviceClass) {
|
||||
boolean bool = false;
|
||||
switch (deviceClass) {
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: // 录像机
|
||||
// "录像机";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
|
||||
// "车载设备";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
|
||||
// "蓝牙耳机";
|
||||
bool = true;
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
|
||||
// "扬声器";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE:
|
||||
// "麦克风";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
|
||||
// "打印机";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX:
|
||||
// "BOX";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED:
|
||||
// "未知的";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_VCR:
|
||||
// "录像机";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA:
|
||||
// "照相机录像机";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING:
|
||||
// "conferencing";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
|
||||
// "显示器和扬声器";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY:
|
||||
// "游戏";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR:
|
||||
// "显示器";
|
||||
break;
|
||||
case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
|
||||
// "可穿戴设备";
|
||||
bool = true;
|
||||
break;
|
||||
case BluetoothClass.Device.PHONE_CELLULAR:
|
||||
// "手机";
|
||||
break;
|
||||
case BluetoothClass.Device.PHONE_CORDLESS:
|
||||
// "无线电设备";
|
||||
break;
|
||||
case BluetoothClass.Device.PHONE_ISDN:
|
||||
// "手机服务数据网";
|
||||
break;
|
||||
case BluetoothClass.Device.PHONE_MODEM_OR_GATEWAY:
|
||||
// "手机调节器";
|
||||
break;
|
||||
case BluetoothClass.Device.PHONE_SMART:
|
||||
// "手机卫星";
|
||||
break;
|
||||
case BluetoothClass.Device.PHONE_UNCATEGORIZED:
|
||||
// "未知手机";
|
||||
break;
|
||||
case BluetoothClass.Device.WEARABLE_GLASSES:
|
||||
// "可穿戴眼睛";
|
||||
break;
|
||||
case BluetoothClass.Device.WEARABLE_HELMET:
|
||||
// "可穿戴头盔";
|
||||
break;
|
||||
case BluetoothClass.Device.WEARABLE_JACKET:
|
||||
// "可穿戴上衣";
|
||||
break;
|
||||
case BluetoothClass.Device.WEARABLE_PAGER:
|
||||
// "客串点寻呼机";
|
||||
break;
|
||||
case BluetoothClass.Device.WEARABLE_UNCATEGORIZED:
|
||||
// "未知的可穿戴设备";
|
||||
break;
|
||||
case BluetoothClass.Device.WEARABLE_WRIST_WATCH:
|
||||
// "手腕监听设备";
|
||||
break;
|
||||
case BluetoothClass.Device.TOY_CONTROLLER:
|
||||
// "可穿戴设备";
|
||||
break;
|
||||
case BluetoothClass.Device.TOY_DOLL_ACTION_FIGURE:
|
||||
// "玩具doll_action_figure";
|
||||
break;
|
||||
case BluetoothClass.Device.TOY_GAME:
|
||||
// "游戏";
|
||||
break;
|
||||
case BluetoothClass.Device.TOY_ROBOT:
|
||||
// "玩具遥控器";
|
||||
break;
|
||||
case BluetoothClass.Device.TOY_UNCATEGORIZED:
|
||||
// "玩具未知设备";
|
||||
break;
|
||||
case BluetoothClass.Device.TOY_VEHICLE:
|
||||
// "vehicle";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_BLOOD_PRESSURE:
|
||||
// "健康状态-血压";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_DATA_DISPLAY:
|
||||
// "健康状态数据";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_GLUCOSE:
|
||||
// "健康状态葡萄糖";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_PULSE_OXIMETER:
|
||||
// "健康状态脉搏血氧计";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_PULSE_RATE:
|
||||
// "健康状态脉搏速率";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_THERMOMETER:
|
||||
// "健康状态体温计";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_WEIGHING:
|
||||
// "健康状态体重";
|
||||
break;
|
||||
case BluetoothClass.Device.HEALTH_UNCATEGORIZED:
|
||||
// "未知健康状态设备";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_DESKTOP:
|
||||
// "电脑桌面";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_HANDHELD_PC_PDA:
|
||||
// "手提电脑或Pad";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_LAPTOP:
|
||||
// "便携式电脑";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_PALM_SIZE_PC_PDA:
|
||||
// "微型电脑";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_SERVER:
|
||||
// "电脑服务";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
|
||||
// "未知的电脑设备";
|
||||
break;
|
||||
case BluetoothClass.Device.COMPUTER_WEARABLE:
|
||||
/// "可穿戴的电脑";
|
||||
break;
|
||||
}
|
||||
return bool;
|
||||
}
|
||||
|
||||
public static boolean isForground(Activity activity) {
|
||||
return isForground(activity, activity.getClass().getName());
|
||||
}
|
||||
|
||||
private static boolean isForground(Context context, String className) {
|
||||
if (context == null || TextUtils.isEmpty(className)) {
|
||||
return false;
|
||||
}
|
||||
ActivityManager activityManager =
|
||||
(ActivityManager) context.getSystemService(context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningTaskInfo> list = activityManager.getRunningTasks(1);
|
||||
if (null != list && list.size() > 0) {
|
||||
ComponentName componentName = list.get(0).topActivity;
|
||||
if (className.equals(componentName.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否支持蓝牙
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isSupportBluetooth() {
|
||||
boolean bool = false;
|
||||
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
if (null != bluetoothAdapter) {
|
||||
bool = true;
|
||||
}
|
||||
RLog.i(TAG, "isSupportBluetooth = " + bool);
|
||||
return bool;
|
||||
}
|
||||
|
||||
public static void startBlueToothSco(Context context) {
|
||||
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
if (am.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
}
|
||||
if (!am.isBluetoothScoOn()) {
|
||||
am.startBluetoothSco();
|
||||
am.setBluetoothScoOn(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void stopBlueToothSco(Context context) {
|
||||
AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
// if (am.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
|
||||
// am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
// }
|
||||
if (am.isBluetoothScoOn()) {
|
||||
am.stopBluetoothSco();
|
||||
am.setBluetoothScoOn(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.renderscript.Allocation;
|
||||
import android.renderscript.Element;
|
||||
import android.renderscript.RenderScript;
|
||||
import android.renderscript.ScriptIntrinsicBlur;
|
||||
|
||||
/** Created by dengxudong on 2018/5/18. */
|
||||
public class BlurBitmapUtil {
|
||||
|
||||
private static class SingletonHolder {
|
||||
|
||||
static BlurBitmapUtil sInstance = new BlurBitmapUtil();
|
||||
}
|
||||
|
||||
private BlurBitmapUtil() {}
|
||||
|
||||
public static BlurBitmapUtil instance() {
|
||||
return SingletonHolder.sInstance;
|
||||
}
|
||||
/**
|
||||
* @param context 上下文对象
|
||||
* @param image 需要模糊的图片
|
||||
* @param outWidth 输入出的宽度
|
||||
* @param outHeight 输出的高度
|
||||
* @return 模糊处理后的Bitmap
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
public Bitmap blurBitmap(
|
||||
Context context, Bitmap image, float blurRadius, int outWidth, int outHeight) {
|
||||
// 将缩小后的图片做为预渲染的图片
|
||||
Bitmap inputBitmap = Bitmap.createScaledBitmap(image, outWidth, outHeight, false);
|
||||
// 创建一张渲染后的输出图片
|
||||
Bitmap outputBitmap = Bitmap.createBitmap(inputBitmap);
|
||||
// 创建RenderScript内核对象
|
||||
RenderScript rs = RenderScript.create(context);
|
||||
// 创建一个模糊效果的RenderScript的工具对象
|
||||
ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
|
||||
// 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
|
||||
// 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
|
||||
Allocation tmpIn = Allocation.createFromBitmap(rs, inputBitmap);
|
||||
Allocation tmpOut = Allocation.createFromBitmap(rs, outputBitmap);
|
||||
// 设置渲染的模糊程度, 25f是最大模糊度
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
blurScript.setRadius(blurRadius);
|
||||
}
|
||||
// 设置blurScript对象的输入内存
|
||||
blurScript.setInput(tmpIn);
|
||||
// 将输出数据保存到输出内存中
|
||||
blurScript.forEach(tmpOut);
|
||||
// 将数据填充到Allocation中
|
||||
tmpOut.copyTo(outputBitmap);
|
||||
return outputBitmap;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
public interface CallKitSearchBarListener {
|
||||
/**
|
||||
* 开始搜索 EditText 中输入内容后,会触发此回调
|
||||
*
|
||||
* @param keyword 搜索关键字
|
||||
*/
|
||||
void onSearchStart(String keyword);
|
||||
|
||||
/** 软键盘中"搜索"被点击后,触发此回调 此回调被触发后,仅收起软键盘 */
|
||||
void onSoftSearchKeyClick();
|
||||
|
||||
/** 搜索控件中,点击"清除"后,触发此回调 */
|
||||
void onClearButtonClick();
|
||||
}
|
@ -0,0 +1,153 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
import io.rong.callkit.R;
|
||||
|
||||
public class CallKitSearchBarView extends RelativeLayout {
|
||||
|
||||
private EditText editSearch;
|
||||
private View clearBtn;
|
||||
private ImageView searchIV;
|
||||
private CallKitSearchBarListener listener;
|
||||
private Handler handler;
|
||||
private boolean searchContentCleared;
|
||||
|
||||
public CallKitSearchBarView(final Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
inflate(context, R.layout.callkit_view_search_bar_layout, this);
|
||||
searchIV = findViewById(R.id.iv_icon);
|
||||
editSearch = findViewById(R.id.et_search);
|
||||
if (CallKitUtils.findConfigurationLanguage(context, "ar")) {
|
||||
editSearch.setTextDirection(View.TEXT_DIRECTION_RTL);
|
||||
}
|
||||
handler = new Handler();
|
||||
editSearch.addTextChangedListener(
|
||||
new TextWatcher() {
|
||||
Runnable searchRunnable = null;
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(
|
||||
CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
if (!TextUtils.isEmpty(s.toString())) {
|
||||
searchIV.setImageDrawable(
|
||||
getResources()
|
||||
.getDrawable(R.drawable.callkit_ic_search_focused_x));
|
||||
clearBtn.setVisibility(VISIBLE);
|
||||
} else {
|
||||
searchIV.setImageDrawable(
|
||||
getResources().getDrawable(R.drawable.callkit_ic_search_x));
|
||||
clearBtn.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
if (searchRunnable != null) {
|
||||
handler.removeCallbacks(searchRunnable);
|
||||
}
|
||||
final String keywords = editSearch.getText().toString().trim();
|
||||
if (!TextUtils.isEmpty(keywords)) {
|
||||
searchContentCleared = false;
|
||||
searchRunnable =
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
listener.onSearchStart(keywords);
|
||||
}
|
||||
};
|
||||
handler.postDelayed(searchRunnable, 500);
|
||||
} else {
|
||||
if (!searchContentCleared) {
|
||||
listener.onClearButtonClick();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
editSearch.setOnEditorActionListener(
|
||||
new TextView.OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
listener.onSoftSearchKeyClick();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
clearBtn = findViewById(R.id.iv_clear);
|
||||
clearBtn.setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
editSearch.setText("");
|
||||
searchIV.setImageDrawable(
|
||||
getResources().getDrawable(R.drawable.callkit_ic_search_x));
|
||||
clearBtn.setVisibility(GONE);
|
||||
listener.onClearButtonClick();
|
||||
}
|
||||
});
|
||||
|
||||
setOnClickListener(
|
||||
new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager)
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.showSoftInput(editSearch, InputMethodManager.HIDE_NOT_ALWAYS);
|
||||
listener.onSoftSearchKeyClick();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
boolean isSearchTextEmpty() {
|
||||
return editSearch.getText().toString().equals("");
|
||||
}
|
||||
|
||||
public void setSearchBarListener(CallKitSearchBarListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void clearSearchContent() {
|
||||
searchContentCleared = true;
|
||||
editSearch.setText("");
|
||||
}
|
||||
|
||||
public void setSearchHint(String text) {
|
||||
editSearch.setHint(text);
|
||||
}
|
||||
|
||||
void setSearchText(String content) {
|
||||
if (TextUtils.isEmpty(content)) {
|
||||
clearBtn.setVisibility(GONE);
|
||||
return;
|
||||
}
|
||||
editSearch.setText(content);
|
||||
editSearch.setSelection(content.length());
|
||||
searchIV.setImageDrawable(
|
||||
getResources().getDrawable(R.drawable.callkit_ic_search_focused_x));
|
||||
clearBtn.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
public EditText getEditText() {
|
||||
return editSearch;
|
||||
}
|
||||
}
|
270
callkit/src/main/java/io/rong/callkit/util/CallKitUtils.java
Normal file
@ -0,0 +1,270 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.AppOpsManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import io.rong.callkit.R;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imlib.model.Message;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/** Created by dengxudong on 2018/5/17. */
|
||||
public class CallKitUtils {
|
||||
|
||||
/** 拨打true or 接听false */
|
||||
public static boolean isDial = true;
|
||||
|
||||
public static boolean shouldShowFloat;
|
||||
/** 是否已经建立通话连接 默认没有,为了修改接听之后将情景模式切换成震动 在通话界面一直震动的问题 */
|
||||
public static boolean callConnected = false;
|
||||
/** true:响铃中,false:响铃已结束 */
|
||||
// public static boolean RINGSTATE=false;
|
||||
/** 当前 免提 是否打开的状态 true:打开中 */
|
||||
public static boolean speakerphoneState = false;
|
||||
|
||||
public static StringBuffer stringBuffer = null;
|
||||
|
||||
private static Map<String, Long> mapLastClickTime = new HashMap<>();
|
||||
|
||||
public static Drawable BackgroundDrawable(int drawable, Context context) {
|
||||
return ContextCompat.getDrawable(context, drawable);
|
||||
}
|
||||
|
||||
public static int dp2px(float dpVal, Context context) {
|
||||
return (int)
|
||||
TypedValue.applyDimension(
|
||||
TypedValue.COMPLEX_UNIT_DIP,
|
||||
dpVal,
|
||||
context.getResources().getDisplayMetrics());
|
||||
}
|
||||
|
||||
/** 关闭软键盘 */
|
||||
public static void closeKeyBoard(Activity activity, View view) {
|
||||
IBinder token;
|
||||
if (view == null || view.getWindowToken() == null) {
|
||||
if (null == activity) {
|
||||
return;
|
||||
}
|
||||
Window window = activity.getWindow();
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
View v = window.peekDecorView();
|
||||
if (v == null) {
|
||||
return;
|
||||
}
|
||||
token = v.getWindowToken();
|
||||
} else {
|
||||
token = view.getWindowToken();
|
||||
}
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(token, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 提供(相对)精确的除法运算。
|
||||
*
|
||||
* @param vl1 被除数
|
||||
* @param vl2 除数
|
||||
* @return 商
|
||||
*/
|
||||
public static double div(double vl1, double vl2) {
|
||||
|
||||
BigDecimal b1 = BigDecimal.valueOf(vl1);
|
||||
BigDecimal b2 = BigDecimal.valueOf(vl2);
|
||||
// 4 表示表示需要精确到小数点以后几位。当发生除不尽的情况时,参数指定精度,以后的数字四舍五入。
|
||||
return b1.divide(b2, 4, BigDecimal.ROUND_HALF_UP).doubleValue();
|
||||
}
|
||||
|
||||
/** 四舍五入把double转化int整型 */
|
||||
public static int getInt(double number) {
|
||||
BigDecimal bd = BigDecimal.valueOf(number).setScale(0, BigDecimal.ROUND_HALF_UP);
|
||||
return Integer.parseInt(bd.toString());
|
||||
}
|
||||
|
||||
public static void textViewShadowLayer(TextView text, Context context) {
|
||||
if (null == text) {
|
||||
return;
|
||||
}
|
||||
text.setShadowLayer(
|
||||
16F,
|
||||
0F,
|
||||
2F,
|
||||
context.getApplicationContext()
|
||||
.getResources()
|
||||
.getColor(R.color.callkit_shadowcolor));
|
||||
}
|
||||
|
||||
public static boolean checkPermissions(Context context, @NonNull String[] permissions) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (permissions == null || permissions.length == 0) {
|
||||
return true;
|
||||
}
|
||||
for (String permission : permissions) {
|
||||
if (!hasPermission(context, permission)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasPermission(Context context, String permission) {
|
||||
String opStr = AppOpsManagerCompat.permissionToOp(permission);
|
||||
if (opStr == null) {
|
||||
return true;
|
||||
}
|
||||
boolean bool =
|
||||
context.checkCallingOrSelfPermission(permission)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
return bool;
|
||||
}
|
||||
|
||||
public static String[] getCallpermissions() {
|
||||
String[] permissions =
|
||||
new String[] {
|
||||
Manifest.permission.CAMERA,
|
||||
Manifest.permission.RECORD_AUDIO,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||
Manifest.permission.INTERNET,
|
||||
Manifest.permission.MODIFY_AUDIO_SETTINGS,
|
||||
Manifest.permission.BLUETOOTH,
|
||||
Manifest.permission.BLUETOOTH_ADMIN,
|
||||
Manifest.permission.READ_PHONE_STATE,
|
||||
};
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/** 获取字符串指定拼接内容 */
|
||||
public static String getStitchedContent(String val1, String val2) {
|
||||
if (TextUtils.isEmpty(val1)) {
|
||||
val1 = "";
|
||||
}
|
||||
if (TextUtils.isEmpty(val2)) {
|
||||
val2 = "";
|
||||
}
|
||||
if (stringBuffer == null) {
|
||||
stringBuffer = new StringBuffer();
|
||||
} else {
|
||||
stringBuffer.setLength(0);
|
||||
}
|
||||
stringBuffer.append(val1);
|
||||
stringBuffer.append(val2);
|
||||
return stringBuffer.toString();
|
||||
}
|
||||
|
||||
/** 是否是debug状态 */
|
||||
public static boolean isDebug(Context context) {
|
||||
try {
|
||||
ApplicationInfo info = context.getApplicationInfo();
|
||||
return (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isNetworkAvailable(Context context) {
|
||||
ConnectivityManager cm =
|
||||
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
|
||||
if (networkInfo == null || !networkInfo.isConnected() || !networkInfo.isAvailable()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** double click */
|
||||
public static boolean isFastDoubleClick() {
|
||||
return isFastDoubleClick("Default");
|
||||
}
|
||||
|
||||
public static boolean isFastDoubleClick(String eventType) {
|
||||
Long lastClickTime = mapLastClickTime.get(eventType);
|
||||
if (lastClickTime == null) {
|
||||
lastClickTime = 0l;
|
||||
}
|
||||
long curTime = System.currentTimeMillis();
|
||||
long timeD = curTime - lastClickTime;
|
||||
if (timeD > 0 && timeD < 800) {
|
||||
return true;
|
||||
}
|
||||
mapLastClickTime.put(eventType, curTime);
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 昵称长度超过5,后面使用...显示 */
|
||||
public static String nickNameRestrict(String userName) {
|
||||
if (!TextUtils.isEmpty(userName) && userName.length() > 5) {
|
||||
StringBuffer stringBuffer = new StringBuffer();
|
||||
stringBuffer.append(userName.substring(0, 5).trim());
|
||||
stringBuffer.append("...");
|
||||
userName = stringBuffer.toString();
|
||||
}
|
||||
return userName;
|
||||
}
|
||||
|
||||
public static Message.ReceivedStatus getReceivedStatus(
|
||||
RongCallCommon.CallDisconnectedReason reason) {
|
||||
// 己方超时未接听或是对方取消通话时,应插入未读消息
|
||||
if (reason == RongCallCommon.CallDisconnectedReason.REMOTE_CANCEL
|
||||
|| reason == RongCallCommon.CallDisconnectedReason.NO_RESPONSE) {
|
||||
return new Message.ReceivedStatus(0);
|
||||
}
|
||||
// 默认是已读状态
|
||||
return new Message.ReceivedStatus(1);
|
||||
}
|
||||
|
||||
/** @param language zh en ar */
|
||||
public static boolean findConfigurationLanguage(Context context, String language) {
|
||||
if (context == null || TextUtils.isEmpty(language)) {
|
||||
RLog.w("bugtags", "findConfigurationLanguage->Resources is empty");
|
||||
return false;
|
||||
}
|
||||
Resources resources = context.getResources();
|
||||
if (resources == null) {
|
||||
RLog.w("bugtags", "findConfigurationLanguage->Resources is empty");
|
||||
return false;
|
||||
}
|
||||
Configuration configuration = resources.getConfiguration();
|
||||
if (configuration == null || configuration.locale == null) {
|
||||
RLog.w("bugtags", "findConfigurationLanguage->configuration is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
Locale locale = configuration.locale;
|
||||
String languageApp = locale.getLanguage();
|
||||
RLog.d(
|
||||
"bugtags",
|
||||
"findConfigurationLanguage->languageApp : "
|
||||
+ languageApp
|
||||
+ " ,language : "
|
||||
+ language);
|
||||
return TextUtils.equals(languageApp, language);
|
||||
}
|
||||
}
|
427
callkit/src/main/java/io/rong/callkit/util/CallRingingUtil.java
Normal file
@ -0,0 +1,427 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.AssetFileDescriptor;
|
||||
import android.media.AudioAttributes;
|
||||
import android.media.AudioAttributes.Builder;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import io.rong.callkit.R;
|
||||
import io.rong.callkit.RongIncomingCallService;
|
||||
import io.rong.common.RLog;
|
||||
import io.rong.imkit.notification.NotificationUtil;
|
||||
import io.rong.push.notification.RongNotificationHelper;
|
||||
import java.io.IOException;
|
||||
|
||||
/** @author gusd @Date 2021/09/14 */
|
||||
public class CallRingingUtil {
|
||||
private static final String TAG = "CallRingingUtil";
|
||||
private volatile boolean isRinging = false;
|
||||
private volatile RingingMode mCurrentRingingMode = null;
|
||||
|
||||
private MediaPlayer mMediaPlayer;
|
||||
private Vibrator mVibrator;
|
||||
private Context applicationContext;
|
||||
private volatile boolean stopServiceAndRingingTag = false;
|
||||
private volatile boolean isFirstReceivedBroadcast = true;
|
||||
|
||||
private static final String DEFAULT_CHANNEL_NAME = "VOIP";
|
||||
private static final String DEFAULT_CHANNEL_ID =
|
||||
RongNotificationHelper.getDefaultVoipChannelId();
|
||||
public static final int DEFAULT_ANSWER_ICON = R.drawable.rc_voip_notification_answer;
|
||||
public static final int DEFAULT_HANGUP_ICON = R.drawable.rc_voip_notification_hangup;
|
||||
|
||||
@DrawableRes private int answerIcon = DEFAULT_ANSWER_ICON;
|
||||
@DrawableRes private int hangupIcon = DEFAULT_HANGUP_ICON;
|
||||
|
||||
private CallRingingUtil() {}
|
||||
|
||||
private static class InstanceHolder {
|
||||
static final CallRingingUtil instance = new CallRingingUtil();
|
||||
}
|
||||
|
||||
public static CallRingingUtil getInstance() {
|
||||
return InstanceHolder.instance;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.O)
|
||||
public void startRingingService(Context context, Bundle bundle) {
|
||||
if (!isRingingServiceRunning(context)) {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtras(bundle);
|
||||
intent.setPackage(context.getPackageName());
|
||||
// KNOTE: 2021/9/29 前台服务启动限制
|
||||
try {
|
||||
RongIncomingCallService.getInstance().startRing(context, intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
RLog.e(TAG, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopService(Context context) {
|
||||
RLog.d(TAG, "stopService: ");
|
||||
if (isRingingServiceRunning(context)) {
|
||||
RongIncomingCallService.getInstance().stopRinging(context);
|
||||
}
|
||||
}
|
||||
|
||||
public void stopServiceButContinueRinging(Context context) {
|
||||
RLog.d(TAG, "stopServiceButContinueRinging: ");
|
||||
if (!isRingingServiceRunning(context)) {
|
||||
stopServiceAndRingingTag = false;
|
||||
return;
|
||||
}
|
||||
stopServiceAndRingingTag = true;
|
||||
stopService(context);
|
||||
}
|
||||
|
||||
public boolean isRingingServiceRunning(Context context) {
|
||||
return RongIncomingCallService.getInstance().isRinging();
|
||||
}
|
||||
|
||||
public void startRinging(Context context, RingingMode mode) {
|
||||
RLog.d(TAG, "startRinging: ");
|
||||
if (isRinging) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
applicationContext = context.getApplicationContext();
|
||||
|
||||
// 注册 BroadcastReceiver 监听情景模式的切换
|
||||
IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
|
||||
isFirstReceivedBroadcast = true;
|
||||
applicationContext.registerReceiver(
|
||||
mRingModeReceiver,
|
||||
filter,
|
||||
context.getApplicationInfo().packageName + ".permission.RONG_ACCESS_RECEIVER",
|
||||
null);
|
||||
|
||||
if (mode == RingingMode.Incoming || mode == RingingMode.Incoming_Custom) {
|
||||
int ringerMode = NotificationUtil.getInstance().getRingerMode(context);
|
||||
if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
|
||||
if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
|
||||
startVibrator(context);
|
||||
} else {
|
||||
if (isVibrateWhenRinging(context)) {
|
||||
startVibrator(context);
|
||||
}
|
||||
callRinging(context, mode);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
callRinging(context, mode);
|
||||
}
|
||||
mCurrentRingingMode = mode;
|
||||
isRinging = true;
|
||||
}
|
||||
|
||||
protected void startVibrator(Context context) {
|
||||
Log.d(TAG, "startVibrator: ");
|
||||
if (mVibrator == null) {
|
||||
mVibrator =
|
||||
(Vibrator)
|
||||
context.getApplicationContext()
|
||||
.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
} else {
|
||||
mVibrator.cancel();
|
||||
}
|
||||
|
||||
long[] pattern = {500, 1000};
|
||||
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
|
||||
AudioAttributes build =
|
||||
new Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_NOTIFICATION)
|
||||
.build();
|
||||
mVibrator.vibrate(pattern, 0, build);
|
||||
} else {
|
||||
mVibrator.vibrate(pattern, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/** 判断系统是否设置了 响铃时振动 */
|
||||
private boolean isVibrateWhenRinging(Context context) {
|
||||
ContentResolver resolver = context.getApplicationContext().getContentResolver();
|
||||
if (Build.MANUFACTURER.equals("Xiaomi")) {
|
||||
return Settings.System.getInt(resolver, "vibrate_in_normal", 0) == 1;
|
||||
} else if (Build.MANUFACTURER.equals("smartisan")) {
|
||||
return Settings.Global.getInt(resolver, "telephony_vibration_enabled", 0) == 1;
|
||||
} else {
|
||||
return Settings.System.getInt(resolver, "vibrate_when_ringing", 0) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void callRinging(Context context, RingingMode mode) {
|
||||
initMp();
|
||||
try {
|
||||
if (mode == RingingMode.Incoming) {
|
||||
Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE);
|
||||
mMediaPlayer.setDataSource(context.getApplicationContext(), uri);
|
||||
} else if (mode == RingingMode.Incoming_Custom || mode == RingingMode.Outgoing) {
|
||||
int rawResId =
|
||||
mode == RingingMode.Outgoing
|
||||
? R.raw.voip_outgoing_ring
|
||||
: R.raw.voip_incoming_ring;
|
||||
AssetFileDescriptor assetFileDescriptor =
|
||||
context.getResources().openRawResourceFd(rawResId);
|
||||
mMediaPlayer.setDataSource(
|
||||
assetFileDescriptor.getFileDescriptor(),
|
||||
assetFileDescriptor.getStartOffset(),
|
||||
assetFileDescriptor.getLength());
|
||||
assetFileDescriptor.close();
|
||||
}
|
||||
|
||||
// 设置 MediaPlayer 播放的声音用途
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
int usage = AudioAttributes.USAGE_VOICE_COMMUNICATION;
|
||||
if (TextUtils.equals(Build.BRAND, "Xiaomi")) {
|
||||
usage = AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
|
||||
}
|
||||
AudioAttributes attributes = new AudioAttributes.Builder().setUsage(usage).build();
|
||||
mMediaPlayer.setAudioAttributes(attributes);
|
||||
} else {
|
||||
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_VOICE_CALL);
|
||||
}
|
||||
mMediaPlayer.prepareAsync();
|
||||
final AudioManager am =
|
||||
(AudioManager)
|
||||
context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
am.setSpeakerphoneOn(
|
||||
mode == RingingMode.Incoming || mode == RingingMode.Incoming_Custom);
|
||||
// 设置此值可在拨打时控制响铃音量
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
// 设置拨打时响铃音量默认值
|
||||
am.setStreamVolume(
|
||||
AudioManager.STREAM_VOICE_CALL, 5, AudioManager.STREAM_VOICE_CALL);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e1) {
|
||||
RLog.i(TAG, "---onOutgoingCallRinging Error---" + e1.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void initMp() {
|
||||
if (mMediaPlayer == null) {
|
||||
mMediaPlayer = new MediaPlayer();
|
||||
mMediaPlayer.setOnPreparedListener(
|
||||
new MediaPlayer.OnPreparedListener() {
|
||||
@Override
|
||||
public void onPrepared(MediaPlayer mp) {
|
||||
try {
|
||||
if (mp != null) {
|
||||
mp.setLooping(true);
|
||||
mp.start();
|
||||
}
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
RLog.i(TAG, "setOnPreparedListener Error!");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void stopRinging() {
|
||||
try {
|
||||
RLog.d(TAG, "stopRinging: ");
|
||||
if (stopServiceAndRingingTag) {
|
||||
stopServiceAndRingingTag = false;
|
||||
return;
|
||||
}
|
||||
if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
|
||||
mMediaPlayer.stop();
|
||||
}
|
||||
if (mMediaPlayer != null) {
|
||||
mMediaPlayer.reset();
|
||||
}
|
||||
if (mVibrator != null) {
|
||||
mVibrator.cancel();
|
||||
}
|
||||
if (applicationContext != null) {
|
||||
try {
|
||||
applicationContext.unregisterReceiver(mRingModeReceiver);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
final AudioManager am =
|
||||
(AudioManager) applicationContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
RLog.i(TAG, "mMediaPlayer stopRing error=" + e.getMessage());
|
||||
} finally {
|
||||
isRinging = false;
|
||||
mCurrentRingingMode = null;
|
||||
}
|
||||
}
|
||||
|
||||
protected final BroadcastReceiver mRingModeReceiver =
|
||||
new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
RLog.d(
|
||||
TAG,
|
||||
"onReceive : " + "context = " + context + "," + "intent = " + intent);
|
||||
// 此类广播为 sticky 类型的,首次注册广播便会收到,因此第一次收到的广播不作处理
|
||||
if (isFirstReceivedBroadcast) {
|
||||
isFirstReceivedBroadcast = false;
|
||||
return;
|
||||
}
|
||||
if (!isRinging) {
|
||||
return;
|
||||
}
|
||||
// 根据 isIncoming 判断只有在接听界面时做铃声和振动的切换,拨打界面不作处理
|
||||
if ((mCurrentRingingMode == RingingMode.Incoming
|
||||
|| mCurrentRingingMode == RingingMode.Incoming_Custom)
|
||||
&& AudioManager.RINGER_MODE_CHANGED_ACTION.equals(intent.getAction())
|
||||
&& !CallKitUtils.callConnected) {
|
||||
AudioManager am =
|
||||
(AudioManager)
|
||||
context.getApplicationContext()
|
||||
.getSystemService(Context.AUDIO_SERVICE);
|
||||
final int ringMode = am.getRingerMode();
|
||||
RLog.i(TAG, "Ring mode Receiver mode=" + ringMode);
|
||||
switch (ringMode) {
|
||||
case AudioManager.RINGER_MODE_NORMAL:
|
||||
stopRinging();
|
||||
callRinging(context.getApplicationContext(), RingingMode.Incoming);
|
||||
break;
|
||||
case AudioManager.RINGER_MODE_SILENT:
|
||||
stopRinging();
|
||||
break;
|
||||
case AudioManager.RINGER_MODE_VIBRATE:
|
||||
stopRinging();
|
||||
startVibrator(context);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 创建通知通道
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public void createNotificationChannel(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel =
|
||||
new NotificationChannel(
|
||||
getNotificationChannelId(),
|
||||
getNotificationChannelName(context),
|
||||
NotificationManager.IMPORTANCE_HIGH);
|
||||
channel.setSound(null, null);
|
||||
context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
public String getNotificationChannelId() {
|
||||
return DEFAULT_CHANNEL_ID;
|
||||
}
|
||||
|
||||
public String getNotificationChannelName(Context context) {
|
||||
int id =
|
||||
context.getResources()
|
||||
.getIdentifier(
|
||||
RongNotificationHelper.getDefaultVoipChannelName(),
|
||||
"string",
|
||||
context.getPackageName());
|
||||
String channelName = id == 0 ? null : context.getResources().getString(id);
|
||||
return TextUtils.isEmpty(channelName) ? DEFAULT_CHANNEL_NAME : channelName;
|
||||
}
|
||||
|
||||
public void setNotificationHangupIcon(@DrawableRes int hangupIcon) {
|
||||
this.hangupIcon = hangupIcon;
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
public int getNotificationHangupIcon() {
|
||||
return this.hangupIcon;
|
||||
}
|
||||
|
||||
public void setNotificationAnswerIcon(@DrawableRes int answerIcon) {
|
||||
this.answerIcon = answerIcon;
|
||||
}
|
||||
|
||||
@DrawableRes
|
||||
public int getNotificationAnswerIcon() {
|
||||
return this.answerIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建通道并检查是否有悬浮通知权限
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public boolean createChannelAndCheckFullScreenPermission(Context context) {
|
||||
createNotificationChannel(context);
|
||||
return checkFullScreenPermission(context, getNotificationChannelId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有悬浮通知权限
|
||||
*
|
||||
* @param context
|
||||
* @param channelId
|
||||
* @return
|
||||
*/
|
||||
public boolean checkFullScreenPermission(Context context, String channelId) {
|
||||
NotificationManager mNotificationManager =
|
||||
(NotificationManager)
|
||||
context.getApplicationContext()
|
||||
.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Android 8.0及以上
|
||||
NotificationChannel channel =
|
||||
mNotificationManager.getNotificationChannel(channelId); // CHANNEL_ID是自己定义的渠道ID
|
||||
if (channel.getImportance() == NotificationManager.IMPORTANCE_DEFAULT) { // 未开启
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转到通知设置界面
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public void gotoChannelSettingPage(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, getNotificationChannelId());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class CallSelectMemberSerializable implements Serializable {
|
||||
private HashMap<String, String> hashMap = new HashMap<>();
|
||||
|
||||
public CallSelectMemberSerializable(HashMap<String, String> hashMap) {
|
||||
this.hashMap = hashMap;
|
||||
}
|
||||
|
||||
public HashMap<String, String> getHashMap() {
|
||||
return hashMap;
|
||||
}
|
||||
|
||||
public void setHashMap(HashMap<String, String> hashMap) {
|
||||
this.hashMap = hashMap;
|
||||
}
|
||||
}
|
@ -0,0 +1,259 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import io.rong.callkit.R;
|
||||
import io.rong.callkit.RongCallKit;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** 竖向滑动 多人语音——主叫方和通话中 */
|
||||
public class CallVerticalScrollView extends ScrollView implements ICallScrollView {
|
||||
private Context context;
|
||||
private boolean enableTitle;
|
||||
private LinearLayout linearLayout;
|
||||
private static int CHILDREN_PER_LINE = 4;
|
||||
private static final int CHILDREN_SPACE = 18;
|
||||
|
||||
private int portraitSize;
|
||||
|
||||
public CallVerticalScrollView(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public CallVerticalScrollView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
this.context = context;
|
||||
linearLayout = new LinearLayout(context);
|
||||
linearLayout.setLayoutParams(
|
||||
new LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
addView(linearLayout);
|
||||
}
|
||||
|
||||
public int dip2pix(int dipValue) {
|
||||
float scale = getResources().getDisplayMetrics().density;
|
||||
return (int) (dipValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildAtIndex(int index) {
|
||||
return linearLayout.getChildAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenSpace() {
|
||||
return CHILDREN_PER_LINE;
|
||||
}
|
||||
|
||||
public int getScreenWidth() {
|
||||
return getResources().getDisplayMetrics().widthPixels;
|
||||
}
|
||||
|
||||
public void setChildPortraitSize(int size) {
|
||||
portraitSize = size;
|
||||
}
|
||||
|
||||
public void enableShowState(boolean enable) {
|
||||
enableTitle = enable;
|
||||
}
|
||||
|
||||
public void addChild(String childId, UserInfo userInfo) {
|
||||
addChild(childId, userInfo, null);
|
||||
}
|
||||
|
||||
public void addChild(String childId, UserInfo userInfo, String state) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
LinearLayout lastContainer = null;
|
||||
int i;
|
||||
for (i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
if (container.getChildCount() < CHILDREN_PER_LINE) {
|
||||
lastContainer = container;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lastContainer == null) {
|
||||
lastContainer = new LinearLayout(context);
|
||||
lastContainer.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
lastContainer.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
lastContainer.setPadding(0, dip2pix(CHILDREN_SPACE), 0, 0);
|
||||
linearLayout.addView(lastContainer);
|
||||
}
|
||||
|
||||
LinearLayout child =
|
||||
(LinearLayout)
|
||||
LayoutInflater.from(context)
|
||||
.inflate(R.layout.rc_voip_user_info_mutlaudio, null);
|
||||
child.setLayoutParams(
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
child.setPadding(0, 0, dip2pix(CHILDREN_SPACE), 0);
|
||||
child.setTag(childId);
|
||||
if (portraitSize > 0) {
|
||||
child.findViewById(R.id.rc_user_portrait_layout)
|
||||
.setLayoutParams(new LinearLayout.LayoutParams(portraitSize, portraitSize));
|
||||
}
|
||||
ImageView imageView = (ImageView) child.findViewById(R.id.rc_user_portrait);
|
||||
TextView name = (TextView) child.findViewById(R.id.rc_user_name);
|
||||
name.setVisibility(enableTitle ? VISIBLE : GONE);
|
||||
TextView stateV = (TextView) child.findViewById(R.id.rc_voip_member_state);
|
||||
stateV.setVisibility(enableTitle ? VISIBLE : GONE);
|
||||
if (state != null) {
|
||||
stateV.setText(state);
|
||||
} else {
|
||||
stateV.setVisibility(GONE);
|
||||
}
|
||||
|
||||
if (userInfo != null) {
|
||||
RongCallKit.getKitImageEngine()
|
||||
.loadPortrait(
|
||||
this.getContext(),
|
||||
userInfo.getPortraitUri(),
|
||||
R.drawable.rc_default_portrait,
|
||||
imageView);
|
||||
name.setText(userInfo.getName() == null ? userInfo.getUserId() : userInfo.getName());
|
||||
} else {
|
||||
name.setText(childId);
|
||||
}
|
||||
lastContainer.addView(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScrollViewOverScrollMode(int mode) {
|
||||
this.setOverScrollMode(mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllChild() {
|
||||
linearLayout.removeAllViews();
|
||||
}
|
||||
|
||||
public void removeChild(String childId) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
LinearLayout lastContainer = null;
|
||||
List<LinearLayout> containerList = new ArrayList<>();
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
containerList.add(container);
|
||||
}
|
||||
for (LinearLayout resultContainer : containerList) {
|
||||
if (lastContainer == null) {
|
||||
LinearLayout child = (LinearLayout) resultContainer.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
resultContainer.removeView(child);
|
||||
if (resultContainer.getChildCount() == 0) {
|
||||
linearLayout.removeView(resultContainer);
|
||||
break;
|
||||
} else {
|
||||
lastContainer = resultContainer;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
View view = resultContainer.getChildAt(0);
|
||||
resultContainer.removeView(view);
|
||||
lastContainer.addView(view);
|
||||
if (resultContainer.getChildCount() == 0) {
|
||||
linearLayout.removeView(resultContainer);
|
||||
break;
|
||||
} else {
|
||||
lastContainer = resultContainer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public View findChildById(String childId) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void updateChildInfo(String childId, UserInfo userInfo) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
LinearLayout lastContainer = null;
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
ImageView imageView = (ImageView) child.findViewById(R.id.rc_user_portrait);
|
||||
Glide.with(this)
|
||||
.load(userInfo.getPortraitUri())
|
||||
.placeholder(R.drawable.rc_default_portrait)
|
||||
.apply(RequestOptions.bitmapTransform(new CircleCrop()))
|
||||
.into(imageView);
|
||||
if (enableTitle) {
|
||||
TextView textView = (TextView) child.findViewById(R.id.rc_user_name);
|
||||
textView.setLines(1);
|
||||
textView.setEllipsize(TextUtils.TruncateAt.END);
|
||||
textView.setText(userInfo.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateChildState(String childId, String state) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
TextView textView = (TextView) child.findViewById(R.id.rc_voip_member_state);
|
||||
textView.setText(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param childId
|
||||
* @param visible
|
||||
*/
|
||||
public void updateChildState(String childId, boolean visible) {
|
||||
int containerCount = linearLayout.getChildCount();
|
||||
|
||||
for (int i = 0; i < containerCount; i++) {
|
||||
LinearLayout container = (LinearLayout) linearLayout.getChildAt(i);
|
||||
LinearLayout child = (LinearLayout) container.findViewWithTag(childId);
|
||||
if (child != null) {
|
||||
TextView textView = (TextView) child.findViewById(R.id.rc_voip_member_state);
|
||||
textView.setVisibility(visible ? VISIBLE : GONE);
|
||||
// ImageView imageView =
|
||||
// (ImageView)
|
||||
// child.findViewById(R.id.callkit_mutilAudio_Floatinglayer);
|
||||
// imageView.setVisibility(visible ? VISIBLE : GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,157 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
import io.rong.callkit.R;
|
||||
import io.rong.imkit.RongIM;
|
||||
import io.rong.imkit.userinfo.RongUserInfoManager;
|
||||
import io.rong.imlib.model.AndroidConfig;
|
||||
import io.rong.imlib.model.IOSConfig;
|
||||
import io.rong.imlib.model.MessagePushConfig;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
|
||||
public class DefaultPushConfig {
|
||||
|
||||
/**
|
||||
* 获取邀请的 push config
|
||||
*
|
||||
* @param isPrivate 是否单人呼叫
|
||||
* @param groupName 群组呼叫的时候才需要填写:
|
||||
*/
|
||||
public static MessagePushConfig getInviteConfig(
|
||||
Context context, boolean isAudio, boolean isPrivate, String groupName) {
|
||||
UserInfo userInfo =
|
||||
RongUserInfoManager.getInstance()
|
||||
.getUserInfo(RongIM.getInstance().getCurrentUserId());
|
||||
String userName = userInfo == null ? "" : userInfo.getName();
|
||||
// 自定义音视频通话推送内容测试代码,融云SealTalk测试时配置写入SharedPreferences,
|
||||
// 开发者根据实际需求定义发起通话和挂断时的push配置,在 startCall 前设置即可
|
||||
SharedPreferences sharedPreferences =
|
||||
context.getSharedPreferences("push_config", MODE_PRIVATE);
|
||||
String id = sharedPreferences.getString("id", "");
|
||||
String title = sharedPreferences.getString("title", "");
|
||||
String pushTile = TextUtils.isEmpty(title) ? (isPrivate ? userName : groupName) : title;
|
||||
String content = sharedPreferences.getString("content", "");
|
||||
if (TextUtils.isEmpty(content)) {
|
||||
content =
|
||||
TextUtils.isEmpty(userName)
|
||||
? context.getResources()
|
||||
.getString(
|
||||
isAudio
|
||||
? R.string
|
||||
.rc_voip_notificatio_audio_call_inviting_general
|
||||
: R.string
|
||||
.rc_voip_notificatio_video_call_inviting_general)
|
||||
: userName
|
||||
+ " "
|
||||
+ context.getResources()
|
||||
.getString(
|
||||
isAudio
|
||||
? R.string
|
||||
.rc_voip_notificatio_audio_call_inviting
|
||||
: R.string
|
||||
.rc_voip_notificatio_video_call_inviting);
|
||||
}
|
||||
String invitePushContent = content;
|
||||
String data = sharedPreferences.getString("data", "");
|
||||
String hw = sharedPreferences.getString("hw", "");
|
||||
String mi = sharedPreferences.getString("mi", "");
|
||||
String oppo = sharedPreferences.getString("oppo", "");
|
||||
String threadId = sharedPreferences.getString("threadId", "");
|
||||
String apnsId = sharedPreferences.getString("apnsId", "");
|
||||
boolean vivo = sharedPreferences.getBoolean("vivo", false);
|
||||
boolean forceDetail = sharedPreferences.getBoolean("forceDetail", false);
|
||||
MessagePushConfig invitePushConfig =
|
||||
getMessagePushConfig(
|
||||
id,
|
||||
pushTile,
|
||||
invitePushContent,
|
||||
data,
|
||||
hw,
|
||||
mi,
|
||||
oppo,
|
||||
threadId,
|
||||
apnsId,
|
||||
forceDetail);
|
||||
|
||||
return invitePushConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取挂断的 push config
|
||||
*
|
||||
* @param isPrivate 是否单人呼叫
|
||||
* @param groupName 群组呼叫的时候才需要填写:
|
||||
*/
|
||||
public static MessagePushConfig getHangupConfig(
|
||||
Context context, boolean isPrivate, String groupName) {
|
||||
UserInfo userInfo = RongUserInfoManager.getInstance().getCurrentUserInfo();
|
||||
String userName = userInfo == null ? "" : userInfo.getName();
|
||||
|
||||
// 自定义音视频通话推送内容测试代码,融云SealTalk测试时配置写入SharedPreferences,
|
||||
// 开发者根据实际需求定义发起通话和挂断时的push配置,在 startCall 前设置即可
|
||||
SharedPreferences sharedPreferences =
|
||||
context.getSharedPreferences("push_config", MODE_PRIVATE);
|
||||
String id = sharedPreferences.getString("id", "");
|
||||
String title = sharedPreferences.getString("title", "");
|
||||
String pushTile = TextUtils.isEmpty(title) ? (isPrivate ? userName : groupName) : title;
|
||||
String content = sharedPreferences.getString("content", "");
|
||||
String hangupPushContent =
|
||||
TextUtils.isEmpty(content)
|
||||
? context.getResources().getString(R.string.rc_voip_call_terminalted_notify)
|
||||
: content;
|
||||
String data = sharedPreferences.getString("data", "");
|
||||
String hw = sharedPreferences.getString("hw", "");
|
||||
String mi = sharedPreferences.getString("mi", "");
|
||||
String oppo = sharedPreferences.getString("oppo", "");
|
||||
String threadId = sharedPreferences.getString("threadId", "");
|
||||
String apnsId = sharedPreferences.getString("apnsId", "");
|
||||
boolean vivo = sharedPreferences.getBoolean("vivo", false);
|
||||
boolean forceDetail = sharedPreferences.getBoolean("forceDetail", false);
|
||||
MessagePushConfig hangupPushConfig =
|
||||
getMessagePushConfig(
|
||||
id,
|
||||
pushTile,
|
||||
hangupPushContent,
|
||||
data,
|
||||
hw,
|
||||
mi,
|
||||
oppo,
|
||||
threadId,
|
||||
apnsId,
|
||||
forceDetail);
|
||||
return hangupPushConfig;
|
||||
}
|
||||
|
||||
private static MessagePushConfig getMessagePushConfig(
|
||||
String id,
|
||||
String pushTile,
|
||||
String invitePushContent,
|
||||
String data,
|
||||
String hw,
|
||||
String mi,
|
||||
String oppo,
|
||||
String threadId,
|
||||
String apnsId,
|
||||
boolean forceDetail) {
|
||||
return new MessagePushConfig.Builder() //
|
||||
.setPushTitle(pushTile) //
|
||||
.setPushContent(invitePushContent) //
|
||||
.setPushData(data) //
|
||||
.setForceShowDetailContent(forceDetail) //
|
||||
.setAndroidConfig(
|
||||
new AndroidConfig.Builder() //
|
||||
.setNotificationId(id) //
|
||||
.setChannelIdHW(hw) //
|
||||
.setChannelIdMi(mi) //
|
||||
.setChannelIdOPPO(oppo) //
|
||||
.setCategoryHW("VOIP")
|
||||
.setCategoryVivo("IM")
|
||||
.build()) //
|
||||
.setIOSConfig(new IOSConfig(threadId, apnsId))
|
||||
.build(); //
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import androidx.annotation.NonNull;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/** Created by dengxudong on 2018/5/18. */
|
||||
public class GlideBlurformation extends BitmapTransformation {
|
||||
private Context context;
|
||||
|
||||
public GlideBlurformation(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap transform(
|
||||
@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
|
||||
return BlurBitmapUtil.instance().blurBitmap(context, toTransform, 20, outWidth, outHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiskCacheKey(MessageDigest messageDigest) {}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/** Created by Ethan on 2018/6/1. */
|
||||
public class GlideRoundTransform extends BitmapTransformation {
|
||||
|
||||
private static float radius = 8f;
|
||||
|
||||
public GlideRoundTransform() {
|
||||
this(8);
|
||||
}
|
||||
|
||||
public GlideRoundTransform(int dp) {
|
||||
super();
|
||||
this.radius = Resources.getSystem().getDisplayMetrics().density * dp;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
|
||||
return roundCrop(pool, toTransform);
|
||||
}
|
||||
|
||||
private static Bitmap roundCrop(BitmapPool pool, Bitmap source) {
|
||||
if (source == null) return null;
|
||||
|
||||
Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
if (result == null) {
|
||||
result =
|
||||
Bitmap.createBitmap(
|
||||
source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
|
||||
Canvas canvas = new Canvas(result);
|
||||
Paint paint = new Paint();
|
||||
paint.setShader(
|
||||
new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
|
||||
paint.setAntiAlias(true);
|
||||
RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight());
|
||||
canvas.drawRoundRect(rectF, radius, radius, paint);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiskCacheKey(MessageDigest messageDigest) {}
|
||||
}
|
54
callkit/src/main/java/io/rong/callkit/util/GlideUtils.java
Normal file
@ -0,0 +1,54 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.widget.ImageView;
|
||||
import cn.rongcloud.rtc.utils.FinLog;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.Priority;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import io.rong.callkit.R;
|
||||
|
||||
/** Created by dengxudong on 2018/5/18. */
|
||||
public class GlideUtils {
|
||||
|
||||
private static final String TAG = GlideUtils.class.getSimpleName();
|
||||
|
||||
public static void showBlurTransformation(Context context, ImageView imageView, Uri val) {
|
||||
if (val == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Glide.with(context)
|
||||
.load(val)
|
||||
.apply(RequestOptions.bitmapTransform(new GlideBlurformation(context)))
|
||||
.apply(new RequestOptions().centerCrop())
|
||||
.into(imageView);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
FinLog.e(TAG, "Glide Utils Error=" + e.getMessage());
|
||||
} catch (NoSuchMethodError noSuchMethodError) {
|
||||
noSuchMethodError.printStackTrace();
|
||||
FinLog.e(TAG, "Glide NoSuchMethodError = " + noSuchMethodError.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static void showPortrait(Context context, ImageView imageView, Uri val) {
|
||||
RequestOptions requestOptions = new RequestOptions();
|
||||
requestOptions
|
||||
.transform(new GlideBlurformation(context))
|
||||
.priority(Priority.HIGH)
|
||||
.placeholder(R.drawable.rc_default_portrait)
|
||||
.apply(new RequestOptions().centerCrop());
|
||||
if (val == null) {
|
||||
Log.d(TAG, "showPortrait: val is Null");
|
||||
Glide.with(context)
|
||||
.load(R.drawable.rc_default_portrait)
|
||||
.apply(requestOptions)
|
||||
.into(imageView);
|
||||
} else {
|
||||
Glide.with(context).load(val).apply(requestOptions).into(imageView);
|
||||
}
|
||||
}
|
||||
}
|
45
callkit/src/main/java/io/rong/callkit/util/HeadsetInfo.java
Normal file
@ -0,0 +1,45 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
/** Created by Dengxudong on 2018/8/23. */
|
||||
public class HeadsetInfo {
|
||||
private boolean isInsert;
|
||||
private HeadsetType type;
|
||||
|
||||
public HeadsetInfo(boolean isInsert, HeadsetType type) {
|
||||
this.isInsert = isInsert;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public boolean isInsert() {
|
||||
return isInsert;
|
||||
}
|
||||
|
||||
public void setInsert(boolean insert) {
|
||||
isInsert = insert;
|
||||
}
|
||||
|
||||
public HeadsetType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(HeadsetType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public enum HeadsetType {
|
||||
/** 有线耳机 */
|
||||
WiredHeadset(0),
|
||||
/** 蓝牙耳机 */
|
||||
BluetoothA2dp(1);
|
||||
|
||||
int value;
|
||||
|
||||
HeadsetType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.view.View;
|
||||
import io.rong.imlib.model.UserInfo;
|
||||
|
||||
/** Created by dengxudong on 2018/5/18. */
|
||||
public interface ICallScrollView {
|
||||
void setScrollViewOverScrollMode(int mode);
|
||||
|
||||
void removeAllChild();
|
||||
|
||||
void removeChild(String childId);
|
||||
|
||||
View findChildById(String childId);
|
||||
|
||||
void updateChildState(String childId, boolean visible);
|
||||
|
||||
void updateChildState(String childId, String state);
|
||||
|
||||
void setChildPortraitSize(int size);
|
||||
|
||||
void enableShowState(boolean enable);
|
||||
|
||||
void addChild(String childId, UserInfo userInfo);
|
||||
|
||||
void addChild(String childId, UserInfo userInfo, String state);
|
||||
|
||||
void updateChildInfo(String childId, UserInfo userInfo);
|
||||
|
||||
int dip2pix(int dipValue);
|
||||
|
||||
View getChildAtIndex(int index);
|
||||
|
||||
int getChildrenSpace();
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import io.rong.callkit.CallFloatBoxView;
|
||||
import io.rong.callkit.VoIPBroadcastReceiver;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
import io.rong.push.notification.RongNotificationInterface;
|
||||
|
||||
/** 适配 Android 10 以上不允许后台启动 Activity 的工具类 */
|
||||
public class IncomingCallExtraHandleUtil {
|
||||
public static int VOIP_NOTIFICATION_ID = 3000; // VoIP类型的通知消息。
|
||||
public static final int VOIP_REQUEST_CODE = 30001;
|
||||
|
||||
private static RongCallSession cachedCallSession = null;
|
||||
private static boolean checkPermissions = false;
|
||||
|
||||
public static void removeNotification(Context context) {
|
||||
RongNotificationInterface.removeNotification(context, VOIP_NOTIFICATION_ID);
|
||||
removeAllPushServiceNotification(context);
|
||||
VoIPBroadcastReceiver.clearNotificationCache();
|
||||
}
|
||||
|
||||
public static void removeAllPushServiceNotification(Context context) {
|
||||
NotificationManager nm =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
for (int i = VOIP_NOTIFICATION_ID; i >= 3000; i--) {
|
||||
nm.cancel(i);
|
||||
}
|
||||
VOIP_NOTIFICATION_ID = 3000;
|
||||
}
|
||||
|
||||
public static RongCallSession getCallSession() {
|
||||
return cachedCallSession;
|
||||
}
|
||||
|
||||
public static void cacheCallSession(RongCallSession callSession, boolean permissions) {
|
||||
cachedCallSession = callSession;
|
||||
checkPermissions = permissions;
|
||||
}
|
||||
|
||||
public static boolean isCheckPermissions() {
|
||||
return checkPermissions;
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
cachedCallSession = null;
|
||||
checkPermissions = false;
|
||||
}
|
||||
|
||||
public static boolean needNotify() {
|
||||
return cachedCallSession != null && !CallFloatBoxView.isCallFloatBoxShown();
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import io.rong.calllib.RongCallSession;
|
||||
|
||||
public class RTCPhoneStateReceiver extends BroadcastReceiver {
|
||||
|
||||
private static final String TAG = "RTCPhoneStateReceiver";
|
||||
// 21以上会回调两次(状态值一样)
|
||||
private static String twice = "";
|
||||
private TelephonyManager mTelephonyManager;
|
||||
|
||||
public int getCallState(Context context) {
|
||||
if (context == null) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mTelephonyManager == null) {
|
||||
mTelephonyManager =
|
||||
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
}
|
||||
return mTelephonyManager.getCallState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (!("android.intent.action.PHONE_STATE").equals(action)) {
|
||||
Log.i(TAG, "action :" + action);
|
||||
return;
|
||||
}
|
||||
|
||||
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
|
||||
|
||||
if (TextUtils.isEmpty(state) && TextUtils.isEmpty(twice)) {
|
||||
int callState = getCallState(context);
|
||||
if (TelephonyManager.CALL_STATE_OFFHOOK == callState) {
|
||||
state = TelephonyManager.EXTRA_STATE_OFFHOOK;
|
||||
} else if (TelephonyManager.CALL_STATE_RINGING == callState) {
|
||||
state = TelephonyManager.EXTRA_STATE_RINGING;
|
||||
} else if (TelephonyManager.CALL_STATE_IDLE == callState) {
|
||||
state = TelephonyManager.EXTRA_STATE_IDLE;
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(TAG, "state : " + state + " , twice : " + twice);
|
||||
|
||||
if (!TextUtils.isEmpty(state) && !twice.equals(state)) {
|
||||
twice = state;
|
||||
|
||||
if (RongCallClient.getInstance() == null) {
|
||||
Log.e(TAG, "RongCallClient is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
RongCallSession callSession = RongCallClient.getInstance().getCallSession();
|
||||
if (callSession == null) {
|
||||
Log.e(TAG, "callSession is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (twice.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
|
||||
// ON_PHONE;
|
||||
RongCallClient.getInstance().hangUpCall();
|
||||
} else if (twice.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
|
||||
// ON_PHONE_ENDED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
callkit/src/main/java/io/rong/callkit/util/RingingMode.java
Normal file
@ -0,0 +1,13 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
public enum RingingMode {
|
||||
Incoming(0),
|
||||
Outgoing(1),
|
||||
Incoming_Custom(2);
|
||||
|
||||
private int val;
|
||||
|
||||
RingingMode(int val) {
|
||||
this.val = val;
|
||||
}
|
||||
}
|
@ -0,0 +1,358 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.AppOpsManagerCompat;
|
||||
import io.rong.callkit.util.permission.PermissionShowDetail;
|
||||
import io.rong.callkit.util.permission.PermissionType;
|
||||
import io.rong.calllib.RongCallCommon;
|
||||
import io.rong.common.RLog;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/** @author gusd @Date 2021/09/17 */
|
||||
public class RongCallPermissionUtil {
|
||||
private static final String TAG = "RongCallPermissionUtil";
|
||||
|
||||
public static void requestPermissions(
|
||||
Activity activity, PermissionType[] permissionTypes, int requestCode) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
return;
|
||||
}
|
||||
final List<String> permissionsNotGranted = new ArrayList<>();
|
||||
for (PermissionType audioCallPermission : permissionTypes) {
|
||||
if (!audioCallPermission.checkPermission(activity)) {
|
||||
permissionsNotGranted.add(audioCallPermission.getPermissionName());
|
||||
}
|
||||
}
|
||||
if (!permissionsNotGranted.isEmpty()) {
|
||||
activity.requestPermissions(permissionsNotGranted.toArray(new String[0]), requestCode);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 音频通话权限相关
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* 请求音频通话所需权限
|
||||
*
|
||||
* @param activity
|
||||
*/
|
||||
public static void requestAudioCallNeedPermission(Activity activity, final int requestCode) {
|
||||
requestPermissions(activity, getAudioCallPermissions(activity), requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取音频通话所需权限
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static PermissionType[] getAudioCallPermissions(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
&& context.getApplicationInfo() != null
|
||||
&& context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
|
||||
return new PermissionType[] {
|
||||
PermissionType.AudioRecord,
|
||||
PermissionType.BluetoothConnect,
|
||||
PermissionType.BluetoothScan,
|
||||
PermissionType.BluetoothAdvertise
|
||||
};
|
||||
} else {
|
||||
return new PermissionType[] {PermissionType.AudioRecord};
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkAudioCallNeedPermission(Context context) {
|
||||
for (PermissionType audioCallPermission : getAudioCallPermissions(context)) {
|
||||
if (!audioCallPermission.checkPermission(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean checkAndRequestAudioCallPermission(
|
||||
Activity activity, final int requestCode) {
|
||||
boolean granted = checkAudioCallNeedPermission(activity);
|
||||
if (!granted) {
|
||||
requestAudioCallNeedPermission(activity, requestCode);
|
||||
}
|
||||
return granted;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 视频通话权限相关
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
public static boolean checkVideoCallNeedPermission(Context context) {
|
||||
for (PermissionType audioCallPermission : getVideoCallPermissions(context)) {
|
||||
if (!audioCallPermission.checkPermission(context)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void requestVideoCallNeedPermission(Activity activity, final int requestCode) {
|
||||
requestPermissions(activity, getVideoCallPermissions(activity), requestCode);
|
||||
}
|
||||
|
||||
public static boolean checkAndRequestVideoCallPermission(
|
||||
Activity activity, final int requestCode) {
|
||||
boolean granted = checkVideoCallNeedPermission(activity);
|
||||
if (!granted) {
|
||||
requestVideoCallNeedPermission(activity, requestCode);
|
||||
}
|
||||
return granted;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取视频通话所需必要权限
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static PermissionType[] getVideoCallPermissions(Context context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
&& context.getApplicationInfo() != null
|
||||
&& context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) {
|
||||
return new PermissionType[] {
|
||||
PermissionType.CameraPermission,
|
||||
PermissionType.AudioRecord,
|
||||
PermissionType.BluetoothConnect,
|
||||
PermissionType.BluetoothScan,
|
||||
PermissionType.BluetoothAdvertise,
|
||||
};
|
||||
} else {
|
||||
return new PermissionType[] {
|
||||
PermissionType.CameraPermission, PermissionType.AudioRecord
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean checkAndRequestPermissionByCallType(
|
||||
Activity activity, RongCallCommon.CallMediaType type, final int requestCode) {
|
||||
if (RongCallCommon.CallMediaType.VIDEO.equals(type)) {
|
||||
return checkAndRequestVideoCallPermission(activity, requestCode);
|
||||
} else if (RongCallCommon.CallMediaType.AUDIO.equals(type)) {
|
||||
return checkAndRequestAudioCallPermission(activity, requestCode);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean checkPermissionByType(
|
||||
Context context, RongCallCommon.CallMediaType type) {
|
||||
if (RongCallCommon.CallMediaType.VIDEO.equals(type)) {
|
||||
return checkVideoCallNeedPermission(context);
|
||||
} else if (RongCallCommon.CallMediaType.AUDIO.equals(type)) {
|
||||
return checkAudioCallNeedPermission(context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 悬浮窗相关
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public static void requestFloatWindowNeedPermission(
|
||||
final Context context, final DialogInterface.OnClickListener listener) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
ArrayList<String> permissionList = new ArrayList<>();
|
||||
permissionList.add(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
|
||||
showPermissionAlert(
|
||||
context,
|
||||
context.getString(io.rong.imkit.R.string.rc_permission_grant_needed)
|
||||
+ getNotGrantedPermissionMsg(context, permissionList),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (listener != null) {
|
||||
listener.onClick(dialog, which);
|
||||
}
|
||||
if (DialogInterface.BUTTON_POSITIVE == which) {
|
||||
Intent intent =
|
||||
new Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + context.getPackageName()));
|
||||
if (intent.resolveActivity(context.getPackageManager()) != null) {
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static String getNotGrantedPermissionMsg(Context context, List<String> permissions) {
|
||||
if (permissions == null || permissions.size() == 0) {
|
||||
return "";
|
||||
}
|
||||
Set<String> permissionsValue = new HashSet<>();
|
||||
String permissionValue;
|
||||
try {
|
||||
for (String permission : permissions) {
|
||||
permissionValue =
|
||||
context.getString(
|
||||
context.getResources()
|
||||
.getIdentifier(
|
||||
"rc_" + permission,
|
||||
"string",
|
||||
context.getPackageName()),
|
||||
0);
|
||||
permissionsValue.add(permissionValue);
|
||||
}
|
||||
} catch (Resources.NotFoundException e) {
|
||||
RLog.e(
|
||||
TAG,
|
||||
"one of the permissions is not recognized by SDK." + permissions.toString());
|
||||
return "";
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder("(");
|
||||
for (String value : permissionsValue) {
|
||||
result.append(value).append(" ");
|
||||
}
|
||||
result = new StringBuilder(result.toString().trim() + ")");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
private static void showPermissionAlert(
|
||||
Context context, String content, DialogInterface.OnClickListener listener) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(content)
|
||||
.setPositiveButton(io.rong.imkit.R.string.rc_confirm, listener)
|
||||
.setNegativeButton(io.rong.imkit.R.string.rc_cancel, listener)
|
||||
.setNeutralButton(io.rong.imkit.R.string.rc_not_prompt, listener)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
public static boolean checkFloatWindowPermission(Context context) {
|
||||
return PermissionType.FloatWindow.checkPermission(context);
|
||||
}
|
||||
|
||||
public static void showRequestPermissionFailedAlter(
|
||||
final Context context, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
final String content = getNotGrantedPermissionMsg(context, permissions, grantResults);
|
||||
if (TextUtils.isEmpty(content)) {
|
||||
return;
|
||||
}
|
||||
DialogInterface.OnClickListener listener =
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case DialogInterface.BUTTON_POSITIVE:
|
||||
PermissionShowDetail.showPermissionDetail(context);
|
||||
break;
|
||||
case DialogInterface.BUTTON_NEGATIVE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
new AlertDialog.Builder(context, android.R.style.Theme_Material_Light_Dialog_Alert)
|
||||
.setMessage(content)
|
||||
.setPositiveButton(io.rong.imkit.R.string.rc_confirm, listener)
|
||||
.setNegativeButton(io.rong.imkit.R.string.rc_cancel, listener)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show();
|
||||
} else {
|
||||
new AlertDialog.Builder(context)
|
||||
.setMessage(content)
|
||||
.setPositiveButton(io.rong.imkit.R.string.rc_confirm, listener)
|
||||
.setNegativeButton(io.rong.imkit.R.string.rc_cancel, listener)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
private static String getNotGrantedPermissionMsg(
|
||||
Context context, String[] permissions, int[] grantResults) {
|
||||
if (permissions == null || permissions.length == 0) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
List<String> permissionNameList = new ArrayList<>(permissions.length);
|
||||
for (int i = 0; i < permissions.length; i++) {
|
||||
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
|
||||
String permissionName =
|
||||
context.getString(
|
||||
context.getResources()
|
||||
.getIdentifier(
|
||||
"rc_" + permissions[i],
|
||||
"string",
|
||||
context.getPackageName()),
|
||||
0);
|
||||
if (!permissionNameList.contains(permissionName)) {
|
||||
permissionNameList.add(permissionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder builder =
|
||||
new StringBuilder(
|
||||
context.getResources()
|
||||
.getString(io.rong.imkit.R.string.rc_permission_grant_needed));
|
||||
return builder.append("(")
|
||||
.append(TextUtils.join(" ", permissionNameList))
|
||||
.append(")")
|
||||
.toString();
|
||||
} catch (Resources.NotFoundException e) {
|
||||
RLog.e(
|
||||
TAG,
|
||||
"One of the permissions is not recognized by SDK."
|
||||
+ Arrays.toString(permissions));
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
public static boolean checkPermissions(Context context, @NonNull String[] permissions) {
|
||||
if (permissions.length == 0) {
|
||||
return true;
|
||||
}
|
||||
for (String permission : permissions) {
|
||||
PermissionType permissionType = PermissionType.gerPermissionByName(permission);
|
||||
if (permissionType != null) {
|
||||
if (!permissionType.checkPermission(context)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
boolean result = hasPermission(context, permission);
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean hasPermission(Context context, String permission) {
|
||||
String opStr = AppOpsManagerCompat.permissionToOp(permission);
|
||||
if (opStr == null && Build.VERSION.SDK_INT < 23) {
|
||||
return true;
|
||||
}
|
||||
return context != null
|
||||
&& context.checkCallingOrSelfPermission(permission)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
}
|
175
callkit/src/main/java/io/rong/callkit/util/SPUtils.java
Normal file
@ -0,0 +1,175 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author dengxudong
|
||||
* @version $Rev$
|
||||
*/
|
||||
public class SPUtils {
|
||||
public SPUtils() {
|
||||
/* cannot be instantiated */
|
||||
throw new UnsupportedOperationException("cannot be instantiated");
|
||||
}
|
||||
|
||||
/** 保存在手机里面的文件名 */
|
||||
public static final String FILE_NAME = "doudou";
|
||||
|
||||
/** 保存当前时间 */
|
||||
public static void putDataAndTime(Context context, String key) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
Date nowdate = new Date();
|
||||
long timeData = nowdate.getTime();
|
||||
editor.putLong(key, timeData);
|
||||
SharedPreferencesCompat.apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
|
||||
*
|
||||
* @param context
|
||||
* @param key
|
||||
* @param object
|
||||
*/
|
||||
public static void put(Context context, 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 context
|
||||
* @param key
|
||||
* @param defaultObject
|
||||
* @return
|
||||
*/
|
||||
public static Object get(Context context, 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值已经对应的值
|
||||
*
|
||||
* @param context
|
||||
* @param key
|
||||
*/
|
||||
public static void remove(Context context, String key) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.remove(key);
|
||||
SharedPreferencesCompat.apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有数据
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public static void clear(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.clear();
|
||||
SharedPreferencesCompat.apply(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询某个key是否已经存在
|
||||
*
|
||||
* @param context
|
||||
* @param key
|
||||
* @return
|
||||
*/
|
||||
public static boolean contains(Context context, String key) {
|
||||
SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
|
||||
return sp.contains(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回所有的键值对
|
||||
*
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, ?> getAll(Context context) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
package io.rong.callkit.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import io.rong.calllib.CallUserProfile;
|
||||
import io.rong.calllib.RongCallClient;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class UserProfileOrderManager {
|
||||
|
||||
private static final String TAG = "UserProfileOrderManager";
|
||||
private final ArrayList<String> userIds;
|
||||
|
||||
public UserProfileOrderManager() {
|
||||
this.userIds = new ArrayList<>();
|
||||
}
|
||||
|
||||
public UserProfileOrderManager(ArrayList<String> value) {
|
||||
this.userIds = new ArrayList<>();
|
||||
if (value != null && !value.isEmpty()) {
|
||||
this.userIds.addAll(value);
|
||||
}
|
||||
ArrayList<String> userIdsTmp = new ArrayList<>();
|
||||
if (RongCallClient.getInstance() != null
|
||||
&& RongCallClient.getInstance().getCallSession() != null) {
|
||||
if (RongCallClient.getInstance().getCallSession().getParticipantProfileList() != null) {
|
||||
for (CallUserProfile userProfile :
|
||||
RongCallClient.getInstance().getCallSession().getParticipantProfileList()) {
|
||||
if (!this.userIds.contains(userProfile.getUserId())) {
|
||||
Log.e(TAG, "userIdsTmp.add : " + userProfile.getUserId());
|
||||
userIdsTmp.add(userProfile.getUserId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!userIdsTmp.isEmpty()) {
|
||||
this.userIds.addAll(userIdsTmp);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CallUserProfile> getSortedProfileList(
|
||||
List<CallUserProfile> participantProfileList) {
|
||||
if (this.userIds.isEmpty()) {
|
||||
// Log.e(TAG, "-------getSortedProfileList--->isEmpty");
|
||||
for (CallUserProfile userProfile : participantProfileList) {
|
||||
this.userIds.add(userProfile.getUserId());
|
||||
}
|
||||
return participantProfileList;
|
||||
} else {
|
||||
List<CallUserProfile> callUserProfileList = new ArrayList<>(this.userIds.size());
|
||||
for (String userId : this.userIds) {
|
||||
// Log.e(TAG,
|
||||
// "-------getSortedProfileList--->userId : "+userId);
|
||||
for (CallUserProfile callUserProfile : participantProfileList) {
|
||||
if (TextUtils.equals(userId, callUserProfile.getUserId())) {
|
||||
callUserProfileList.add(callUserProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return callUserProfileList;
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<String> getUserIds() {
|
||||
// Log.d(TAG, "------getUserIds.start");
|
||||
// if (userIds != null && userIds.size() >= 0) {
|
||||
// for (String userId : userIds) {
|
||||
// Log.d(TAG, "getUserIds.id: " + userId);
|
||||
// }
|
||||
// }
|
||||
return userIds;
|
||||
}
|
||||
|
||||
public void exchange(String fromUserId, String toUserId) {
|
||||
int fromUserIdIndex = -1, toUserIdIndex = -1;
|
||||
for (int i = 0; i < this.userIds.size(); i++) {
|
||||
if (TextUtils.equals(userIds.get(i), fromUserId)) {
|
||||
fromUserIdIndex = i;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(userIds.get(i), toUserId)) {
|
||||
toUserIdIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (fromUserIdIndex != -1) {
|
||||
try {
|
||||
userIds.set(fromUserIdIndex, toUserId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (toUserIdIndex != -1) {
|
||||
try {
|
||||
userIds.set(toUserIdIndex, fromUserId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Log.e(TAG, "-----exchange----fromUserId : "+fromUserId + ", fromUserIdIndex :
|
||||
// "+fromUserIdIndex + " , toUserId : "+toUserId +" ,toUserIdIndex : " +toUserIdIndex);
|
||||
}
|
||||
}
|
@ -0,0 +1,263 @@
|
||||
package io.rong.callkit.util.permission;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AppOpsManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.media.AudioFormat;
|
||||
import android.media.AudioRecord;
|
||||
import android.media.MediaRecorder;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import androidx.core.app.AppOpsManagerCompat;
|
||||
import io.rong.common.RLog;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author gusd @Date 2021/09/17
|
||||
* @escription 处理系统之间的差异化问题
|
||||
*/
|
||||
public enum DeviceAdapter {
|
||||
defaultAdapter(), // 默认 adapter
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 小米手机
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
MiuiAdapter() {
|
||||
@Override
|
||||
public void gotoAppPermissionSettingPage(Context context) {
|
||||
try { // MIUI 8
|
||||
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
localIntent.setClassName(
|
||||
"com.miui.securitycenter",
|
||||
"com.miui.permcenter.permissions.PermissionsEditorActivity");
|
||||
localIntent.putExtra("extra_pkgname", context.getPackageName());
|
||||
localIntent.setPackage(context.getPackageName());
|
||||
context.startActivity(localIntent);
|
||||
} catch (Exception e) {
|
||||
try { // MIUI 5/6/7
|
||||
Intent localIntent = new Intent("miui.intent.action.APP_PERM_EDITOR");
|
||||
localIntent.setClassName(
|
||||
"com.miui.securitycenter",
|
||||
"com.miui.permcenter.permissions.AppPermissionsEditorActivity");
|
||||
localIntent.putExtra("extra_pkgname", context.getPackageName());
|
||||
localIntent.setPackage(context.getPackageName());
|
||||
context.startActivity(localIntent);
|
||||
} catch (Exception e1) { // 否则跳转到应用详情
|
||||
super.gotoAppPermissionSettingPage(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkLockScreenDisplayPermission(Context context) {
|
||||
AppOpsManager ops = null;
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.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;
|
||||
}
|
||||
},
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 魅族手机
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
MeiZuAdapter() {
|
||||
private static final String TAG = "MeiZuAdapter";
|
||||
|
||||
@Override
|
||||
public void gotoAppPermissionSettingPage(Context context) {
|
||||
try {
|
||||
Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
|
||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
intent.putExtra("packageName", context.getApplicationInfo().processName);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
super.gotoAppPermissionSettingPage(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkRecordPermission(Context context) {
|
||||
return super.checkRecordPermission(context) || hasRecordPermision(context);
|
||||
}
|
||||
|
||||
private boolean hasRecordPermision(Context context) {
|
||||
boolean hasPermission = false;
|
||||
int bufferSizeInBytes =
|
||||
AudioRecord.getMinBufferSize(
|
||||
44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
|
||||
if (bufferSizeInBytes < 0) {
|
||||
RLog.e(TAG, "bufferSizeInBytes = " + bufferSizeInBytes);
|
||||
return false;
|
||||
}
|
||||
AudioRecord audioRecord;
|
||||
try {
|
||||
audioRecord =
|
||||
new AudioRecord(
|
||||
MediaRecorder.AudioSource.MIC,
|
||||
44100,
|
||||
AudioFormat.CHANNEL_IN_STEREO,
|
||||
AudioFormat.ENCODING_PCM_16BIT,
|
||||
bufferSizeInBytes);
|
||||
audioRecord.startRecording();
|
||||
if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
|
||||
hasPermission = true;
|
||||
audioRecord.stop();
|
||||
}
|
||||
audioRecord.release();
|
||||
} catch (Exception e) {
|
||||
RLog.e(TAG, "Audio record exception.");
|
||||
}
|
||||
return hasPermission;
|
||||
}
|
||||
},
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// 华为手机
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
HuiWeiAdapter() {
|
||||
private static final String TAG = "HuiWeiAdapter";
|
||||
|
||||
@Override
|
||||
public void gotoAppPermissionSettingPage(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"); // 华为权限管理
|
||||
intent.setComponent(comp);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
super.gotoAppPermissionSettingPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// oppo 手机
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
OppoAdapter() {
|
||||
@Override
|
||||
public void gotoAppPermissionSettingPage(Context context) {
|
||||
try {
|
||||
Intent intent = new Intent("android.settings.APPLICATION_DETAILS_SETTINGS");
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra("packageName", context.getApplicationInfo().processName);
|
||||
ComponentName comp =
|
||||
new ComponentName(
|
||||
"com.coloros.securitypermission",
|
||||
"com.coloros.securitypermission.permission.PermissionAppAllPermissionActivity"); // R11t 7.1.1 os-v3.2
|
||||
intent.setComponent(comp);
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
super.gotoAppPermissionSettingPage(context);
|
||||
}
|
||||
}
|
||||
},
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// vivo 手机
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
VivoAdapter() {
|
||||
@Override
|
||||
public boolean checkLockScreenDisplayPermission(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;
|
||||
}
|
||||
};
|
||||
|
||||
private static final String TAG = "DeviceAdapter";
|
||||
|
||||
public static DeviceAdapter getDeviceAdapter() {
|
||||
DeviceAdapter adapter = null;
|
||||
if (OSUtils.isEmui()) {
|
||||
adapter = DeviceAdapter.HuiWeiAdapter;
|
||||
} else if (OSUtils.isFlyme()) {
|
||||
adapter = DeviceAdapter.MeiZuAdapter;
|
||||
} else if (OSUtils.isMiui()) {
|
||||
adapter = DeviceAdapter.MiuiAdapter;
|
||||
} else if (OSUtils.isOppo()) {
|
||||
adapter = DeviceAdapter.OppoAdapter;
|
||||
} else if (OSUtils.isVivo()) {
|
||||
adapter = DeviceAdapter.VivoAdapter;
|
||||
} else {
|
||||
adapter = DeviceAdapter.defaultAdapter;
|
||||
}
|
||||
RLog.d(TAG, "current device adapter = " + adapter.getClass().getName());
|
||||
return adapter;
|
||||
}
|
||||
|
||||
public void gotoAppPermissionSettingPage(Context context) {
|
||||
Intent intent = new Intent();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setData(Uri.fromParts("package", context.getPackageName(), null));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public boolean checkRecordPermission(Context context) {
|
||||
String opStr = AppOpsManagerCompat.permissionToOp(Manifest.permission.RECORD_AUDIO);
|
||||
if (opStr == null && Build.VERSION.SDK_INT < 23) {
|
||||
return true;
|
||||
}
|
||||
return context != null
|
||||
&& context.checkCallingOrSelfPermission(Manifest.permission.RECORD_AUDIO)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
// 只有 小米 和 vivo 设备可检测,其他设备无法检测,默认返回 true
|
||||
public boolean checkLockScreenDisplayPermission(Context context) {
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
package io.rong.callkit.util.permission;
|
||||
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/** Created by Android Studio. User: lvhongzhen Date: 2019-08-15 Time: 01:49 */
|
||||
public class OSUtils {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public static boolean isVivo() {
|
||||
return check(ROM_VIVO);
|
||||
}
|
||||
|
||||
public static boolean isOppo() {
|
||||
return check(ROM_OPPO);
|
||||
}
|
||||
|
||||
public static boolean isFlyme() {
|
||||
return check(ROM_FLYME);
|
||||
}
|
||||
|
||||
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) {
|
||||
return null;
|
||||
} finally {
|
||||
if (input != null) {
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return line;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package io.rong.callkit.util.permission;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
public class PermissionShowDetail {
|
||||
private static final String VIVO = "vivo";
|
||||
|
||||
public static void showPermissionDetail(Context context) {
|
||||
String manufacturer = Build.MANUFACTURER.toLowerCase();
|
||||
switch (manufacturer) {
|
||||
case VIVO:
|
||||
openVIVODetail(context);
|
||||
break;
|
||||
default:
|
||||
defaultToDetail(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void defaultToDetail(Context context) {
|
||||
try {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.setPackage(context.getPackageName());
|
||||
Uri uri = Uri.fromParts("package", context.getPackageName(), null);
|
||||
intent.setData(uri);
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Intent intent = new Intent(Settings.ACTION_SETTINGS);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private static void openVIVODetail(Context context) {
|
||||
Intent localIntent;
|
||||
if (((Build.MODEL.contains("Y85")) && (!Build.MODEL.contains("Y85A")))
|
||||
|| (Build.MODEL.contains("vivo Y53L"))) {
|
||||
localIntent = new Intent();
|
||||
localIntent.setClassName(
|
||||
"com.vivo.permissionmanager",
|
||||
"com.vivo.permissionmanager.activity.PurviewTabActivity");
|
||||
localIntent.putExtra("packagename", context.getPackageName());
|
||||
localIntent.putExtra("tabId", "1");
|
||||
context.startActivity(localIntent);
|
||||
} else {
|
||||
localIntent = new Intent();
|
||||
localIntent.setClassName(
|
||||
"com.vivo.permissionmanager",
|
||||
"com.vivo.permissionmanager.activity.SoftPermissionDetailActivity");
|
||||
localIntent.setAction("secure.intent.action.softPermissionDetail");
|
||||
localIntent.putExtra("packagename", context.getPackageName());
|
||||
context.startActivity(localIntent);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
package io.rong.callkit.util.permission;
|
||||
|
||||
import android.Manifest;
|
||||
import android.Manifest.permission;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.AppOpsManagerCompat;
|
||||
import io.rong.common.RLog;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** @author gusd @Date 2021/09/17 别在多线程里面用 */
|
||||
public enum PermissionType {
|
||||
|
||||
/** {@link io.rong.callkit.util.RTCPhoneStateReceiver } 类逻辑需要该权限 用于监听SIM卡来电 */
|
||||
ReadPhoneStatePermission(permission.READ_PHONE_STATE) {
|
||||
@Override
|
||||
public boolean checkPermission(Context context) {
|
||||
return super.checkPermission(context);
|
||||
}
|
||||
},
|
||||
|
||||
// 摄像头权限
|
||||
CameraPermission(Manifest.permission.CAMERA) {
|
||||
@Override
|
||||
public boolean checkPermission(Context context) {
|
||||
return super.checkPermission(context);
|
||||
}
|
||||
},
|
||||
// 录音权限
|
||||
AudioRecord(Manifest.permission.RECORD_AUDIO) {
|
||||
public boolean checkPermission(Context context) {
|
||||
return DeviceAdapter.getDeviceAdapter().checkRecordPermission(context);
|
||||
}
|
||||
},
|
||||
// android 12 的蓝牙权限
|
||||
BluetoothConnect("android.permission.BLUETOOTH_CONNECT") {
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return Build.VERSION_CODES.S;
|
||||
}
|
||||
},
|
||||
|
||||
BluetoothScan("android.permission.BLUETOOTH_SCAN") {
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return Build.VERSION_CODES.S;
|
||||
}
|
||||
},
|
||||
|
||||
BluetoothAdvertise("android.permission.BLUETOOTH_ADVERTISE") {
|
||||
@Override
|
||||
public int getVersion() {
|
||||
return Build.VERSION_CODES.S;
|
||||
}
|
||||
},
|
||||
|
||||
// 悬浮窗
|
||||
FloatWindow("android.settings.action.MANAGE_OVERLAY_PERMISSION") {
|
||||
private static final String TAG = "FloatWindow";
|
||||
|
||||
@Override
|
||||
public boolean checkPermission(final Context context) {
|
||||
boolean result = true;
|
||||
boolean booleanValue;
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
try {
|
||||
booleanValue =
|
||||
(Boolean)
|
||||
Settings.class
|
||||
.getDeclaredMethod("canDrawOverlays", Context.class)
|
||||
.invoke(null, new Object[] {context});
|
||||
RLog.i(TAG, "isFloatWindowOpAllowed allowed: " + booleanValue);
|
||||
return booleanValue;
|
||||
} catch (Exception e) {
|
||||
RLog.e(
|
||||
TAG,
|
||||
String.format(
|
||||
"getDeclaredMethod:canDrawOverlays! Error:%s, etype:%s",
|
||||
e.getMessage(), e.getClass().getCanonicalName()));
|
||||
return true;
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT < 19) {
|
||||
return true;
|
||||
} else if (Build.BRAND.toLowerCase().contains("xiaomi")) {
|
||||
Method method;
|
||||
Object systemService = context.getSystemService(Context.APP_OPS_SERVICE);
|
||||
try {
|
||||
method =
|
||||
Class.forName("android.app.AppOpsManager")
|
||||
.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);
|
||||
} catch (NoSuchMethodException e) {
|
||||
RLog.e(
|
||||
TAG,
|
||||
String.format(
|
||||
"NoSuchMethodException method:checkOp! Error:%s",
|
||||
e.getMessage()));
|
||||
method = null;
|
||||
} catch (ClassNotFoundException e) {
|
||||
RLog.e(TAG, "canDrawOverlays", e);
|
||||
method = null;
|
||||
}
|
||||
if (method != null) {
|
||||
try {
|
||||
Integer tmp =
|
||||
(Integer)
|
||||
method.invoke(
|
||||
systemService,
|
||||
new Object[] {
|
||||
24,
|
||||
context.getApplicationInfo().uid,
|
||||
context.getPackageName()
|
||||
});
|
||||
result = tmp != null && tmp == 0;
|
||||
} catch (Exception e) {
|
||||
RLog.e(
|
||||
TAG,
|
||||
String.format(
|
||||
"call checkOp failed: %s etype:%s",
|
||||
e.getMessage(), e.getClass().getCanonicalName()));
|
||||
}
|
||||
}
|
||||
RLog.i(TAG, "isFloatWindowOpAllowed allowed: " + result);
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gotoSettingPage(Context context) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
|
||||
Intent intent =
|
||||
new Intent(
|
||||
Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + context.getPackageName()));
|
||||
intent.setPackage(context.getPackageName());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
},
|
||||
// 悬浮通知
|
||||
FloatNotification("FloatNotificationPermission") {
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean checkPermission(Context context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean checkPermission(Context context, String channelId) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return false;
|
||||
}
|
||||
NotificationManager mNotificationManager =
|
||||
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
NotificationChannel channel =
|
||||
mNotificationManager.getNotificationChannel(channelId); // CHANNEL_ID是自己定义的渠道ID
|
||||
return channel.getImportance() == NotificationManager.IMPORTANCE_HIGH;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public void gotoSettingPage(Context context) {
|
||||
super.gotoSettingPage(context);
|
||||
}
|
||||
|
||||
public void gotoSettingPage(Context context, String channelId) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.putExtra(Settings.EXTRA_APP_PACKAGE, context.getPackageName());
|
||||
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
},
|
||||
// 通知锁屏显示
|
||||
DisplayLockScreen("DisplayLockScreen") {
|
||||
@Override
|
||||
public boolean checkPermission(Context context) {
|
||||
return DeviceAdapter.getDeviceAdapter().checkLockScreenDisplayPermission(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gotoSettingPage(Context context) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Intent intent = new Intent();
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
|
||||
intent.putExtra("app_package", context.getPackageName());
|
||||
intent.putExtra("app_uid", context.getApplicationInfo().uid);
|
||||
context.startActivity(intent);
|
||||
} else if (android.os.Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
|
||||
intent.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
intent.setPackage(context.getPackageName());
|
||||
intent.setData(Uri.parse("package:" + context.getPackageName()));
|
||||
context.startActivity(intent);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static final Map<String, PermissionType> permissionMap = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (PermissionType value : PermissionType.values()) {
|
||||
permissionMap.put(value.mPermissionName, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PermissionType gerPermissionByName(String permissionName) {
|
||||
return permissionMap.get(permissionName);
|
||||
}
|
||||
|
||||
private final String mPermissionName;
|
||||
// 是否为必要权限
|
||||
private boolean isNecessary;
|
||||
|
||||
PermissionType(@NonNull String permissionName) {
|
||||
this.mPermissionName = permissionName;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getPermissionName() {
|
||||
return mPermissionName;
|
||||
}
|
||||
|
||||
/** 跳转到对应的设置页面 */
|
||||
public void gotoSettingPage(Context context) {
|
||||
// 默认跳转到权限设置界面
|
||||
DeviceAdapter.getDeviceAdapter().gotoAppPermissionSettingPage(context);
|
||||
}
|
||||
|
||||
public boolean checkPermission(Context context) {
|
||||
String opStr = AppOpsManagerCompat.permissionToOp(mPermissionName);
|
||||
if (opStr == null && Build.VERSION.SDK_INT < 23) {
|
||||
return true;
|
||||
}
|
||||
return context != null
|
||||
&& context.checkCallingOrSelfPermission(mPermissionName)
|
||||
== PackageManager.PERMISSION_GRANTED;
|
||||
}
|
||||
|
||||
// 权限对应的版本
|
||||
public int getVersion() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public boolean isNecessary() {
|
||||
return isNecessary;
|
||||
}
|
||||
|
||||
public void setNecessary(boolean isNecessary) {
|
||||
this.isNecessary = isNecessary;
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 634 B |
BIN
callkit/src/main/res/drawable-xhdpi/callkit_ic_nav_back_x.png
Normal file
After Width: | Height: | Size: 652 B |
BIN
callkit/src/main/res/drawable-xhdpi/callkit_ic_search.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 473 B |
After Width: | Height: | Size: 1.3 KiB |
BIN
callkit/src/main/res/drawable-xhdpi/callkit_ic_search_x.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 886 B |
BIN
callkit/src/main/res/drawable-xhdpi/callkit_mute_unavailable.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 652 B |
BIN
callkit/src/main/res/drawable-xhdpi/rc_ic_phone_normal.png
Normal file
After Width: | Height: | Size: 525 B |
BIN
callkit/src/main/res/drawable-xhdpi/rc_ic_phone_pressed.png
Normal file
After Width: | Height: | Size: 4.5 KiB |
BIN
callkit/src/main/res/drawable-xhdpi/rc_voip_add.png
Normal file
After Width: | Height: | Size: 848 B |
BIN
callkit/src/main/res/drawable-xhdpi/rc_voip_audio_answer.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 623 B |
After Width: | Height: | Size: 912 B |
After Width: | Height: | Size: 679 B |
After Width: | Height: | Size: 982 B |
BIN
callkit/src/main/res/drawable-xhdpi/rc_voip_camera.png
Normal file
After Width: | Height: | Size: 4.6 KiB |