How to detect user is inactivity for some time with multiple activity in android
Asked Answered
V

2

8

I know same question is here, already asked by some one and there is answer for that,but i have issue with that answer.

My requirement is,I want to identify if user is inactivity for 5 minute then user get auto logout from app and upload the the sign in and sign out log on server.

I have tried all answer from above link,but that answer are working for Single activity,but i want to track it for multiple activity.

For that i have created abstract class

public abstract class SessionTimeOutActivity extends BaseActivity {

    public static final long DISCONNECT_TIMEOUT = 1000 * 60; // 5 min = 5 * 60 * 1000 ms

    private static Handler disconnectHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            Log.d("SessionTimeOutActivity", "disconnectHandler");

            return false;
        }
    });

    private Runnable disconnectCallback = new Runnable() {
        @Override
        public void run() {
            // Perform any required operation on disconnect
            Log.d("SessionTimeOutActivity", "disconnectCallback");


            Toast.makeText(getApplicationContext(), "Session time out", Toast.LENGTH_LONG).show();
            Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
            startActivity(intent);
            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);

            finish();

        }
    };

    public void resetDisconnectTimer() {
        disconnectHandler.removeCallbacks(disconnectCallback);
        disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
    }

    public void stopDisconnectTimer() {
        disconnectHandler.removeCallbacks(disconnectCallback);
    }

    @Override
    public void onUserInteraction() {
        Log.d("SessionTimeOutActivity", "onUserInteraction");
        resetDisconnectTimer();
    }

    @Override
    public void onResume() {
        super.onResume();
//        resetDisconnectTimer();
    }

    @Override
    public void onStop() {
        super.onStop();
//        stopDisconnectTimer();
    }


}

Other activities in app

    public class MenuActivtyNav extends SessionTimeOutActivity{
.....
}

MenuActivity

 public class MenuActivty extends SessionTimeOutActivity{
....
}

Issue is

1) In Lock screen Auto logout not working,it not calling disconnectCallback

2) While using app it shows toast message "Session time out"

Vestiary answered 29/5, 2018 at 14:16 Comment(0)
A
11

Let's start with understanding the reasons why your code is not doing what you want.

Problem 1. You should avoid having long running tasks in your activities on android, especially when they are not visible because the system can kill your activity or application process, so the code won't run.

Problem 2. Every activity posts the disconnect callback to your handler, and in case when user is interacting with second activity the first activity's callback won't be reset and will trigger.

Solution.

Now let's think about an approach to solve this. It would be better to have one place to track the inactivity. It can be a singleton or you can use application object. And handle 2 cases separately: first one is if your user is inside the app and is not using it and another one is when your user is out of the app.

For the first goal you can have approach similar to your current one, but make shared disconnectCallback in the application. Move the handler, callback and reset callback logic to your Application class and make your

@Override
public void onUserInteraction()

call resetCallback on the Application. This will make all Activities use the same callback and solve problem 2.

For the second goal, you can make use of the Activity Lifecycle Callbacks. Every time you have your activity paused save the timestamp. Then every time the activity is resumed compare this timestamp with the resume time, if it is greater than 5 minutes, then log out your user and route to login screen. It will solve problem 1. You'll need to persist this timestamp because the application can be killed by the system and everything shared in memory will be wiped off. Use shared preferences for example.

And the last thing, you'll need to cancel your callbacks when the app is going to background. You can do it in onActivityPaused(Activity activity) of your activity callbacks, in the same place where you'll be triggering logic for the second case.

Argentic answered 31/5, 2018 at 19:52 Comment(2)
thank you reply,how can i handle Case 2 because every time a start a new activity after some time my previous activity goes to paused mode and it call onActivityPausedVestiary
Your new activity will call onActivityResumed right after that, where you check if the time lapsed from last onActivityPaused is greater that 5 minutes. If it's smaller you do nothing and clear your paused timestamp.Argentic
C
2

Even you could manage your requirement after doing some efforts with this answer.

But I was thinking about some Timer and Handlers free solutions for this. I already have well managed Timer solution for this. But i have successfully implemented Timer and Handler free solution.

First i tell you what you have to manage if you use Timer or Handlers.

  • If your app is killed by user or by a optimizer, your app will never be logout automatically, because all your callbacks are destroyed. (Manage some Alarm Manager or Service?)
  • Is it good to have timer in every base class? You are making many threads for just invoking logout process (Manage static Handler or Timer at app level?).
  • What if user is in background, your Handler will start Login Activity if user is doing some other work outside your app. (Manage app foreground or background?).
  • What if screen gets off automatically. (Manage screen off on broadcast receiver?)

Finally i implemented a solution which is

  1. You don't have to use Hander or Timer.
  2. You don't have to use Alarm Manager.
  3. You don't have to use App Lifecycle.
  4. You don't have to use ACTION_SCREEN_ON / ACTION_SCREEN_OFF Broadcast Receiver.

Solution

We will not observe user inactivity by timers rather than we will check last activity time on user activity. So when user interact app next time, i check last interaction time.

Here is BaseActivity.class which you will extend from every of your activity class instead of LoginActivity.

import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class BaseActivity extends AppCompatActivity {
    public static final long TIMEOUT_IN_MILLI = 1000 * 20;
    public static final String PREF_FILE = "App_Pref";
    public static final String KEY_SP_LAST_INTERACTION_TIME = "KEY_SP_LAST_INTERACTION_TIME";

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        if (isValidLogin())
            getSharedPreference().edit().putLong(KEY_SP_LAST_INTERACTION_TIME, System.currentTimeMillis()).apply();
        else logout();
    }

    public SharedPreferences getSharedPreference() {
        return getSharedPreferences(PREF_FILE, MODE_PRIVATE);
    }

    public boolean isValidLogin() {
        long last_edit_time = getSharedPreference().getLong(KEY_SP_LAST_INTERACTION_TIME, 0);
        return last_edit_time == 0 || System.currentTimeMillis() - last_edit_time < TIMEOUT_IN_MILLI;
    }

    public void logout() {
        Intent intent = new Intent(this, LoginActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
        finish();
        Toast.makeText(this, "User logout due to inactivity", Toast.LENGTH_SHORT).show();
        getSharedPreference().edit().remove(KEY_SP_LAST_INTERACTION_TIME).apply(); // make shared preference null.
    }
}
Cascade answered 3/6, 2018 at 6:57 Comment(4)
if i swipe out the app or remove from recent task,is it call the onUserInteraction()?Vestiary
I did not check it. You can check it out by debugging or put a log. By the way i did not mention onResume() as userInteraction because including only onUserInteraction() will give you correct results.Cascade
using onUserInteraction only and not managing anything else, will reduce complexity and give you accurate results when user truly interacted with app.Cascade
Strange why this answer have very less vote! by the way thanks for this answer @KhemrajSharmaBekki

© 2022 - 2024 — McMap. All rights reserved.