Android SDK Quick Start
This page is for quick integration, visit detailed documentation
Android SDK integration guide
SDK architecture
Lanying IM SDK (codename floo) is implemented in C++, and each platform (Android, iOS, Linux, etc.) is encapsulated on this basis to complete the development of local library, so as to achieve the purpose of multi-platform reuse and keep the consistency of cross-platform protocol implementation. Floo-Android, as a native application library for Android, has two APIs available to developers, namely, low-level API and high-level API, in other words synchronous interface and asynchronous interface.
Low level API (low-level)
Synchronous interface, the class name ends with Service, which is represented by L/S in the following description for easier understanding, where L stands for Low-level and S for Sync.
As mentioned earlier, floo-android entity is SWIG generated by framework automatically. Its java code is generated by SWIG, invokes the underlaying C++ class library by JNI, so most interfaces are synchronous, that is, the result is returned after interface invoked.
In code generation and transformation process, related data structures are directly mapped to the underlying class library for reducing memory copies, so its performance is close to calling the underlying library directly.
Synchronous service classes are as follows:
- BMXClient: SDK aggregation class, which contains all service classes and implements network event listening interface
- BMXChatService: message sending, message history retrieval, conversation list
- BMXUserService: Register account, login, logout, my settings
- BMXRosterService: Friend list, blacklist
- BMXGroupService: Group management (create, dissolve, search, set, member management, invite, apply, accept, reject)
High level API (high-level)
Asynchronous interface, and the class name ends with Manager, which is represented by H/A for easier understanding, where H for High-level and A for Async.
We also re-encapsulated high-level API based on this class, using a more friendly data structure and completing asynchronous encapsulation for development integration.
Simply put, the related call performs specific actions on child thread (for example, searching for friends), and the current thread will return result directly without blocking. The result of the action is notified to the caller through a callback function, where the caller can handle business logic such as UI refresh.
Asynchronous service classes are as follows:
- BaseManeger:Manger: Manage basic classes
- ChatManager: Message sending, message history retrieval, conversation list
- UserManager: Register account, login, logout, my settings
- RosterManager: Friend list, blacklist
- BMXCallBack: Untyped interface callback
- BMXDataCallBack<T>: Generic type with data callback
Other tool classes
- BMXGroupServiceListener: Group event listening
- BMXUserServiceListener: User event listening
- BMXRosterServiceListener: Friend event listening
- BMXNetworkListener: Network event listening interface, implemented by BMXClient
- BMXConversation: Conversation
- BMXMessage: Message
- BMXGroup: Group
- BMXRosterItem roster item (friend, stranger, blacklist, former friend)
- BMXUserProfile: User information
Class library diagram as follows
+---> BMXUserService
|
+---------------+ +---> BMXChatService
| | |
+---+ Low-level API: L/S +------> BMXRosterService
| | | |
| +---------------+ +---> BMXGroupService
|
| +---> BMXUserManager
| +---------------+ |
+----------------------+ | | | +---> BMXChatManager
| | +---+ High-level API: H/A +-----+
| Lanying IM SDK: Floo +--+ | | +---> BMXRosterManager
| | | +---------------+ |
+----------------------+ | +---> BMXGroupManager
|
| +---> BMXClient
| +----------------+ |
| | | +---> BMXSDKConfig
+---+ Utility:Tool class +--+
| | +---> BMXMessage
+----------------+ |
+---> BMXConversation
|
+---> BMXUserProfile
|
+---> BMXGroup
|
+---> BMXDevice
Import SDK
Aar or jar + so formats are selectable in SDK import
aar format
- Download aar file to /libs in project
- Add dependency in the dependencies block of build.gradle file, refer to lanying-im-android sourcefor the latest version.
implementation(name:'floo-android_2.3.1.20200428',ext:'aar')
implementation 'com.squareup.okhttp3:okhttp:3.12.3'
implementation 'com.squareup.okio:okio:1.12.0'
jar+so format
- Download jar package and so library to /lib in project
- Add implementation fileTree(dir: ‘libs’, include: [’*.jar’]) to build.gradle file
Permission configuration
Add permissions as follows in AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
APP obfuscation
Add -keep class im.floo.floolib.**{*;} in ProGuard file
Quick integration
BMXClient initialization
Step 1 Import so library file
Add import to app entry class
static {
System.loadLibrary("floo");
}
Step 2 - Initialize BMXClient
// Set storage path
String appPath = AppContextUtils.getAppContext().getFilesDir().getPath();
File dataPath = new File(appPath + "/data_dir");
File cachePath = new File(appPath + "/cache_dir");
dataPath.mkdirs();
cachePath.mkdirs();
// Set ID for push platform
String pushId = getPushId();
// Set sdk config
BMXSDKConfig config = new BMXSDKConfig(BMXClientType.Android, "1", dataPath.getAbsolutePath(),
cachePath.getAbsolutePath(), TextUtils.isEmpty(pushId) ? "MaxIM" : pushId);
config.setConsoleOutput(true);
config.setLogLevel(BMXLogLevel.Debug);
// Initialize BMXClient
BMXClient bmxClient = BMXClient.create(conf);
Note: If you use a High-level API (asynchronous invocation), following methods are required to get the service class API entity:
- ChatManager: Get message’s manager object via bmxClient.getChatManager().
- UserManager: Get user’s manager object via bmxClient.getUserManager().
- GroupManager: Get group’s manager object via bmxClient.getGroupManager().
- RosterManager: Get roster’s manager object via bmxClient.getRosterManager().
Register user
L/S: BMXUserProfile object reference is passed in and synchronous called, after which the profile is retrieved.
bmxClient.signUpNewUser("zhangsan", "sFo!slk1x", new BMXUserProfile());
H/A: Asynchronous call on BMXDataCallBackReturn profile instance in callback.
bmxClient.getUserManager().signUpNewUser("zhangsan", "sFo!slk1x", new BMXDataCallBack<BMXUserProfile>(){
@Override
public void onResult(BMXErrorCode bmxErrorCode, BMXUserProfile bmxUserProfile) {
//Return profile
}
});
Login
Two login modes: One is normal manual login, which involves getting the token and establishing a tcp connection; the other is quick login, which caches the token locally.
L/S: Success is judged by the value of returned BMXErrorCode.
// Parameter:username password
bmxClient.getUserService().signInByName("zhangsan", "sFo!slk1x");
bmxClient.getUserService().fastSignInByName("zhangsan", "sFo!slk1x");
H/A: Success is judge by the returned BMXErrorCode in BMXCallBack.
bmxClient.getUserManager().signInByName("zhangsan", "sFo!slk1x", new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
}
});
bmxClient.getUserManager().signInByName("zhangsan", "sFo!slk1x", new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
}
});
Get conversation list
L/S: BMXConversationList object reference is passed in and called, after which the conversation list is retrieved.
BMXConversationList cl = bmxClient.getChatService().getAllConversations();
for (int i=0; i<cl.size(); i++){
BMXConversation c = cl.get(i);
Log.e("conversation id:",""+c.conversationId());
}
H/A: From BMXDataCallBackto get conversation list.
bmxClient.getChatManager().getAllConversations(new BMXDataCallBack<BMXConversationList>() {
@Override
public void onResult(BMXErrorCode bmxErrorCode, BMXConversationList list) {
//Return BMXConversation instance
for (int i=0; i<cl.size(); i++){
BMXConversation c = cl.get(i);
Log.e("conversation id:",""+c.conversationId());
}
}
});
Disconnect
L/S: Success is judged by the value of returned BMXErrorCode.
bmxClient.getUserService().signOut();
H/A: Judged by BMXErrorCode in BMXCallBack.
bmxClient.getUserManager().signOut(new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode
}
});
User system
- Add friend
L/S: BMXErrorCode is retrieved to determine success. Parameter description: RosterId reason
bmxClient.getRosterService().apply(22342342, "Hi, I'm Lisi");
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getRosterManager().apply(22342342, "Hi, I'm Lisi", new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode
}
});
- Delete friend
L/S: Success is judged by the value of returned BMXErrorCode.
bmxClient.getRosterService().remove(22342342);
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getRosterManager().remove(22342342, new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode
}
});
- Agree to add friend
L/S: Success is judged by the value of returned BMXErrorCode.
//Parameter description: rosterId
bmxClient.getRosterService().accept(333453112);
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getRosterManager().accept(333453112, new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode
}
});
- Reject to add friend
L/S: Success is judged by the value of returned BMXErrorCode.
//Parameter description: rosterId reason
bmxClient.getRosterService().decline(333453112,"I'm not Lisi");
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getRosterManager().decline(333453112,"I'm not Lisi", new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode
}
});
- Get rosterId
L/S: ListOfLongLong object reference is passed in and called, after which the rosterId list is retrieved.
ListOfLongLong roster = new ListOfLongLong();
bmxClient.getRosterService().get(roster, true);
for (int i=0; i<roster.size(); i++){
long roster_id = roster.get(i);
Log.e("roster id:",""+roster_id);
}
H/A: From BMXDataCallBackGet rosterID list in callback.
bmxClient.getRosterManager().get(roster, true, new BMXDataCallBack<ListOfLongLong>(){
@Override
public void onResult(BMXErrorCode bmxErrorCode, ListOfLongLong list) {
//Return ListOfLongLong instance
for (int i=0; i<list.size(); i++){
long roster_id = list.get(i);
Log.e("roster id:",""+roster_id);
}
}
});
Basic features
Single chat
Single chat refers to one-to-one chat function, BMXConversationType of single chat is BMXConversationSingle, and toId refers to userId of single chat object.
Group chat
Group chat refers to the chat function of internal broadcast mode within the user set with roles and permissions, BMXConversationType is BMXConversationGroup, and toId refers to group Id.
Group management
- Create group
L/S: BMXGroup object reference is passed in and called, after which the group information is retrieved.
//Parameter description: option(group configuration) group(group information)
BMXGroupService.CreateGroupOptions options = new BMXGroupService.CreateGroupOptions(name, desc, publicCheckStatus);
options.setMMembers(members);
BMXGroup group = new BMXGroup();
bmxClient.getGroupService().create(options, group);
H/A: From BMXDataCallBackGet group information in callback.
bmxClient.getGroupManager().create(option, new BMXDataCallBack<BMXGroup>(){
@Override
public void onResult(BMXErrorCode bmxErrorCode, BMXGroup group) {
//Return BMXGroup instance
}
});
- Join group
L/S: Success is judged by the value of returned BMXErrorCode.
//Parameter description: group(group information) message(reason for membership application)
bmxClient.getGroupService().join(group, message);
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getGroupManager().join(group, message, new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode instance
}
});
- Quit group Parameter description: group (group information)
L/S: Success is judged by the value of returned BMXErrorCode.
bmxClient.getGroupService().leave(group);
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getGroupManager().leave(group, new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode instance
}
});
- Dissolve group
L/S: Success is judged by the value of returned BMXErrorCode.
//Parameter description: group (group information)
bmxClient.getGroupService().destroy(group);
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getGroupManager().destroy(group, new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErrorCode instance
}
});
- Get group member list
L/S: BMXGroupMemberList object reference is passed in and called, after which the group member list is retrieved.
//Parameter description: group (group information) forceRefresh (whether to pull from server)
boolean forceRefresh = true;
BMXGroupMemberList memberList = new BMXGroupMemberList();
bmxClient.getGroupService().getMembers(group, memberList, forceRefresh);
H/A: From BMXDataCallBackGet group member list in callback.
bmxClient.getGroupManager().getMembers(group, forceRefresh, new BMXDataCallBack<BMXGroupMemberList>(){
@Override
public void onResult(BMXErrorCode bmxErrorCode, BMXGroupMemberList list) {
//Return BMXGroupMemberList instance
}
});
- Get group list
L/S: BMXGroupList object reference is passed in and called, after which the group list is retrieved.
//Parameter description: forceRefresh (whether to pull from server)
BMXGroupList list = new BMXGroupList();
bmxClient.getGroupService().search(list, true);
H/A: From BMXDataCallBackGet group list in callback.
bmxClient.getGroupManager().getGroupList(true, new BMXDataCallBack<BMXGroupList>(){
@Override
public void onResult(BMXErrorCode bmxErrorCode, BMXGroupList list) {
//Return BMXGroupList instance
}
});
- Get group information
L/S: BMXGroup object reference is passed in and called, after which the group information is retrieved.
//Parameter description: groupId forceRefresh(whether to pull from server)
BMXGroup bmxGroup = new BMXGroup();
bmxClient.getGroupService().search(mGroupId, bmxGroup, true);
H/A: From BMXDataCallBackGet group information in callback.
bmxClient.getGroupManager().getGroupList(mGroupId, true, new BMXDataCallBack<BMXGroup>(){
@Override
public void onResult(BMXErrorCode bmxErrorCode, BMXGroup group) {
//Return BMXGroup instance
}
});
Message sending
You can't chat until you login successfully. When sending messages, single chat and group chat call the same unified interface, but the only difference is to set BMXConversationType.
Message format
Text message
//Parameter description: from(sender id) to(receiver id) type(single/group chat type) text(text-message)
BMXMessage msg = BMXMessage.createMessage(from, to, type, to, text);
Image message
//Parameter description: from(sender id) to(receiver id) type(single/group chat type) w(image width) h(image height) path(local path of image) size(image size)
BMXImageAttachment.Size size = new BMXMessageAttachment.Size(w, h);
BMXImageAttachment imageAttachment = new BMXImageAttachment(path, size);
BMXMessage msg = BMXMessage.createMessage(from, to, type, to, imageAttachment);
File message
Parameter description: from(sender id) to(receiver id) type(singe/group chat type) path(local path of file) name(file name)
BMXFileAttachment fileAttachment = new BMXFileAttachment(path, name);
BMXMessage msg = BMXMessage.createMessage(from, to, type, to, fileAttachment);
Location message
Parameter description: from(sender id) to(receiver id) type(single/group chat type) latitude longitude address
BMXLocationAttachment locationAttachment = new BMXLocationAttachment(latitude, longitude, address);
BMXMessage msg = BMXMessage.createMessage(from, to, type, to, locationAttachment);
Voice message
Parameter description: from(sender id) to(receiver id) type(single/group chat type) path(local path of voice) time(voice length)
BMXVoiceAttachment voiceAttachment = new BMXVoiceAttachment(path, time);
BMXMessage msg = BMXMessage.createMessage(from, to, type, to, voiceAttachment);
Message operation
- Send
L/S:
// Parameter description: BMXMessage(message body)
bmxClient.getChatService().sendMessage(msg);
H/A:
//Message sending status, required to register message receiving listener
bmxClient.getChatManager().sendMessage(msg);
- Forward
L/S:
// Parameter description: BMXMessage(message body)
bmxClient.getChatService().forwardMessage(msg);
H/A:
//Message sending status, required to register message receiving listener
// Parameter description: BMXMessage(message body)
bmxClient.getChatManager().forwardMessage(msg);
- Resend
L/S:
// Parameter description: BMXMessage(message body)
bmxClient.getChatService().resendMessage(msg);
H/A:
//Message sending status, required to register message receiving listener
bmxClient.getChatManager().resendMessage(msg);
- Revoke
L/S:
// Parameter description: BMXMessage(message body)
bmxClient.getChatService().recallMessage(msg);
H/A:
//Message sending status, required to register message receiving listener
bmxClient.getChatManager().recallMessage(msg);
- Download message attachment
Call instruction: pass in the download url in FileCallBack, onProgress for download progress, onFail returned for download failure, and onFinish returned for download successfully with the path.
bmxClient.getChatManager().downloadAttachment(message, new FileCallback(body.url()) {
@Override
protected void onProgress(long percent, String path, boolean isThumbnail) {
}
@Override
protected void onFinish(String url, boolean isThumbnail) {
BMImageLoader.getInstance().display(mImageView, "file://" + body.path(), mImageConfig);
}
@Override
protected void onFail(String path, boolean isThumbnail) {
}
});
Message delivery listening
- Register message callback
private BMXChatServiceListener mChatListener = new BMXChatServiceListener() {
@Override
public void onStatusChanged(BMXMessage msg, BMXErrorCode error) {
//Message status update
}
@Override
public void onAttachmentStatusChanged(BMXMessage msg, BMXErrorCode error, int percent) {
//Attachment status update
}
@Override
public void onRecallStatusChanged(BMXMessage msg, BMXErrorCode error) {
//Revoke status update
}
@Override
public void onReceive(BMXMessageList list) {
//Messages received
}
@Override
public void onReceiveSystemMessages(BMXMessageList list) {
//System notification received
}
@Override
public void onReceiveReadAcks(BMXMessageList list) {
//Read acknowledgement received
}
@Override
public void onReceiveDeliverAcks(BMXMessageList list) {
//Message delivery acknowledgement received
}
@Override
public void onReceiveRecallMessages(BMXMessageList list) {
//Message revocation notification received
}
@Override
public void onAttachmentUploadProgressChanged(BMXMessage msg, int percent) {
//Attachment upload progress update
}
};
Advanced features
Custom messages
BMXMessageObject entity provides extensible attributes (extensionJson and configJson), extensionJson is an extension field for development use, such as edit status; configJson is an extension field for SDK's own use, such as mention and push functions.
Group @ function
@ function is supported in group, for meeting your @ specified user or @every member’s need, Developer shall set the config field in BMXMessage to implement group Owner’s @ function, the later @ actions will be notified by push
// Text function: Add @ object
if (atMap != null && !atMap.isEmpty()) {
MentionBean mentionBean = new MentionBean();
mentionBean.setSenderNickname(senderName);
mentionBean.setPushMessage(pushText);
// Stored information for @ object, including all or partial members
if (atMap.containsKey("-1")) {
// @all
String atTitle = atMap.get("-1");
if (!TextUtils.isEmpty(atTitle) && text.contains(atTitle)) {
// @all if all are included, besides it is necessary to judge whether the text message contains the completed @ name. If not, @ will not be triggered.
mentionBean.setMentionAll(true);
}
} else {
// @partial members, need to traverse added @ information
List<Long> atIds = new ArrayList<>();
mentionBean.setMentionAll(false);
for (Map.Entry<String, String> entry : atMap.entrySet()) {
// Add this only if the sent text includes the name of @object, prevent the @object name in input box being modified
if (entry.getValue() != null && !TextUtils.isEmpty(entry.getValue())
&& text.contains(entry.getValue())) {
// @partial members, only feedId and userId are necessary in feed, so other useless information shall be removed
atIds.add(Long.valueOf(entry.getKey()));
}
}
mentionBean.setMentionList(atIds);
}
msg.setConfig(new Gson().toJson(mentionBean));
}
- Typing message
String INPUT_STATUS = "input_status";
interface InputStatus {
// Empty
String NOTHING_STATUS = "nothing";
// Key up
String TYING_STATUS = "typing";
}
String extension = "";
try {
JSONObject object = new JSONObject();
object.put(INPUT_STATUS,
tag == MessageInputBar.OnInputPanelListener.TAG_OPEN
? InputStatus.TYING_STATUS
: InputStatus.NOTHING_STATUS);
extension = object.toString();
} catch (JSONException e) {
e.printStackTrace();
}
BMXMessage msg = BMXMessage.createMessage(from, to, type, to, "");
if (msg == null) {
return null;
}
msg.setDeliveryQos(BMXMessage.DeliveryQos.AtMostOnce);
msg.setExtension(extension);
Push
Step 1 Push certificates configuration Sign in your Lanying IM console and navigate the "Push" page. Add push certificates of the major push vendors.
Step 2 Intergrate the push SDKs Integrate the SDKs of each push vendors, set permissions in the Android client code and register the push service at the app is started.
Step 3 upload device token Get the device token through the SDK of each push vendor in Android client and upload it to the Lanying IM server (please refer to the following code). When the receiver is not online, the Lanying IM server will automatically send the message through push service.
L/S: Success is judged by the value of returned BMXErrorCode.
//Parameter description: pushToken
bmxClient.getUserService().bindDevice(pushToken);
H/A: Get BMXErrorCode in BMXCallBack to determine success.
bmxClient.getUserManager().bindDevice(pushToken, new BMXCallBack(){
@Override
public void onResult(BMXErrorCode bmxErrorCode) {
//Return BMXErroeCode instance
}
});
The default push titile is the message sender's nickname, and the push content is the message body. If you need to customize the push title and push content, you can set it in the config field of the message. setPushMessageLocKey sets the key name of the text resource in the multilingual environment, and setPushMessageLocArgs sets the value of variable parameters in the text resource in the multilingual environment. The code example is as follows:
BMXMessageConfig con = BMXMessageConfig.createMessageConfig(false);
con.setRTCHangupInfo(callId, peerDrop);
con.setPushMessageLocKey(pushMessageLocKey);
if (pushMessageLocArgs.length() > 0){
con.setPushMessageLocArgs(pushMessageLocArgs);
}
BMXMessage msg = BMXMessage.createRTCMessage(from, to, BMXMessage.MessageType.Single, to, content);
msg.setConfig(con);
handlerMessage(msg);
RTC AV Call
The RTC call function needs to integrate floo-android and floo-rtc-android on the client side.Floo-android provides a signaling channel for AV calls, and floo-rtc-android implements the business logic in RTC calls. Therefore, before implementing AV calls, it is necessary to integrate floo-android, and implement the features of login and messaging.
Floo-rtc-android supports one-to-one AV calls. Download link:https://github.com/maxim-top/floo-rtc-android/releases。
- Download the aar file to the libs directory in your project.
- Add a dependency to the dependencies block in the build.gradle, refer tolanying-im-android. In the latest version of gradle, you only need to declare the dependency on the libs directory, and no longer need to declare the dependency on a specific file in it separately.
Create the user interface
Import dependencies of video view
import top.maxim.rtc.view.BMXRtcRenderView; import top.maxim.rtc.view.RTCRenderView;
Create two containers of the video views (in this example, the large container is full screen, and the small container is on the upper right) ```
3. Add the local video view to the small container
ViewGroup smallViewGroup = mVideoContainer.findViewById(R.id.video_view_container_small);
//The remote view is null before the call is established,the local video view displays in full screen.
if (mRemoteView == null){
ViewGroup.LayoutParams layoutParams = smallViewGroup.getLayoutParams();
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
smallViewGroup.setLayoutParams(layoutParams);
}
smallViewGroup.setVisibility(View.VISIBLE);
mLocalView = new RTCRenderView(this);
mLocalView.init();
mLocalView.setScalingType(BMXRtcRenderView.ScalingType.SCALE_ASPECT_FILL);
mLocalView.getSurfaceView().setZOrderMediaOverlay(true);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_VERTICAL);
smallViewGroup.addView(mLocalView, layoutParams);
4. Add the remote video view to the large container
//Restore the full screen local view to a small size
if (mLocalView != null){
ViewGroup smallViewGroup = mVideoContainer.findViewById(R.id.video_view_container_small);
ViewGroup.LayoutParams layoutParams = smallViewGroup.getLayoutParams();
layoutParams.width = getPixelsFromDp(120);
layoutParams.height = getPixelsFromDp(212);
smallViewGroup.setLayoutParams(layoutParams);
mLocalView.setScalingType(BMXRtcRenderView.ScalingType.SCALE_ASPECT_FILL);
}
ViewGroup largeViewGroup = mVideoContainer.findViewById(R.id.video_view_container_large);
largeViewGroup.setVisibility(View.VISIBLE);
mRemoteView = new RTCRenderView(this);
mRemoteView.init();
mRemoteView.setScalingType(BMXRtcRenderView.ScalingType.SCALE_ASPECT_FILL);
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
largeViewGroup.addView(mRemoteView, layoutParams);
### Business logic of an AV call
1. Import RTCManager
import top.maxim.rtc.RTCManager;
2. Add a RTC engine listener
mEngine = RTCManager.getInstance().getRTCEngine(); mEngine.addRTCEngineListener(mListener = new BMXRTCEngineListener() {
@Override
public void onJoinRoom(String info, long roomId, BMXErrorCode error) {
super.onJoinRoom(info, roomId, error);
mRoomId = roomId;
if (BaseManager.bmxFinish(error)) {
mEngine.publish(BMXVideoMediaType.Camera, mHasVideo, true);
Log.e(TAG, "Join the room successfully and start publishing local streams, roomId= " + roomId + "msg = " + info);
} else {
Log.e(TAG, "Failed to join the room, roomId= " + roomId + "msg = " + info);
}
}
@Override
public void onLeaveRoom(String info, long roomId, BMXErrorCode error, String reason) {
super.onLeaveRoom(info, roomId, error, reason);
if (BaseManager.bmxFinish(error)) {
Log.e(TAG, "Leave the room successfully, roomId= " + roomId + "msg = " + reason);
}else{
Log.e(TAG, "Failed to leave the room, roomId= " + roomId + "msg = " + reason);
}
}
@Override
public void onMemberJoined(long roomId, long usedId) {
super.onMemberJoined(roomId, usedId);
Log.e(TAG, "Member joined, uid= " + usedId);
}
@Override
public void onMemberExited(long roomId, long usedId, String reason) {
super.onMemberExited(roomId, usedId, reason);
Log.e(TAG, "Member leave uid= " + usedId);
//Close the call page
onRemoteLeave();
}
@Override
public void onLocalPublish(BMXStream stream, String info, BMXErrorCode error) {
super.onLocalPublish(stream, info, error);
if (BaseManager.bmxFinish(error)) {
//Open local video view and send a RTC call message
onUserJoin(stream);
Log.e(TAG, "Publish local stream successfully and start to preview, msg = " + info);
}else{
Log.e(TAG, "Failed to publish local stream, msg = " + info);
}
}
@Override
public void onLocalUnPublish(BMXStream stream, String info, BMXErrorCode error) {
super.onLocalUnPublish(stream, info, error);
if (BaseManager.bmxFinish(error)) {
Log.e(TAG, "Unpublish local stream successfully, msg = " + info);
}else{
Log.e(TAG, "Failed to unpublish local stream, msg = " + info);
}
}
@Override
public void onRemotePublish(BMXStream stream, String info, BMXErrorCode error) {
super.onRemotePublish(stream, info, error);
if (BaseManager.bmxFinish(error)) {
mEngine.subscribe(stream);
Log.e(TAG, "The remote stream published successfully and start to subscribe it");
}else{
Log.e(TAG, "The remote stream published failure, msg = " + info);
}
}
@Override
public void onRemoteUnPublish(BMXStream stream, String info, BMXErrorCode error) {
super.onRemoteUnPublish(stream, info, error);
if (BaseManager.bmxFinish(error)) {
Log.e(TAG, "The remote stream unpublished");
BMXVideoCanvas canvas = new BMXVideoCanvas();
canvas.setMStream(stream);
mEngine.stopRemoteView(canvas);
mEngine.unSubscribe(stream);
//Close the call page
onRemoteLeave();
}else{
Log.e(TAG, "The remote stream unpublished failure, msg = " + info);
}
}
@Override
public void onSubscribe(BMXStream stream, String info, BMXErrorCode error) {
super.onSubscribe(stream, info, error);
if (BaseManager.bmxFinish(error)) {
onRemoteJoin(stream);
mPickupTimestamp = getTimeStamp();
Log.e(TAG, "Subscribe the remote stream successfully, msg = " + info);
} else {
Log.e(TAG, "Failed to subscribe the remote stream, msg = " + info);
}
}
@Override
public void onUnSubscribe(BMXStream stream, String info, BMXErrorCode error) {
super.onUnSubscribe(stream, info, error);
if (BaseManager.bmxFinish(error)) {
Log.e(TAG, "Unsubscribe the remote stream successfully, msg = " + info);
} else {
Log.e(TAG, "Failed to unsubscribe the remote stream, msg = " + info);
}
}
});
3. Join a room(Make a call)
//Set the video resolution
BMXVideoConfig config = new BMXVideoConfig();
config.setProfile(EngineConfig.VIDEO_PROFILE);
mEngine.setVideoProfile(config);
BMXRoomAuth auth = new BMXRoomAuth();
auth.setMUserId(mUserId);
// The caller does not need to set the roomId, it will be returned when
// the room is successfully created; the callee needs to set the same
// roomId as the caller
auth.setMRoomId(mRoomId);
auth.setMToken(mPin);//The room password
mEngine.joinRoom(auth);
4. Hangup a call
public void onCallHangup(View view){
//Send a RTC hangup message
sendRTCHangupMessage();
leaveRoom();
finish();
}
5. Close the call page
private void onRemoteLeave() {
removeRemoteView();
mHandler.removeAll();
finish();
}
6. Open the local video view, and send a RTC call message
private void onUserJoin(BMXStream info){
if(info == null){
return;
}
if (mHasVideo) {
runOnUiThread(() -> {
addLocalView();
BMXVideoCanvas canvas = new BMXVideoCanvas();
canvas.setMView(mLocalView.getObtainView());
canvas.setMStream(info);
mEngine.startPreview(canvas);
});
} else {
}
if (mIsInitiator) {
//send a RTC call message
sendRTCCallMessage();
}
}
7. Open the remote video view
private void onRemoteJoin(BMXStream info) {
if(info == null){
return;
}
runOnUiThread(() -> {
if (mHasVideo) {
addRemoteView();
BMXVideoCanvas canvas = new BMXVideoCanvas();
canvas.setMView(mRemoteView.getObtainView());
canvas.setMUserId(info.getMUserId());
canvas.setMStream(info);
mEngine.startRemoteView(canvas);
}
});
}
8. Release the resources
@Override
protected void onDestroy() {
super.onDestroy();
if (mEngine != null) {
mEngine.removeRTCEngineListener(mListener);
mListener = null;
}
RTCManager.getInstance().removeRTCServiceListener(mRTCListener);
mRTCListener = null;
}
### Implement AV call signaling with RTC service
1. Add a listener
private BMXRTCServiceListener mRTCListener = new BMXRTCServiceListener(){
public void onRTCCallMessageReceive(BMXMessage msg) {
new Thread(new Runnable() {
@Override
public void run() {
try {
// Delay handling to determine whether a hangup message for the corresponding call will be received later,
// so as to avoid popping up the ended call
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
String callId = msg.config().getRTCCallId();
if (mHungupCalls.contains(callId)){
mHungupCalls.remove(callId);
ackMessage(msg);
return;
}
long roomId = msg.config().getRTCRoomId();
long chatId = msg.config().getRTCInitiator();
long myId = SharePreferenceUtils.getInstance().getUserId();
if (myId == chatId){
return;
}
//If already in a call, send a busy message to the new caller
if (RTCManager.getInstance().getRTCEngine().isOnCall){
replyBusy(callId, myId, chatId);
return;
}
String pin = msg.config().getRTCPin();
if(mActivityRef != null && mActivityRef.get() != null){
Context context = mActivityRef.get();
if (msg.type() == BMXMessage.MessageType.Single) {
//Open the call page(incoming call ringing)
SingleVideoCallActivity.openVideoCall(context, chatId, roomId, callId,
false, msg.config().getRTCCallType(), pin, msg.msgId());
}
}
}
}, "onRTCCallMessageReceive").start();
}
public void onRTCPickupMessageReceive(BMXMessage msg) {
if (msg.config().getRTCCallId().equals(mCallId) && msg.fromId() == mUserId){
leaveRoom();
ackMessage(msg);
}
}
public void onRTCHangupMessageReceive(BMXMessage msg) {
long otherId = mEngine.otherId;
if (msg.config().getRTCCallId().equals(mCallId) &&
(msg.fromId()==otherId
|| msg.content().equals("busy")
|| msg.content().equals("rejected")
|| msg.content().equals("canceled")
|| msg.content().equals("timeout")
|| !mEngine.isOnCall)){
leaveRoom();
ackMessage(msg);
}
}
};
RTCManager.getInstance().addRTCServiceListener(mRTCListener);
2. Send a RTC call message
public String sendRTCCallMessage(BMXMessageConfig.RTCCallType type, long roomId, long from, long to, String pin) { BMXMessageConfig con = BMXMessageConfig.createMessageConfig(false); con.setRTCCallInfo(type, roomId, from, BMXMessageConfig.RTCRoomType.Broadcast, pin); con.setIOSConfig("{\"mutable_content\":true,\"loc-key\":\"call_in\"}"); BMXMessage msg = BMXMessage.createRTCMessage(from, to, BMXMessage.MessageType.Single, to, ""); msg.setConfig(con); msg.setExtension("{\"rtc\":\"call\"}"); handlerMessage(msg); return con.getRTCCallId(); }
3. Send a RTC pickup message
public void sendRTCPickupMessage(long from, long to, String callId) { BMXMessageConfig con = BMXMessageConfig.createMessageConfig(false); con.setRTCPickupInfo(callId); BMXMessage msg = BMXMessage.createRTCMessage(from, to, BMXMessage.MessageType.Single, to, ""); msg.setConfig(con); handlerMessage(msg); }
4. Send a RTC hangup message
public void sendRTCHangupMessage(long from, long to, String callId, String content, String iosConfig) { BMXMessageConfig con = BMXMessageConfig.createMessageConfig(false); con.setRTCHangupInfo(callId); BMXMessage msg = BMXMessage.createRTCMessage(from, to, BMXMessage.MessageType.Single, to, content); msg.setConfig(con); handlerMessage(msg); } ```