同步ios接听界面

This commit is contained in:
zlzw 2023-11-06 18:24:57 +08:00
parent 23a2a2efb3
commit 48b30868f4
212 changed files with 17437 additions and 24 deletions

View File

@ -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

View File

@ -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);
}

View File

@ -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()) {

View File

@ -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 -> {

View File

@ -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();
}

View File

@ -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() {

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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));
}
});
}

View File

@ -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);

View File

@ -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();

View File

@ -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) {

View File

@ -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));
}
});
}

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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
View File

@ -0,0 +1 @@
/build

43
callkit/README.md Normal file
View 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
View 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
View 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 *;
#}

View 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>

View 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;
}
}

View 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 initViewincoming处注册 */
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 !");
}
}
}
//
}

View File

@ -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);
}
}

View File

@ -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));
}
}
}

File diff suppressed because it is too large Load Diff

View 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);
}
}

View 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);
}
}
}
}

View 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();
}
}

View File

@ -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());
}
}

View 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);
}
}

View 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;
}

View File

@ -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);
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}

File diff suppressed because it is too large Load Diff

View 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);
}
}

View 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;
}
}

View File

@ -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);
}

View 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;
}
}

View 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]);
}
}
}
}

View 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);
}
}
}
}

View File

@ -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();
}
}
}

View 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";
}

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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();
}
}

View File

@ -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();
}
}
}

View 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);
}
}
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,16 @@
package io.rong.callkit.util;
public interface CallKitSearchBarListener {
/**
* 开始搜索 EditText 中输入内容后会触发此回调
*
* @param keyword 搜索关键字
*/
void onSearchStart(String keyword);
/** 软键盘中"搜索"被点击后,触发此回调 此回调被触发后,仅收起软键盘 */
void onSoftSearchKeyClick();
/** 搜索控件中,点击"清除"后,触发此回调 */
void onClearButtonClick();
}

View File

@ -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;
}
}

View 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);
}
}

View 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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}

View File

@ -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(); //
}
}

View File

@ -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) {}
}

View File

@ -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) {}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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;
}
}
}
}

View 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;
}
}

View File

@ -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;
}
}

View 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();
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 634 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 848 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 912 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

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