para - manual de programacion android pdf
Cómo detectar la inactividad del usuario en Android (10)
Más vale tarde que nunca
Incluso usted puede administrar su requerimiento con las soluciones @gfrigon o @AKh .
Pero estaba pensando en algunas soluciones gratuitas Timer and Handlers para esto. Ya he manejado bien la solución Timer para esto. Pero he implementado con éxito la solución gratuita Timer and Handler.
Primero te digo lo que tienes que administrar si usas Timer o Handlers.
- Si el usuario o un optimizador mata tu aplicación, tu aplicación nunca cerrará sesión automáticamente, ya que todas tus devoluciones de llamadas se destruirán. (¿ Gestiona algún administrador o servicio de alarma? )
- ¿Es bueno tener temporizador en cada clase base? Está haciendo muchos subprocesos solo para invocar el proceso de cierre de sesión (¿ Administrar el controlador estático o el temporizador a nivel de la aplicación? ).
- Si el usuario está en segundo plano, su controlador iniciará la actividad de inicio de sesión si el usuario está realizando otro trabajo fuera de su aplicación. (¿ Administrar el fondo o la aplicación de la aplicación? ).
- ¿Qué pasa si la pantalla se apaga automáticamente? (¿ Administrar la pantalla en el receptor de difusión? )
Finalmente implementé una solución que es
- No tiene que usar Hander o Timer.
- No tiene que usar Alarm Manager.
- No tiene que usar App Lifecycle.
- No tiene que usar
ACTION_SCREEN_ON
/ACTION_SCREEN_OFF
Broadcast Receiver.
Solución
No observaremos la inactividad del usuario por temporizadores en lugar de verificaremos el último tiempo de actividad en la actividad del usuario. Entonces, cuando el usuario interactúa la próxima vez, verifico el último tiempo de interacción.
Aquí está BaseActivity.class
que extenderá de cada una de sus clases de actividad en lugar de LoginActivity
. Definirá su hora de cierre de sesión en el campo TIMEOUT_IN_MILLI
en 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.
}
}
El usuario inicia mi aplicación e inicia sesión.
Selecciona el tiempo de espera de la sesión en 5 minutos.
Hace algunas operaciones en la aplicación. (todo en primer plano)
Ahora el usuario trae a Myapp a un segundo plano y comienza otra aplicación.
----> El temporizador de cuenta regresiva se inicia y cierra la sesión del usuario después de 5 minutos
O el usuario apaga la pantalla.
----> El temporizador de cuenta regresiva se inicia 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. Supongamos 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 reiniciar el temporizador de cuenta atrás.
Creo que deberías ir con este código, esto es para el tiempo de espera de la sesión inactiva 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
}
Creo que tiene que ser combinando el temporizador con la última hora de actividad.
Así que así:
En onCreate (Bundle savedInstanceState), inicie un temporizador, digamos 5 minutos
En onUserInteraction () solo almacena la hora actual
Muy simple hasta ahora.
Ahora cuando el temporizador pop hace así:
- Tome la hora actual y reste el tiempo de interacción almacenado para obtener timeDelta
- Si timeDelta es> = los 5 minutos, ha terminado
- Si timeDelta es <los 5 minutos, inicie nuevamente el temporizador, pero esta vez use 5 minutos: el tiempo almacenado. En otras palabras, 5 minutos forman la última interacción
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 actividades no para aplicaciones completas. 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, si usó 8000, la tarea se realizará después de 8 segundos de inactividad del usuario.
En mi clase base de actividad, creé 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 eso?
idleTimer = new IdleTimer(60000, new IIdleCallback() {
@Override
public void inactivityDetected() {
...your move...
}
});
idleTimer.startIdleTimer();
No conozco una forma de rastrear la inactividad, pero hay una manera de rastrear la actividad del usuario. Puede capturar una devolución de llamada llamada onUserInteraction()
en sus actividades que se onUserInteraction()
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 (ampliando la Activity
) y luego tener todas sus actividades ampliándola?
No existe el concepto de "inactividad del usuario" en el nivel del sistema operativo, más allá de las ACTION_SCREEN_OFF
y ACTION_USER_PRESENT
. Deberá definir "inactividad" de alguna manera dentro de su propia aplicación.
Se me ocurrió una solución que encuentro bastante simple basada en la respuesta de Fredrik Wallenius. Esta es una clase de actividad base que necesita ser extendida por todas las actividades.
public class MyBaseActivity extends Activity {
public static final long DISCONNECT_TIMEOUT = 300000; // 5 min = 5 * 60 * 1000 ms
private Handler disconnectHandler = new Handler(){
public void handleMessage(Message msg) {
}
};
private 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();
}
}
@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));
}
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 utilizando el siguiente código de dispositivo:
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);
}
}