This example will show you how to implement force log out when the same user account login in another instance of the same android app. It will use both android Activity, Service, and Broadcast Receiver components to implement.
1. Android Force Logout Example.
If you can not watch the above video, you can see it on the youtube URL https://youtu.be/4Ka4ggSq2aU
- Let us see the example demo first. There are two android apps ( App One and App Two ) running in the same android emulator with the same function. Each app provides a login form activity, after login, it will direct the user to dashboard activity.
- When user dashboard activity is created, it will start a background service. The background service will register a broadcast receiver when it is created.
- The broadcast receiver will respond to broadcasts sent from other apps when the user login into those apps.
- In user login activity, if the user inputs the wrong username and password, there will popup a toast message at the bottom to let the user input again.
- If the user inputs the correct username and password in App One login activity, it will send a custom normal broadcast to notify App Two that the same user account logged in in App One.
- App Two‘s broadcast receiver which is registered in the background service will receive another login broadcast. Then it will log out and show login activity again in App Two.
2. Android Force Logout Source Code.
- To run the example, you need to open two android studios and run two example projects.
- The two projects have the same java file. The two apps should run in the same android emulator.
- Below is the example project’s files list.
./ ├── app │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── com │ │ │ └── dev2qa │ │ │ └── example │ │ │ ├── broadcast │ │ │ │ ├── activity │ │ │ │ │ └── forceoffline │ │ │ │ │ ├── BaseActivity.java │ │ │ │ │ ├── LoginDashboardActivity.java │ │ │ │ │ └── LoginFormActivity.java │ │ │ │ ├── receiver │ │ │ │ │ └── forceoffline │ │ │ │ │ └── ForceOfflineReceiver.java │ │ │ │ └── service │ │ │ │ └── forceoffline │ │ │ │ └── LoginBackgroundService.java │ │ ├── res │ │ │ ├── layout │ │ │ │ ├── activity_login_dashboard.xml │ │ │ │ ├── activity_login_form.xml
2.1 Base Activity Java File.
- BaseActivity is the base class which login activity and dashboard activity extends. Its child activity will be managed by a util class ActivityManagerUtil.
- BaseActivity.java
package com.dev2qa.example.broadcast.activity.forceoffline; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import com.dev2qa.example.broadcast.receiver.forceoffline.ForceOfflineReceiver; import com.dev2qa.example.util.ActivityManagerUtil; public class BaseActivity extends AppCompatActivity { private ForceOfflineReceiver forceOfflineReceiver; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Add this activity to a central activity controller. ActivityManagerUtil.addActivity(this); } @Override protected void onDestroy() { super.onDestroy(); // Remove current activity from central activity controller. ActivityManagerUtil.finishActivity(this); } }
- ActivityManagerUtil.java
package com.dev2qa.example.util; import android.app.Activity; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; import android.util.Log; import java.util.ArrayList; import java.util.List; public class ActivityManagerUtil { // This list is used to save all activities. private static List<Activity> activityList = new ArrayList<Activity>(); // Add an activity in activityList. public static void addActivity(Activity activity) { if(activityList==null) { activityList = new ArrayList<Activity>(); } if(activity!=null) { activityList.add(activity); } } // Finish and remove an activity from activityList. public static void finishActivity(Activity activity) { if(activityList!=null) { if(activity!=null && activityList.contains(activity)) { activity.finish(); activityList.remove(activity); } } } // Finish and remove all activity in activityList. public static void finishAllActivity() { if(activityList!=null) { // First finish all the activity in the list. int size = activityList.size(); for(int i=0;i<size;i++) { Activity activity = activityList.get(i); activity.finish(); } // Then remove all the activity in the list. for(int i=0;i<size;i++) { activityList.remove(i); } } } }
2.2 Login Activity Java File.
- LoginFormActivity.java
package com.dev2qa.example.broadcast.activity.forceoffline; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.dev2qa.example.R; import com.dev2qa.example.broadcast.receiver.forceoffline.ForceOfflineReceiver; public class LoginFormActivity extends BaseActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login_form); setTitle("App One. dev2qa.com - Android Force Offline Login Example."); final EditText userNameEditor = (EditText)findViewById(R.id.force_offline_login_username); final EditText passwordEditor = (EditText)findViewById(R.id.force_offline_login_password); Button loginButton = (Button)findViewById(R.id.force_offline_login_button); loginButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { String userName = userNameEditor.getText().toString(); String password = passwordEditor.getText().toString(); if("dev2qa.com".equalsIgnoreCase(userName) && "dev2qa.com".equalsIgnoreCase(password)) { // User account is correct. // Send a broadcast to let already login user force logoff. Intent forceLogoffIntent = new Intent(ForceOfflineReceiver.ACTION_FORCE_OFFLINE); sendBroadcast(forceLogoffIntent); // Start login dashboard activity. Intent intent = new Intent(getApplicationContext(), LoginDashboardActivity.class); startActivity(intent); // Finish current activity. finish(); }else { // User account is not correct, show a toast popup message. Toast.makeText(getApplicationContext(), "User name or password is not correct, please input again.", Toast.LENGTH_LONG).show(); } } }); } }
2.3 Dashboard Activity Java File.
- LoginDashboardActivity.java
package com.dev2qa.example.broadcast.activity.forceoffline; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import com.dev2qa.example.R; import com.dev2qa.example.broadcast.service.forceoffline.LoginBackgroundService; public class LoginDashboardActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login_dashboard); setTitle("App One. dev2qa.com - User Dashboard Activity"); // Start a login background service to process user login force logoff action. Intent backgroundService = new Intent(getApplicationContext(), LoginBackgroundService.class); startService(backgroundService); } }
2.4 Background Service Java File.
- LoginBackgroundService.java
package com.dev2qa.example.broadcast.service.forceoffline; import android.app.Service; import android.content.Intent; import android.content.IntentFilter; import android.os.IBinder; import android.support.annotation.Nullable; import com.dev2qa.example.broadcast.receiver.forceoffline.ForceOfflineReceiver; public class LoginBackgroundService extends Service { private ForceOfflineReceiver forceOfflineReceiver; @Override public void onCreate() { super.onCreate(); // When service object is created, register a custom broadcast receiver. // So that this activity can process ACTION_FORCE_OFFLINE custom broadcast. forceOfflineReceiver = new ForceOfflineReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ForceOfflineReceiver.ACTION_FORCE_OFFLINE); registerReceiver(forceOfflineReceiver, intentFilter); } @Override public void onDestroy() { super.onDestroy(); // When service object destroy, unregister the force offline receiver. if(forceOfflineReceiver!=null) { unregisterReceiver(forceOfflineReceiver); forceOfflineReceiver = null; } } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
2.5 Force Offline Broadcast Receiver Java File.
- ForceOfflineReceiver.java
package com.dev2qa.example.broadcast.receiver.forceoffline; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; import com.dev2qa.example.broadcast.activity.forceoffline.LoginFormActivity; import com.dev2qa.example.util.ActivityManagerUtil; public class ForceOfflineReceiver extends BroadcastReceiver { public static final String ACTION_FORCE_OFFLINE = "com.dev2qa.example.broadcast.receiver.ACTION_FORCE_OFFLINE"; @Override public void onReceive(final Context context, Intent intent) { // Get intent action value. String action = intent.getAction(); // If force offline action. if(ACTION_FORCE_OFFLINE.equals(action)) { Toast.makeText(context, "This account login in another device, you need login again", Toast.LENGTH_LONG).show(); // Finish all activities. ActivityManagerUtil.finishAllActivity(); // Start the login form activity to let user login again. Intent loginFormIntent = new Intent(context, LoginFormActivity.class); context.startActivity(loginFormIntent); } } }
2.6 Login Activity Layout Xml File.
- activity_login_form.xml
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="User Name :"/> <EditText android:id="@+id/force_offline_login_username" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="3" android:hint="Input user name."/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Password :"/> <EditText android:id="@+id/force_offline_login_password" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="3" android:hint="Input password." android:password="true"/> </LinearLayout> <Button android:id="@+id/force_offline_login_button" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Login"/> </LinearLayout>
2.7 User Dashboard Layout XML File.
- activity_login_dashboard.xml
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:text="Welcom to user dashboard after login." android:textSize="20dp"/>
2.8 Android Manifest XML File.
- AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.dev2qa.example"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".broadcast.activity.forceoffline.LoginFormActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".broadcast.activity.forceoffline.LoginDashboardActivity"></activity> <service android:name=".broadcast.service.forceoffline.LoginBackgroundService" android:enabled="true" /> </application> </manifest>