Cómo detectar la inactividad del usuario en Android

101

El usuario inicia mi aplicación e inicia sesión.
Selecciona el tiempo de espera de la sesión en 5 minutos.
Realiza algunas operaciones en la aplicación. (todo en primer plano)
Ahora el usuario lleva Myapp a segundo plano e inicia alguna otra aplicación.
----> El temporizador de cuenta regresiva comienza y cierra la sesión del usuario después de 5 minutos
O el usuario apaga la pantalla.
----> El temporizador de cuenta regresiva comienza y cierra la sesión del usuario después de 5 minutos

Quiero el mismo comportamiento incluso cuando la aplicación está en primer plano, pero el usuario no interactúa con la aplicación durante mucho tiempo, digamos 6-7 minutos. Suponga que la pantalla está encendida todo el tiempo. Quiero detectar el tipo de inactividad del usuario (sin interacción con la aplicación aunque la aplicación esté en primer plano) y poner en marcha mi temporizador de cuenta atrás.

Akh
fuente
1
¿Podría siempre tener ese temporizador funcionando y reiniciarlo cada vez que el usuario hace algo?
Kyle P

Respuestas:

111

Se me ocurrió una solución que encuentro bastante simple basada en la respuesta de Fredrik Wallenius. Esta es una clase de actividad básica que debe ampliarse a todas las actividades.

public class MyBaseActivity extends Activity {

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


    private static Handler disconnectHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            // todo
            return true;
        }
    });

    private static Runnable disconnectCallback = new Runnable() {
        @Override
        public void run() {
            // Perform any required operation on disconnect
        }
    };

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

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

    @Override
    public void onUserInteraction(){
        resetDisconnectTimer();
    }

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

    @Override
    public void onStop() {
        super.onStop();
        stopDisconnectTimer();
    }
}
gfrigon
fuente
3
Esto creará múltiples instancias de Handlery Runnablepara cada Activitycreado. Si convertimos estos dos miembros a static, esto se evitará. Además, ¿podría decirme por qué has llamado stopDisconnectTimer()de onStop()`?
Gaurav Bhor
@Gaurav En mi caso, esto solo se implementa en una actividad (por lo tanto, no detecté el problema con el staticmodificador). En cuanto a onStop(), por lo que recuerdo, llamo onBackPressed()para volver a una pantalla de inicio de sesión en la devolución de llamada de desconexión que a su vez llama al onStop()método. Cuando el usuario regresa a la pantalla de inicio de sesión manualmente, presionando Atrás, el temporizador debe detenerse y, por lo tanto, el stopDisconnectTimer()in onStop(). Supongo que esta parte depende de sus necesidades e implementación.
gfrigon
@gfrigon ¿es posible redirigir al usuario a la actividad de inicio de sesión?
Apostrofix
@Apostrifix, por supuesto que es posible. En mi caso solo hubo una actividad: llamar onBackPressed()vas suficiente. Si tiene más de una actividad en su pila, solo tiene que crear una intención para ese asunto. Es posible que desee ver la siguiente respuesta para borrar la tarea Actividad (y evitar que los usuarios se vuelvan a conectar en la parte posterior): stackoverflow.com/questions/7075349/…
gfrigon
¡Buen trabajo! Agregué getter y setter para el ejecutable y luego lo configuré en la clase de extensión según sea necesario usando el método onCreate ... perfecto, nuevamente gracias.
CrandellWS
90

No conozco una forma de rastrear la inactividad, pero hay una manera de rastrear la actividad del usuario. Puede recibir una devolución de llamada llamada onUserInteraction()en sus actividades que se llama cada vez que el usuario interactúa con la aplicación. Sugeriría hacer algo como esto:

@Override
public void onUserInteraction(){
    MyTimerClass.getInstance().resetTimer();
}

Si su aplicación contiene varias actividades, ¿por qué no poner este método en una superclase abstracta (extensión Activity) y luego hacer que todas sus actividades la extiendan?

Fredrik Wallenius
fuente
1
Sí, esta es una forma de hacerlo ... pero mi aplicación tiene 30 actividades diferentes y habría demasiada interacción cuando el usuario está activo ... así que cada vez que reiniciar el temporizador sería una operación costosa ... el peor de los casos puede ser de 50 a 60 veces en un minuto.
Akh
3
No lo he cronometrado, pero diría que reiniciando un temporizador como este lastInteraction = System.currentTimeMillis (); tomaría, digamos, 2 ms. Hazlo 60 veces por minuto y "perderás" 120ms. De 60000.
Fredrik Wallenius
1
Fredrik ... También estoy usando tu sugerencia para cumplir con este escenario. El tiempo de espera de la pantalla está configurado en un máximo de 30 minutos en el dispositivo. MyApp shd timeout después de 15 minutos ... Si el usuario no toca nada en la pantalla durante más de 1 minuto, iniciaré el temporizador de cierre de sesión de 15 minutos ... En este caso, verificaría la diferencia (lastInteractionTime y System.currentTimeMills ( )) es más de 1 minuto ... luego dispara ..
Akh
3
onUserInteraction () no se llama en algunos casos, sin embargo (los diálogos no lo llaman y el desplazamiento en los controles giratorios) ¿hay alguna solución para estas situaciones?
AndroidNoob
¿Podrías compartir tu MyTimerClass?
Sibelius Seraphini
19

Creo que debería seguir este código, esto es para un tiempo de espera de sesión inactivo de 5 minutos: ->

Handler handler;
Runnable r;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    handler = new Handler();
    r = new Runnable() {

       @Override
       public void run() {
            // TODO Auto-generated method stub
            Toast.makeText(MainActivity.this, "user is inactive from last 5 minutes",Toast.LENGTH_SHORT).show();
        }
    };
    startHandler();
}
@Override
public void onUserInteraction() {
     // TODO Auto-generated method stub
     super.onUserInteraction();
     stopHandler();//stop first and then start
     startHandler();
}
public void stopHandler() {
    handler.removeCallbacks(r);
}
public void startHandler() {
    handler.postDelayed(r, 5*60*1000); //for 5 minutes 
}
Pradeep Gupta
fuente
Me salvaste la vida con onUserInteraction
codezombie
10
public class MyApplication extends Application {
      private int lastInteractionTime;
      private Boolean isScreenOff = false; 
      public void onCreate() {
        super.onCreate();
        // ......   
        startUserInactivityDetectThread(); // start the thread to detect inactivity
        new ScreenReceiver();  // creating receive SCREEN_OFF and SCREEN_ON broadcast msgs from the device.
      }

      public void startUserInactivityDetectThread() {
        new Thread(new Runnable() {
          @Override
          public void run() {
            while(true) {
              Thread.sleep(15000); // checks every 15sec for inactivity
              if(isScreenOff || getLastInteractionTime()> 120000 ||  !isInForeGrnd)
                {
                  //...... means USER has been INACTIVE over a period of
                  // and you do your stuff like log the user out 
                }
              }
          }
        }).start();
      }

      public long getLastInteractionTime() {
        return lastInteractionTime;
      }

      public void setLastInteractionTime(int lastInteractionTime) {
        this.lastInteractionTime = lastInteractionTime;
      }

      private class ScreenReceiver extends BroadcastReceiver {

        protected ScreenReceiver() {
           // register receiver that handles screen on and screen off logic
           IntentFilter filter = new IntentFilter();
           filter.addAction(Intent.ACTION_SCREEN_ON);
           filter.addAction(Intent.ACTION_SCREEN_OFF);
           registerReceiver(this, filter);
        }

        @Override
        public void onReceive(Context context, Intent intent) {
          if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            isScreenOff = true;
          } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            isScreenOff = false;
          }
        }
      }
    }

isInForeGrnd ===> la lógica no se muestra aquí ya que está fuera del alcance de la pregunta

Puede activar el bloqueo de la CPU mediante el código de dispositivo a continuación:

  if(isScreenOff || getLastInteractionTime()> 120000 ||  !isInForeGrnd)
    {
      //...... means USER has been INACTIVE over a period of
      // and you do your stuff like log the user out 

      PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);

      boolean isScreenOn = pm.isScreenOn();
      Log.e("screen on.................................", "" + isScreenOn);

      if (isScreenOn == false) {

        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "MyLock");

        wl.acquire(10000);
        PowerManager.WakeLock wl_cpu = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyCpuLock");

        wl_cpu.acquire(10000);
      }
    }
Akh
fuente
4
@Nappy: Entonces, explique la forma correcta de hacerlo. Tu comentario es vago e indeciso.
Akh
2
@AKh: Las otras respuestas ya muestran las posibilidades. En su solución, no veo ningún beneficio en las encuestas cada 15 segundos. Tendría el mismo efecto, ya que inicia un temporizador en "ACTION_SCREEN_OFF" con una duración aleatoria de 0 a 15 segundos. Esto simplemente no tiene sentido ..
Pañal
1
@Nappy: Cada 15 segundos no solo verifico SCREEN_ON o SCREEN_OFF, sino también el tiempo de la última interacción del usuario y el estado de primer plano de la aplicación. Basándome en estos tres factores, tomo una decisión lógica sobre qué tan activo está interactuando el usuario con la aplicación.
Akh
Por favor complete su comentario. .... "si su isScreenof boolean es?" Y también debe tenerse en cuenta el estado de foregrnd de la aplicación.
Akh
1
Este código está lleno de errores, algunas variables no se inicializan.
Big.Child
8
@Override
public void onUserInteraction() {
    super.onUserInteraction();
    delayedIdle(IDLE_DELAY_MINUTES);
}

Handler _idleHandler = new Handler();
Runnable _idleRunnable = new Runnable() {
    @Override
    public void run() {
        //handle your IDLE state
    }
};

private void delayedIdle(int delayMinutes) {
    _idleHandler.removeCallbacks(_idleRunnable);
    _idleHandler.postDelayed(_idleRunnable, (delayMinutes * 1000 * 60));
}
soenke
fuente
Esta es la base de la solución, el resto se puede modificar según sus necesidades particulares y la complejidad de la arquitectura de la aplicación. ¡Gracias por la respuesta!
Hack06
Cómo aplicar esto en la clase de aplicación
Gaju Kollur
6

No existe el concepto de "inactividad del usuario" a nivel del sistema operativo, más allá de las transmisiones ACTION_SCREEN_OFFy ACTION_USER_PRESENT. Tendrá que definir "inactividad" de alguna manera dentro de su propia aplicación.

CommonsWare
fuente
6

Incluso puede gestionar sus necesidades con @gfrigon o @AKh soluciones .

Pero aquí hay una solución gratuita de Timer and Handlers para esto. Ya tengo una solución de temporizador bien administrada para esto. Pero he implementado con éxito la solución gratuita Timer and Handler.

Primero te cuento lo que tienes que gestionar si usas Timer o Handlers.

  • Si su aplicación es eliminada por el usuario o por un optimizador, su aplicación nunca se cerrará automáticamente, porque todas sus devoluciones de llamada se destruyen. (¿ Administrar algún administrador de alarmas o servicio? )
  • ¿Es bueno tener un temporizador en todas las clases básicas? Está creando muchos subprocesos solo para invocar el proceso de cierre de sesión (¿ Administrar controlador estático o temporizador a nivel de aplicación? ).
  • ¿Qué pasa si el usuario está en segundo plano, su controlador iniciará la actividad de inicio de sesión si el usuario está haciendo algún otro trabajo fuera de su aplicación? (¿ Administrar el primer plano o el fondo de la aplicación? ).
  • ¿Qué pasa si la pantalla se apaga automáticamente? (¿ Administrar pantalla apagada en el receptor de transmisión? )

Finalmente implementé una solución que es

  1. SIN controlador ni temporizador.
  2. SIN administrador de alarmas.
  3. NO administra App LifeCycle.
  4. NO ACTION_SCREEN_ON/ ACTION_SCREEN_OFFReceptor de difusión.

La solución confiable más fácil

No observaremos la inactividad del usuario por temporizadores en lugar de verificar el tiempo de la última actividad en la actividad del usuario. Entonces, cuando el usuario interactúe con la aplicación la próxima vez, verifico el tiempo de la última interacción.

Aquí es BaseActivity.classlo que extenderá de cada una de sus clases de actividades en lugar de LoginActivity. Definirá su tiempo de cierre de sesión en el campo TIMEOUT_IN_MILLIde esta clase.

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.
    }
}
Khemraj
fuente
¿Por qué es mejor acceder a las preferencias compartidas en el hilo principal en cada interacción del usuario que invocar varios hilos?
Nishita
@Nishita al momento de publicar esta respuesta, no estaba al tanto de esta desventaja. Gracias por comentar mi 1 mala respuesta. Tienes razón, esta no es la forma correcta de hacerlo. Ocultaré esta respuesta.
Khemraj
2

En mi clase base de actividad creé una clase protegida:

protected class IdleTimer
{
    private Boolean isTimerRunning;
    private IIdleCallback idleCallback;
    private int maxIdleTime;
    private Timer timer;

    public IdleTimer(int maxInactivityTime, IIdleCallback callback)
    {
        maxIdleTime = maxInactivityTime;
        idleCallback = callback;
    }

    /*
     * creates new timer with idleTimer params and schedules a task
     */
    public void startIdleTimer()
    {
        timer = new Timer();            
        timer.schedule(new TimerTask() {

            @Override
            public void run() {             
                idleCallback.inactivityDetected();
            }
        }, maxIdleTime);
        isTimerRunning = true;
    }

    /*
     * schedules new idle timer, call this to reset timer
     */
    public void restartIdleTimer()
    {
        stopIdleTimer();
        startIdleTimer();
    }

    /*
     * stops idle timer, canceling all scheduled tasks in it
     */
    public void stopIdleTimer()
    {
        timer.cancel();
        isTimerRunning = false;
    }

    /*
     * check current state of timer
     * @return boolean isTimerRunning
     */
    public boolean checkIsTimerRunning()
    {
        return isTimerRunning;
    }
}

protected interface IIdleCallback
{
    public void inactivityDetected();
}

Entonces, en el método onResume , puede especificar la acción en su devolución de llamada qué desea hacer con ella ...

idleTimer = new IdleTimer(60000, new IIdleCallback() {
            @Override
            public void inactivityDetected() {
                ...your move...
            }
        });
        idleTimer.startIdleTimer();
dividir entre cero
fuente
¿Cómo comprobar que el usuario está inactivo? alguna entrada del sistema?
MohsinSyd
2

Durante mi búsqueda encontré muchas respuestas, pero esta es la mejor respuesta que obtuve. Pero la limitación de este código es que funciona solo para la actividad, no para toda la aplicación. Toma esto como referencia.

myHandler = new Handler();
myRunnable = new Runnable() {
    @Override
    public void run() {
        //task to do if user is inactive

    }
};
@Override
public void onUserInteraction() {
    super.onUserInteraction();
    myHandler.removeCallbacks(myRunnable);
    myHandler.postDelayed(myRunnable, /*time in milliseconds for user inactivity*/);
}

por ejemplo, usó 8000, la tarea se realizará después de 8 segundos de inactividad del usuario.

A_rmas
fuente
2

La inactividad del usuario puede detectar usando el onUserInteraction()método de anulación en Android

  @Override
    public void onUserInteraction() {
        super.onUserInteraction();

    }

Aquí está el código de muestra, cierre la sesión (HomeActivity -> LoginActivity) después de 3 minutos cuando el usuario está inactivo

public class HomeActivity extends AppCompatActivity {

    private static String TAG = "HomeActivity";
    private Handler handler;
    private Runnable r;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_home);


        handler = new Handler();
        r = new Runnable() {

            @Override
            public void run() {

                Intent intent = new Intent(getApplicationContext(), LoginActivity.class);
                startActivity(intent);
                Log.d(TAG, "Logged out after 3 minutes on inactivity.");
                finish();

                Toast.makeText(HomeActivity.this, "Logged out after 3 minutes on inactivity.", Toast.LENGTH_SHORT).show();
            }
        };

        startHandler();

    }

    public void stopHandler() {
        handler.removeCallbacks(r);
        Log.d("HandlerRun", "stopHandlerMain");
    }

    public void startHandler() {
        handler.postDelayed(r, 3 * 60 * 1000);
        Log.d("HandlerRun", "startHandlerMain");
    }

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();
        stopHandler();
        startHandler();
    }

    @Override
    protected void onPause() {

        stopHandler();
        Log.d("onPause", "onPauseActivity change");
        super.onPause();

    }

    @Override
    protected void onResume() {
        super.onResume();
        startHandler();

        Log.d("onResume", "onResume_restartActivity");

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopHandler();
        Log.d("onDestroy", "onDestroyActivity change");

    }

}
Damith Alahakoon
fuente
2

Manejo de usuario en tiempo de espera de interacción en KOTLIN:

     //Declare handler
      private var timeoutHandler: Handler? = null
      private var interactionTimeoutRunnable: Runnable? = null

 override fun onCreate(savedInstanceState: Bundle?) {
      super.onCreate(savedInstanceState)
      setContentView(R.layout.activity_aspect_ratio)

       //Initialise handler
      timeoutHandler =  Handler();
      interactionTimeoutRunnable =  Runnable {
         // Handle Timeout stuffs here
          }

      //start countdown
      startHandler()
}

// reset handler on user interaction
override fun onUserInteraction() {
      super.onUserInteraction()
      resetHandler()
}

 //restart countdown
fun resetHandler() {
      timeoutHandler?.removeCallbacks(interactionTimeoutRunnable);
      timeoutHandler?.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second

}

 // start countdown
fun startHandler() {
    timeoutHandler?.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second
}
Hitesh Sahu
fuente
1

Aquí hay una solución completa que maneja la inactividad del usuario después de unos minutos (por ejemplo, 3 minutos). Esto resuelve los problemas comunes, como que la actividad salte al primer plano cuando la aplicación está en segundo plano cuando se agota el tiempo de espera.

En primer lugar, creamos una BaseActivity que todas las demás actividades pueden ampliar.

Este es el código BaseActivity.

package com.example.timeout;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;


import javax.annotation.Nullable;

public class BaseActivity extends AppCompatActivity implements LogoutListener {

    private Boolean isUserTimedOut = false;
    private static Dialog mDialog;



    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        ((TimeOutApp) getApplication()).registerSessionListener(this);
        ((TimeOutApp) getApplication()).startUserSession();

    }

    @Override
    public void onUserInteraction() {
        super.onUserInteraction();


    }

    @Override
    protected void onResume() {
        super.onResume();

        if (isUserTimedOut) {
            //show TimerOut dialog
            showTimedOutWindow("Time Out!", this);

        } else {

            ((TimeOutApp) getApplication()).onUserInteracted();

        }

    }

    @Override
    public void onSessionLogout() {


        isUserTimedOut = true;

    }


    public void showTimedOutWindow(String message, Context context) {


        if (mDialog != null) {
            mDialog.dismiss();
        }
        mDialog = new Dialog(context);


        mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        mDialog.setContentView(R.layout.dialog_window);

        mDialog.setCancelable(false);
        mDialog.setCanceledOnTouchOutside(false);

        TextView mOkButton = (TextView) mDialog.findViewById(R.id.text_ok);
        TextView text_msg = (TextView) mDialog.findViewById(R.id.text_msg);

        if (message != null && (!TextUtils.isEmpty(message)) && (!message.equalsIgnoreCase("null"))) {
            text_msg.setText(message);

        }


        mOkButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (mDialog != null){

                    mDialog.dismiss();

                    Intent intent = new Intent(BaseActivity.this, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                    startActivity(intent);

                    finish();
                }


            }
        });

        if(!((Activity) context).isFinishing())
        {
            //show dialog
            mDialog.show();
        }

    }

}

A continuación, creamos una interfaz para nuestro "Logout Listener"

package com.example.timeout;

public interface LogoutListener {

    void onSessionLogout();

}

Finalmente, creamos una clase Java que extiende "Aplicación"

package com.example.timeout;

import android.app.Application;

import java.util.Timer;
import java.util.TimerTask;

public class TimeOutApp extends Application {

    private LogoutListener listener;
    private Timer timer;
    private static final long INACTIVE_TIMEOUT = 180000; // 3 min


    public void startUserSession () {
        cancelTimer ();

        timer = new Timer ();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {

                listener.onSessionLogout ();

            }
        }, INACTIVE_TIMEOUT);

    }

    private void cancelTimer () {
        if (timer !=null) timer.cancel();
    }

    public void registerSessionListener(LogoutListener listener){
        this.listener = listener;
    }

    public void onUserInteracted () {
        startUserSession();
    }


}

Nota: No olvide agregar la clase "TimeOutApp" a la etiqueta de su aplicación dentro de su archivo de manifiesto.

<application
        android:name=".TimeOutApp">
        </application>
Oyewo Remi
fuente
0

Creo que debe ser combinando el temporizador con el último tiempo de actividad.

Así que así:

  1. En onCreate (Bundle SavedInstanceState) inicie un temporizador, digamos 5 minutos

  2. En onUserInteraction () solo almacena la hora actual

Bastante simple hasta ahora.

Ahora, cuando el temporizador suene, haz lo siguiente:

  1. Tome el tiempo actual y reste el tiempo de interacción almacenado para obtener timeDelta
  2. Si timeDelta es> = los 5 minutos, ya está.
  3. Si timeDelta es <los 5 minutos, inicie el temporizador nuevamente, pero esta vez use 5 minutos, el tiempo almacenado. En otras palabras, 5 minutos forman la última interacción
Robert Wiebe
fuente
0

Tuve una situación similar a la pregunta SO, donde necesitaba rastrear la inactividad del usuario durante 1 minuto y luego redirigir al usuario para que inicie la Actividad, también necesitaba borrar la pila de actividades.

Basado en la respuesta de @gfrigon, se me ocurre esta solución.

ActionBar.java

public abstract class ActionBar extends AppCompatActivity {

    public static final long DISCONNECT_TIMEOUT = 60000; // 1 min

    private final MyHandler mDisconnectHandler = new MyHandler(this);

    private Context mContext;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mContext = this;
    }



    /*
    |--------------------------------------------------------------------------
    | Detect user inactivity in Android
    |--------------------------------------------------------------------------
    */

    // Static inner class doesn't hold an implicit reference to the outer class

    private static class MyHandler extends Handler {

        // Using a weak reference means you won't prevent garbage collection

        private final WeakReference<ActionBar> myClassWeakReference;

        public MyHandler(ActionBar actionBarInstance) {

            myClassWeakReference = new WeakReference<ActionBar>(actionBarInstance);
        }

        @Override
        public void handleMessage(Message msg) {

            ActionBar actionBar = myClassWeakReference.get();

            if (actionBar != null) {
                // ...do work here...
            }
        }
    }


    private Runnable disconnectCallback = new Runnable() {

        @Override
        public void run() {

            // Perform any required operation on disconnect

            Intent startActivity = new Intent(mContext, StartActivity.class);

            // Clear activity stack

            startActivity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);

            startActivity(startActivity);
        }
    };

    public void resetDisconnectTimer() {

        mDisconnectHandler.removeCallbacks(disconnectCallback);
        mDisconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
    }

    public void stopDisconnectTimer() {

        mDisconnectHandler.removeCallbacks(disconnectCallback);
    }

    @Override
    public void onUserInteraction(){

        resetDisconnectTimer();
    }

    @Override
    public void onResume() {

        super.onResume();
        resetDisconnectTimer();
    }

    @Override
    public void onStop() {

        super.onStop();
        stopDisconnectTimer();
    }
}

Recursos complementarios

Android: Borrar pila de actividades

Esta clase de controlador debe ser estática o podrían producirse fugas

chebaby
fuente
0

Lo mejor es manejar esto en toda su aplicación (suponiendo que tenga varias actividades) registrándose AppLifecycleCallbacksen la aplicación Calss. Puede usar registerActivityLifecycleCallbacks()en la clase Application con las siguientes devoluciones de llamada (recomiendo crear una clase AppLifecycleCallbacks que amplíe ActivityLifecycleCallbacks):

public interface ActivityLifecycleCallbacks {
    void onActivityCreated(Activity activity, Bundle savedInstanceState);
    void onActivityStarted(Activity activity);
    void onActivityResumed(Activity activity);
    void onActivityPaused(Activity activity);
    void onActivityStopped(Activity activity);
    void onActivitySaveInstanceState(Activity activity, Bundle outState);
    void onActivityDestroyed(Activity activity);
}
caja
fuente
0
open class SubActivity : AppCompatActivity() {
    var myRunnable:Runnable
    private var myHandler = Handler()

    init {
        myRunnable = Runnable{
            toast("time out")
            var intent = Intent(this, MainActivity::class.java)
            startActivity(intent)

        }
    }

    fun toast(text: String) {
        runOnUiThread {
            val toast = Toast.makeText(applicationContext, text, Toast.LENGTH_SHORT)
            toast.show()
        }
    }

   override fun onUserInteraction() {
        super.onUserInteraction();
        myHandler.removeCallbacks(myRunnable)
        myHandler.postDelayed(myRunnable, 3000)
    }

    override fun onPause() {
        super.onPause()
        myHandler.removeCallbacks(myRunnable)
    }

    override fun onResume() {
            super.onResume()
            myHandler.postDelayed(myRunnable, 3000)
    }
}

Amplíe su actividad con

YourActivity:SubActivity(){}

para llegar a MainActivity cuando el usuario está inactivo después de 3000 milisegundos en YourActivity

Usé una respuesta anterior y la convertí a kotlin.

Tom Peak
fuente