Last Updated on : 2025-11-20 10:11:34download
The multi-device login feature allows users to view which devices their current account is logged into and remotely log out of the specified devices. This topic describes how to use the multi-device login interfaces provided by the SDK.
The multi-device login feature includes the following core interfaces:
getUserLoginMultiTerminal: Gets the list of devices where the current account is logged in.getMultiTerminalLogoutCode: Gets the device logout verification code (loginOutCode).logoutMultiTerminal: Logs out a specified device.registerTerminalLogoutListener: Registers a listener for account logout events (receives notifications when the account is logged out from another device).void getUserLoginMultiTerminal(IUserBusinessCallback<UserLoginMultiterminalVO> callback)
Get login information for the current account across all devices, including device ID, operating system, platform, login status, and login time.
| Parameter | Type | Description |
|---|---|---|
| callback | IUserBusinessCallback |
The callback interface that returns the list of logged-in devices. |
IUserBusinessCallback<UserLoginMultiterminalVO>
public interface IUserBusinessCallback<ResponseValue> {
/**
* Success callback
* @param success Returns a UserLoginMultiterminalVO object
*/
void onSuccess(ResponseValue success);
/**
* Failure callback
* @param code The error code
* @param error The error message
*/
void onError(String code, String error);
}
UserLoginMultiterminalVO
public class UserLoginMultiterminalVO {
private List<UserLoginMultiterminalBean> terminalLoginList;
public List<UserLoginMultiterminalBean> getTerminalLoginList() {
return terminalLoginList;
}
}
UserLoginMultiterminalBean
public class UserLoginMultiterminalBean {
private String terminalId; // The unique device identifier
private String os; // The operating system
private String osSystem; // The version of the operating system
private String platform; // The platform information
private Integer status; // The session status. 0 - invalid, 1 - valid
private Long loginTime; // The login timestamp
// Getter and Setter methods
public String getTerminalId() { return terminalId; }
public String getOs() { return os; }
public String getOsSystem() { return osSystem; }
public String getPlatform() { return platform; }
public Integer getStatus() { return status; }
public Long getLoginTime() { return loginTime; }
}
ThingHomeSdk.getUserInstance().getUserLoginMultiTerminal(object : IUserBusinessCallback<UserLoginMultiterminalVO> {
override fun onSuccess(result: UserLoginMultiterminalVO) {
val deviceList = result.terminalLoginList
if (!deviceList.isNullOrEmpty()) {
for (device in deviceList) {
Log.d("MultiTerminal", "Device ID: ${device.terminalId}")
Log.d("MultiTerminal", "Operating system: ${device.os}")
Log.d("MultiTerminal", "Platform: ${device.platform}")
Log.d("MultiTerminal", "Status: ${if (device.status == 1) "Valid" else "Invalid"}")
Log.d("MultiTerminal", "Login time: ${Date(device.loginTime)}")
}
}
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to get device list: $code - $error")
}
})
void getMultiTerminalLogoutCode(String countryCode, String username, String password,
String code, Boolean ifencrypt,
Integer type, Integer verifyType,
IUserBusinessCallback<MultiTerminalBean> callback)
Before performing a device logout operation, account verification is required. After successful verification, a logout verification code (loginOutCode) is obtained. This code is used for the subsequent logout operation.
For verifications through password and verification code, you only need to call this method directly. There is no need to call other interfaces beforehand.
| Parameter | Type | Required | Description |
|---|---|---|---|
| countryCode | String | Yes | The country code, for example, “86”. |
| username | String | Yes | The username. It can be a mobile phone number or email address. |
| password | String | Conditionally | The password. This parameter is required when verifyType is 1, and can be empty when verifyType is 2. |
| code | String | Conditionally | The verification code. This parameter is required when verifyType is 2, and can be empty when verifyType is 1. |
| ifencrypt | Boolean | Yes | Indicates whether the password is encrypted. This parameter must be true when verifyType is 1, and can be false when verifyType is 2. |
| type | Integer | Yes | The account type. 1: Mobile phone number, 2: email address. |
| verifyType | Integer | Yes | The verification method. 1: Password, 2: verification code. |
| callback | IUserBusinessCallback |
Yes | Callback interface |
Method 1: Password verification (verifyType is 1)
username and password: Required.ifencrypt: Must be true.code: Pass null.Call this method directly to complete verification and obtain the loginOutCode.
Method 2: Verification code (verifyType is 2)
username and code: Required.password: Can be empty (Pass null).ifencrypt: Pass false.When verifying through verification code, you must first call sendVerifyCodeWithUserName (with type of 8) to send a verification code. After receiving the code, call this method.
MultiTerminalBean
public class MultiTerminalBean {
private String loginOutCode; // Logout verification code
private long expireTime; // Expiration time of the verification code (timestamp in seconds)
public String getLoginOutCode() {
return loginOutCode;
}
public long getExpireTime() {
return expireTime;
}
}
Example 1: Verify through password
val countryCode = "86"
val username = "13800138000" // Mobile phone number or email address
val password = "your_password"
val type = 1 // 1 - 1 - Mobile phone number, 2 - email address
val verifyType = 1 // 1 - Verify through password
ThingHomeSdk.getUserInstance().getMultiTerminalLogoutCode(
countryCode,
username,
password,
null, // code
true, // ifencrypt must be true
type,
verifyType,
object : IUserBusinessCallback<MultiTerminalBean> {
override fun onSuccess(result: MultiTerminalBean) {
val loginOutCode = result.loginOutCode
val expireTime = result.expireTime
Log.d("MultiTerminal", "Logout verification code: $loginOutCode")
Log.d("MultiTerminal", "Expiration time: ${Date(expireTime * 1000)}")
// Save loginOutCode for subsequent logout operations
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to get logout verification code: $code - $error")
}
}
)
Example 2: Verify through verification code
// Step 1: Send verification code
val countryCode = "86"
val username = "13800138000"
val type = 1 // 1 - Mobile phone number, 2 - email address
ThingHomeSdk.getUserInstance().sendVerifyCodeWithUserName(
username,
null, // region
countryCode,
8, // type = 8, indicates verification code for account logout
object : IResultCallback {
override fun onSuccess() {
// Verification code is sent successfully. Prompt the user to enter the code
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to send verification code: $code - $error")
}
}
)
// Step 2: After the user enters the verification code, obtain logout verification code
val verifyCode = "123456" // Verification code entered by the user
val verifyType = 2 // 2 - Verify through verification code
ThingHomeSdk.getUserInstance().getMultiTerminalLogoutCode(
countryCode,
username,
null, // password can be omitted for verification code authentication
verifyCode, // code - verification code
false, // ifencrypt
type,
verifyType,
object : IUserBusinessCallback<MultiTerminalBean> {
override fun onSuccess(result: MultiTerminalBean) {
val loginOutCode = result.loginOutCode
// Save loginOutCode for subsequent logout operations
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to get logout verification code: $code - $error")
}
}
)
void logoutMultiTerminal(String terminalId, String loginOutCode, IBooleanCallback callback)
Log out of the specified device remotely using the device ID and the logout verification code. After successful logout, the device will no longer be able to use the current account.
| Parameter | Type | Required | Description |
|---|---|---|---|
| terminalId | String | Yes | The unique device identifier obtained from getUserLoginMultiTerminal. |
| loginOutCode | String | Yes | The logout verification code obtained from getMultiTerminalLogoutCode. |
| callback | IBooleanCallback | Yes | The callback interface. |
IBooleanCallback
public interface IBooleanCallback {
/**
* Success callback
*/
void onSuccess();
/**
* Failure callback
* @param code The error code
* @param error The error message
*/
void onError(String code, String error);
}
// terminalId obtained from getUserLoginMultiTerminal
val terminalId = "device_terminal_id_12345"
// loginOutCode obtained from getMultiTerminalLogoutCode
val loginOutCode = "logout_code_abc123"
ThingHomeSdk.getUserInstance().logoutMultiTerminal(terminalId, loginOutCode, object : IBooleanCallback {
override fun onSuccess() {
Log.d("MultiTerminal", "Device logged out successfully")
// Refresh the device list to confirm the device has been logged out
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Device logout failed: $code - $error")
}
})
When an account is logged out from another device, the current device can receive a notification via this listener. This effectively meets the need to promptly respond to user logout operations on other devices, such as clearing local cache and redirecting to the login page.
Please note that this feature depends on the thingsmart-businessapiextensionkit module.
fun registerTerminalLogoutListener(callback: INotificationCallback)
fun unregisterTerminalLogoutListener()
INotificationCallback
interface INotificationCallback {
fun onSuccess()
}
import com.thingclips.businessapiextensionkit.ThingSmartExtNotificationFunc
import com.thingclips.businessapiextensionkit.callback.INotificationCallback
// Register a listener
ThingSmartExtNotificationFunc.registerTerminalLogoutListener(object : INotificationCallback {
override fun onSuccess() {
// Account was logged out from another device
Log.d("MultiTerminal", "Account logged out from another device")
// Execute cleanup operations, for example:
// 1. Clear local user data
// 2. Redirect to login page
// 3. Clear cache
handleTerminalLogout()
}
})
// Unregister the listener (Call this interface when the register is no longer needed, such as during activity destruction)
ThingSmartExtNotificationFunc.unregisterTerminalLogoutListener()
class MainActivity : AppCompatActivity() {
private var terminalLogoutCallback: INotificationCallback? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Ensure initialization is completed (required if user just logged in)
if (!ThingSmartExtNotificationFunc.isInitialized()) {
ThingSmartExtNotificationFunc.initialize()
}
// Register multi-device logout listener
terminalLogoutCallback = object : INotificationCallback {
override fun onSuccess() {
runOnUiThread {
// Account was logged out from another device, perform cleanup
Log.d("MultiTerminal", "Account logged out from another device")
// Clear user data
ThingHomeSdk.getUserInstance().removeUser()
// Redirect to login page
val intent = Intent(this@MainActivity, LoginActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
startActivity(intent)
finish()
}
}
}
ThingSmartExtNotificationFunc.registerTerminalLogoutListener(terminalLogoutCallback!!)
}
override fun onDestroy() {
super.onDestroy()
// Unregister the listener to prevent memory leaks
terminalLogoutCallback?.let {
ThingSmartExtNotificationFunc.unregisterTerminalLogoutListener()
}
}
}
When a user logs out, the resources of ThingSmartExtNotificationFunc are automatically destroyed. Therefore, after a user successfully logs in, the initialize() method must be called again to re-initialize before registering the listener.
Initialization example
// Call after the user logs in successfully
ThingSmartExtNotificationFunc.initialize()
// After initialization completes, register the listener
ThingSmartExtNotificationFunc.registerTerminalLogoutListener(object : INotificationCallback {
override fun onSuccess() {
// Handle account logout logic
}
})
Complete example: Initialization after successful login
// In login success callback
fun onLoginSuccess() {
// 1. Initialize multi-device logout monitoring functionality
ThingSmartExtNotificationFunc.initialize()
// 2. Register a listener
ThingSmartExtNotificationFunc.registerTerminalLogoutListener(object : INotificationCallback {
override fun onSuccess() {
// Account was logged out from another device
handleTerminalLogout()
}
})
}
Resource destroy mechanism: When the current account logs out, the resources of ThingSmartExtNotificationFunc are automatically destroyed (the destroy() method is called), and isInitialized() will return false.
Re-initialization after login: Since resources are destroyed during logout, you must call the initialize() method again to re-initialize after the user successfully logs back in, before registering the listener. Otherwise, the listener will not function correctly.
Listener registration timing: It is recommended to call initialize() immediately after successful user login, then register the listener to ensure timely receipt of logout notifications.
Listener unregistration: When the listener is no longer needed (for example, during activity destruction, or user logout), be sure to call unregisterTerminalLogoutListener() to unregister it and prevent memory leaks.
Thread safety: The onSuccess() callback might not execute on the main thread. If you need to update the UI within the callback, use runOnUiThread() or a handler to switch to the main thread.
class MultiTerminalManager {
private val userManager: IThingUser = ThingHomeSdk.getUserInstance()
/**
* Step 1: Get logged-in device list
*/
fun getLoginDevices() {
userManager.getUserLoginMultiTerminal(object : IUserBusinessCallback<UserLoginMultiterminalVO> {
override fun onSuccess(result: UserLoginMultiterminalVO) {
val deviceList = result.terminalLoginList
// Show the device list to the user
displayDeviceList(deviceList)
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to get device list: $code - $error")
}
})
}
/**
* Step 2: User selects device to log out, performs account verification, and gets a logout code
* Use password verification method
*/
fun prepareLogoutDeviceWithPassword(
terminalId: String,
countryCode: String,
username: String,
password: String,
type: Int
) {
userManager.getMultiTerminalLogoutCode(
countryCode,
username,
password,
null, // code
true, // ifencrypt must be true for password verification
type,
1, // Use password verification
object : IUserBusinessCallback<MultiTerminalBean> {
override fun onSuccess(result: MultiTerminalBean) {
val loginOutCode = result.loginOutCode
// Step 3: Execute logout operation
logoutDevice(terminalId, loginOutCode)
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to get logout verification code: $code - $error")
}
}
)
}
/**
* Step 2: User selects device to log out, performs account verification, and gets a logout code
* Use verification code method
*/
fun prepareLogoutDeviceWithCode(
terminalId: String,
countryCode: String,
username: String,
verifyCode: String,
type: Int
) {
userManager.getMultiTerminalLogoutCode(
countryCode,
username,
null, // password can be omitted for verification code authentication
verifyCode, // code
false, // ifencrypt
type,
2, // Use verification code authentication
object : IUserBusinessCallback<MultiTerminalBean> {
override fun onSuccess(result: MultiTerminalBean) {
val loginOutCode = result.loginOutCode
// Step 3: Execute logout operation
logoutDevice(terminalId, loginOutCode)
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Failed to get logout verification code: $code - $error")
}
}
)
}
/**
* Step 3: Log out of the specified device
*/
private fun logoutDevice(terminalId: String, loginOutCode: String) {
userManager.logoutMultiTerminal(terminalId, loginOutCode, object : IBooleanCallback {
override fun onSuccess() {
Log.d("MultiTerminal", "Device logged out successfully")
// Refresh the device list
getLoginDevices()
}
override fun onError(code: String, error: String) {
Log.e("MultiTerminal", "Device logout failed: $code - $error")
}
})
}
private fun displayDeviceList(deviceList: List<UserLoginMultiterminalBean>?) {
// Show the device list on UI
}
}
Before calling these interfaces, ensure the user is logged in. That is, ThingHomeSdk.getUserInstance().isLogin() returns true.
The loginOutCode is time-sensitive. The expiration time can be obtained from the expireTime returned by getMultiTerminalLogoutCode. It is recommended to use the code promptly after retrieval.
The terminalId is a unique identifier for a device, used to distinguish between different logged-in devices. The same account will have different terminalId values when logged in on different devices.
verifyType is 1):
username and password: Required.ifencrypt: Must be true.code: Pass null.getMultiTerminalLogoutCode directly. There is no need to call other interfaces first.verifyType is 2):
username and code: Required.password: Can be empty (Pass null).ifencrypt: Pass false.sendVerifyCodeWithUserName (with type of 8) to send a verification code. After receiving the code, call this method.All interfaces provide error callbacks. It is recommended to show user-friendly error messages in the UI.
Compare the terminalId with the current device’s identifier to determine if it is the currently active device, thus preventing accidental logout of the device in use.
It is recommended to register the registerTerminalLogoutListener listener upon application startup to promptly handle scenarios where the account is logged out from another device. This allows for the timely cleanup of local data and guidance for the user to log in again.
Is this page helpful?
YesFeedbackIs this page helpful?
YesFeedback