Skip to main content
Android
iOS
Web
Windows
Unity
Flutter
React Native

Log in from multiple devices

Agora Chat supports logging in to the same account from up to 4 devices at the same time by default. You can increase the number of devices per platform up to 10 by going to Agora Console > Project management > Relevant project > Agora Chat configuration > Features > Multi Device.

In cases of multiple device login, all logged-in devices synchronize the following information and operations:

  • Messages, including online messages, offline messages, push notifications (if the third-party push service is enabled, the offline device receives it), the corresponding receipts and read status, and so on;
  • Friends and group-related operations;
  • Message-thread-related operations;
  • Conversation-related operations.

The following table summarizes the strategy of forcing other devices to log out and the automatic login security check in the single-device and multi-device login use-cases:

Single/multi-device login Forced logout strategy Automatic login security check
Single-device login The newly logged-in device will force the current device to log out. The logged-out device will receive the ConnectionListener#onLogout callback. For devices that log in automatically, the device will automatically reconnect to the Agora server after going offline. If the reconnection is successful, the current logged-in device will be forced to log out by default (for multiple devices, the earliest logged-in device will be logged out). If you want to keep the current device logged in, contact support@agora.io. In this use-case, the device that logs in automatically won't be able to log in and will receive error 214, indicating that the number of currently logged-in devices exceeds the limit.
Multi-device login If the number of logged-in devices on one end reaches the limit, the device that logged in the latest will force the device that logged in the earliest to log out. The logged-out device will receive the ConnectionListener#onLogout callback. Agora Chat only supports forced logout for the devices on the same end.

Understand the tech

During initialization, the SDK generates a login ID to identify the device when multiple devices log in and push messages, and sends this ID to the server. The server automatically sends new messages to the user's logged-in device, monitors operations on other devices, and synchronizes multiple devices. The SDK provides the following multi-device use-case functions:

  • Get the login ID list of the other logged-in devices of the current user;

  • Get the list of online logged-in devices of the specified account;

  • Force the specified account to log out from a single device;

  • Force the specified account to log out from all devices;

  • Get friend or group operations on other devices.

Prerequisites

Before starting this procedure, initialize and connect the SDK to the server. See SDK quickstart for details.

Implement multi-device login

Get the login ID list of other logged-in devices of the current user

Call getSelfIdsOnOtherPlatform to get the login ID list of other logged-in devices, and then select the target login ID as the message recipient to send a message to the specified device.


_8
// A synchronous method that will block the current thread. The asynchronous method is asyncGetSelfIdsOnOtherPlatform(ValueCallBack).
_8
List<String> ids = ChatClient.getInstance().contactManager().getSelfIdsOnOtherPlatform();
_8
// Select a login ID as the message recipient.
_8
String toChatUserId = ids.get(0);
_8
// Create a text message, content is the message text, toChatUserId passes in the login ID as the message recipient.
_8
ChatMessage message = ChatMessage.createTextSendMessage(content, toChatUserId);
_8
// Send a message.
_8
ChatClient.getInstance().chatManager().sendMessage(message);

Get the list of online logged-in devices for the specified account

Call getLoggedInDevicesFromServerWithToken to get the list of online logged-in devices for the specified account from the server by passing in the user ID and user token.


_5
try {
_5
List<EMDeviceInfo> deviceInfos = ChatClient.getInstance().getLoggedInDevicesFromServerWithToken(userId, token);
_5
} catch (HyphenateException e) {
_5
e.printStackTrace();
_5
}

Force the specified account to log out from a single device

Call kickDeviceWithToken and pass in the user ID and user token to force the specified account to log out from a single logged-in device. Before calling this method, obtain the device ID through the ChatClient#getLoggedInDevicesFromServerWithToken and DeviceInfo#getResource methods. The logged-out device will receive the ConnectionListener#onLogout event.

Note
You can also use this interface without logging in.

_4
// userId: User ID, token: User token. Need to be executed in an asynchronous thread.
_4
List<DeviceInfo> deviceInfos = ChatClient.getInstance().getLoggedInDevicesFromServerWithToken(userId, token);
_4
// userId: User ID, token: User token, resource: Device ID. Need to be executed in an asynchronous thread.
_4
ChatClient.getInstance().kickDeviceWithToken(userId, token, deviceInfos.get(selectedIndex).getResource());

Force the specified account to log out from all devices

Call kickAllDevicesWithToken and pass in the user ID and user token to force the specified account to log out from from all logged-in devices. The logged-out device will receive the ConnectionListener#onLogout event.

Note
You can also use this interface without logging in.

_5
try {
_5
ChatClient.getInstance().kickAllDevicesWithToken(userId,token);
_5
} catch (HyphenateException e) {
_5
e.printStackTrace();
_5
}

Get operations on other devices

Let's say that account A logs in on devices A and B at the same time and performs operations on device A. Device B will receive notifications corresponding to these operations.

You need to implement the MultiDeviceListener class to listen to operations on other devices, and then call the addMultiDeviceListener method to add multi-device listening.


_164
//Implement `MultiDeviceListener` to listen to operations on other devices.
_164
private class ChatMultiDeviceListener implements MultiDeviceListener {
_164
//@param event event.
_164
@Override
_164
//@param target friend's user ID; @param ext event extended information.
_164
public void onContactEvent(int event, String target, String ext) {
_164
EMLog.i(TAG, "onContactEvent event"+event);
_164
String message = null;
_164
switch (event) {
_164
//The current user deletes friends on other devices.
_164
case CONTACT_REMOVE:
_164
break;
_164
//The current user accepts friend requests on other devices.
_164
case CONTACT_ACCEPT:
_164
break;
_164
//The current user rejects friend requests on other devices.
_164
case CONTACT_DECLINE:
_164
break;
_164
//The current user adds a user to the blocklist on other devices.
_164
case CONTACT_BAN:
_164
break;
_164
//The current user removes a user from the blocklist on other devices.
_164
case CONTACT_ALLOW:
_164
break;
_164
default:
_164
break;
_164
}
_164
}
_164
_164
@Override
_164
public void onGroupEvent(int event, String groupId, List<String> userIds) {
_164
EMLog.i(TAG, "onGroupEvent event"+event);
_164
switch (event) {
_164
//The current user created a group on another device.
_164
case GROUP_CREATE:
_164
break;
_164
//The current user destroyed a group on another device.
_164
case GROUP_DESTROY:
_164
break;
_164
//The current user joined a group on another device.
_164
case GROUP_JOIN:
_164
break;
_164
//The current user left a group on another device.
_164
case GROUP_LEAVE:
_164
break;
_164
//The current user initiated a group application on another device.
_164
case GROUP_APPLY:
_164
break;
_164
//The current user approved the group application on another device.
_164
case GROUP_APPLY_ACCEPT:
_164
break;
_164
//The current user rejected the group application on another device.
_164
case GROUP_APPLY_DECLINE:
_164
break;
_164
//The current user invited group members on other devices.
_164
case GROUP_INVITE:
_164
break;
_164
//The current user accepted the invitation to join the group on other devices.
_164
case GROUP_INVITE_ACCEPT:
_164
break;
_164
//The current user rejected the invitation to join the group on other devices.
_164
case GROUP_INVITE_DECLINE:
_164
break;
_164
//The current user kicked members out of the group on other devices.
_164
case GROUP_KICK:
_164
break;
_164
//The current user added members to the group blocklist on other devices.
_164
case GROUP_BAN:
_164
break;
_164
//The current user removed members from the group blocklist on other devices.
_164
case GROUP_ALLOW:
_164
break;
_164
//The current user blocked the group on other devices.
_164
case GROUP_BLOCK:
_164
break;
_164
//The current user unblocked the group on other devices.
_164
case GROUP_UNBLOCK:
_164
break;
_164
//The current user transferred group ownership on other devices.
_164
case GROUP_ASSIGN_OWNER:
_164
break;
_164
//The current user adds an administrator on other devices.
_164
case GROUP_ADD_ADMIN:
_164
break;
_164
//The current user removes the administrator on other devices.
_164
case GROUP_REMOVE_ADMIN:
_164
break;
_164
//The current user mutes users on other devices.
_164
case GROUP_ADD_MUTE:
_164
break;
_164
//The current user removes the muting on other devices.
_164
case GROUP_REMOVE_MUTE:
_164
break;
_164
//The current user sets custom attributes for group members on other devices.
_164
case GROUP_METADATA_CHANGED:
_164
break;
_164
default:
_164
break;
_164
}
_164
}
_164
_164
@Override
_164
public void onChatThreadEvent(int event, String target, List<String> userIds) {
_164
EMLog.i(TAG, "onChatThreadEvent event"+event);
_164
switch (event) {
_164
case THREAD_CREATE:
_164
//The current user creates a message thread on other devices.
_164
break;
_164
case THREAD_DESTROY:
_164
//The current user destroys the message thread on other devices.
_164
break;
_164
case THREAD_JOIN:
_164
//The current user joins the message thread on other devices.
_164
break;
_164
case THREAD_LEAVE:
_164
//The current user leaves the message thread on other devices.
_164
break;
_164
case THREAD_UPDATE:
_164
//The current user updates the message thread on other devices.
_164
break;
_164
case THREAD_KICK:
_164
//The current user kicks a member out of the message thread on other devices.
_164
break;
_164
default:
_164
break;
_164
_164
}
_164
}
_164
_164
@Override
_164
public void onConversationEvent(int event, String conversationId, Conversation.ConversationType type) {
_164
EMLog.i(TAG, "onConversationEvent event"+event);
_164
switch (event) {
_164
case CONVERSATION_PINNED:
_164
//The current user pins a conversation on other devices.
_164
break;
_164
case CONVERSATION_UNPINNED:
_164
//The current user unpins a conversation on other devices.
_164
break;
_164
case CONVERSATION_DELETED:
_164
//The current user deletes a conversation on other devices.
_164
break;
_164
case CONVERSATION_MARK_UPDATE:
_164
//The current user updates the conversation tag on other devices, including adding and removing conversation tags.
_164
break;
_164
default:
_164
break;
_164
}
_164
}
_164
_164
@Override
_164
public void onMessageRemoved(String conversationId, String deviceId) {
_164
EMLog.i(TAG, "onMessageRemoved conversationId "+conversationId);
_164
//The current user unidirectionally deletes message history of a conversation on other devices from the server.
_164
}
_164
}
_164
_164
ChatMultiDeviceListener chatMultiDeviceListener = new ChatMultiDeviceListener();
_164
_164
//Add a multi-device listener.
_164
ChatClient.getInstance().addMultiDeviceListener(chatMultiDeviceListener);
_164
_164
//Remove a multi-device listener.
_164
ChatClient.getInstance().removeMultiDeviceListener(chatMultiDeviceListener);

vundefined