Come rilevare l'inattività dell'utente in Android


101

L'utente avvia la mia app ed effettua il login.
Seleziona Session Timeout su 5 minuti.
Esegue alcune operazioni sull'app. (tutto in primo piano)
Ora l'utente porta Myapp in background e avvia un'altra app.
----> Il conto alla rovescia si avvia e disconnette l'utente dopo 5 minuti
OPPURE l'utente spegne lo schermo.
----> Il conto alla rovescia si avvia e disconnette l'utente dopo 5 minuti

Voglio lo stesso comportamento anche quando l'app è in primo piano ma l'utente non interagisce con l'app per molto tempo, diciamo 6-7 minuti. Supponiamo che lo schermo sia sempre acceso. Voglio rilevare il tipo di inattività dell'utente (nessuna interazione con l'app anche se l'app è in primo piano) e avviare il mio conto alla rovescia.


1
Potresti sempre avere quel timer in esecuzione e resettarlo ogni volta che l'utente fa qualcosa?
Kyle P

Risposte:


111

Ho trovato una soluzione che trovo abbastanza semplice basata sulla risposta di Fredrik Wallenius. Questa è una classe di attività di base che deve essere estesa a tutte le attività.

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();
    }
}

3
Questo creerà più istanze di Handlere Runnableper ogni Activitycreato. Se convertiamo questi due membri in static, questo sarà evitato. Inoltre, potrebbe dirmi il motivo per cui avete chiamato stopDisconnectTimer()in onStop()`?
Gaurav Bhor

@ Gaurav Nel mio caso, questo è implementato solo in un'attività (quindi non ho rilevato il problema con il staticmodificatore). Per quanto riguarda il onStop(), da quello che ricordo, chiamo onBackPressed()per tornare a una schermata di login nella callback di disconnessione che a sua volta chiama il onStop()metodo. Quando l'utente ritorna manualmente alla schermata di accesso, premendo indietro, anche il timer deve essere fermato, quindi stopDisconnectTimer()in onStop(). Immagino che questa parte dipenda dalle tue esigenze e dalla tua implementazione.
gfrigon

@gfrigon è possibile reindirizzare l'utente all'attività di login?
Apostrofix

@Apostrifix, Ovviamente è possibile. Nel mio caso c'era solo un'attività: chiamare onBackPressed()vas sufficiente. Se hai più di un'attività nel tuo stack, devi solo creare un intento per quella materia. Si consiglia di guardare il seguente risposta per chiarire il compito di attività (e impedire agli utenti di ri-collegamento su una schiena): stackoverflow.com/questions/7075349/...
gfrigon

Ottimo lavoro! Ho aggiunto getter e setter per il runnable e poi l'ho impostato nella classe extending secondo necessità usando il metodo onCreate ... perfetto, ancora grazie.
CrandellWS

90

Non conosco un modo per monitorare l'inattività, ma esiste un modo per monitorare l'attività dell'utente. Puoi intercettare una richiamata chiamata onUserInteraction()nelle tue attività che viene chiamata ogni volta che l'utente interagisce con l'applicazione. Suggerirei di fare qualcosa del genere:

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

Se la tua app contiene diverse attività, perché non inserire questo metodo in una super classe astratta (estensione Activity) e poi fare in modo che tutte le tue attività lo estendano.


1
Sì, questo è un modo per farlo ... ma la mia app ha 30 diverse attività e ci sarebbe troppa interazione quando l'utente è attivo ... quindi ogni volta che reimposta il timer sarebbe un'operazione costosa ... il caso peggiore può essere da 50 a 60 volte in un minuto.
Akh

3
Non l'ho cronometrato ma direi di resettare un timer come questo lastInteraction = System.currentTimeMillis (); richiederebbe, diciamo, 2 ms. Fallo 60 volte al minuto e "perdi" 120 ms. Su 60000.
Fredrik Wallenius

1
Fredrik ... Sto usando anche il tuo suggerimento per soddisfare questo scenario .. Il timeout dello schermo è impostato su un massimo di 30 minuti sul dispositivo. MyApp shd timeout dopo 15 minuti ... Se l'utente non tocca nulla sullo schermo per più di 1 minuto, avvierò il timer di disconnessione di 15 minuti ... In questo caso controllerei la differenza (lastInteractionTime e System.currentTimeMills ( )) è più di 1 min ... poi spara ..
Akh

3
onUserInteraction () non viene chiamato in alcuni casi, tuttavia (le finestre di dialogo non lo chiamano e lo scorrimento negli spinner) esiste una soluzione per queste situazioni?
Android Noob

potresti condividere la tua MyTimerClass?
Sibelius Seraphini

19

Penso che dovresti andare con questo codice, questo è per il timeout di sessione di inattività di 5 minuti: ->

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 
}

Mi hai salvato la vita 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 logica non è mostrata qui in quanto è fuori dall'ambito della domanda

È possibile riattivare il blocco della CPU utilizzando il codice del dispositivo di seguito-

  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);
      }
    }

4
@Nappy: Allora per favore spiega il modo giusto per farlo. Il tuo commento è vago e indeciso.
Akh

2
@AKh: Le altre risposte mostrano già le possibilità. Nella tua soluzione, non vedo alcun vantaggio dal sondaggio ogni 15 secondi. Avrebbe lo stesso effetto se avvii un timer su "ACTION_SCREEN_OFF" con una durata casuale da 0-15 secondi. Questo semplicemente non ha senso ..
Nappy

1
@Nappy: ogni 15 secondi non controllo solo SCREEN_ON o SCREEN_OFF, ma anche l'ora dell'ultima interazione dell'utente e lo stato in primo piano dell'app. Sulla base di questi tre fattori prendo una decisione logica su quanto attivo l'utente sta interagendo con l'app.
Akh

Per favore completa il tuo commento. .... "se il tuo isScreenof booleano è?" E anche lo stato di avanzamento dell'app deve essere preso in considerazione.
Akh

1
Questo codice è pieno di errori, alcune variabili non vengono inizializzate.
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));
}

Questa è la base della soluzione, il resto può essere modificato a seconda delle vostre particolari esigenze e della complessità dell'architettura dell'applicazione! Grazie per la risposta!
Hack06

Come applicarlo nella classe di applicazione
Gaju Kollur

6

Non esiste il concetto di "inattività dell'utente" a livello di sistema operativo, al di là delle trasmissioni ACTION_SCREEN_OFFe ACTION_USER_PRESENT. Dovrai definire "inattività" in qualche modo all'interno della tua applicazione.


6

Anche tu puoi gestire le tue esigenze con le soluzioni @gfrigon o @AKh .

Ma ecco la soluzione gratuita di Timer and Handlers per questo. Ho già una soluzione Timer ben gestita per questo. Ma ho implementato con successo la soluzione gratuita Timer e Handler.

Per prima cosa ti dico cosa devi gestire se usi Timer o Handlers.

  • Se la tua app viene terminata dall'utente o da un ottimizzatore, la tua app non verrà mai disconnessa automaticamente, perché tutti i tuoi callback vengono distrutti. ( Gestisci un gestore di allarmi o un servizio? )
  • È bene avere un timer in ogni classe base? Stai creando molti thread solo per richiamare il processo di disconnessione ( Gestisci il gestore statico o il timer a livello di app? ).
  • Che cosa succede se l'utente è in background, il gestore inizierà l'attività di accesso se l'utente sta facendo qualche altro lavoro al di fuori della tua app. ( Gestisci il primo piano o lo sfondo dell'app? ).
  • Cosa succede se lo schermo si spegne automaticamente. ( Gestire lo schermo spento sul ricevitore di trasmissione? )

Finalmente ho implementato una soluzione che è

  1. NESSUN gestore o timer.
  2. NO Alarm Manager.
  3. NON gestire il ciclo di vita delle app.
  4. NO ACTION_SCREEN_ON/ ACTION_SCREEN_OFFRicevitore di trasmissione.

La soluzione più semplice e affidabile

Non osserveremo l'inattività dell'utente in base ai timer piuttosto che controlleremo l'ora dell'ultima attività sull'attività dell'utente. Quindi, quando l'utente interagisce con l'app la prossima volta, controllo l'ora dell'ultima interazione.

Ecco quello BaseActivity.classche estenderai da ogni tua classe di attività invece di LoginActivity. Definirai il tuo tempo di logout nel campo TIMEOUT_IN_MILLIdi questa classe.

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.
    }
}

In che modo l'accesso alle preferenze condivise sul thread principale su ogni interazione dell'utente è migliore rispetto al richiamo di più thread.
Nishita

@ Nishita al momento di postare questa risposta, non ero a conoscenza di questo svantaggio. Grazie per aver commentato la mia 1 cattiva risposta. Hai ragione, questo non è il modo giusto per farlo. Nasconderò questa risposta.
Khemraj

2

Nella mia classe base di attività ho creato la classe protetta:

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();
}

Quindi, nel metodo onResume , puoi specificare l'azione nella tua richiamata cosa vuoi fare con esso ...

idleTimer = new IdleTimer(60000, new IIdleCallback() {
            @Override
            public void inactivityDetected() {
                ...your move...
            }
        });
        idleTimer.startIdleTimer();

come controllare che l'utente sia inattivo ?? qualsiasi input dal sistema?
MohsinSyd

2

Durante la mia ricerca ho trovato molte risposte ma questa è la migliore risposta che ho avuto. Ma la limitazione di questo codice è che funziona solo per l'attività non per l'intera applicazione. Prendi questo come riferimento.

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*/);
}

ad esempio, hai utilizzato 8000, l'attività verrà eseguita dopo 8 secondi di inattività dell'utente.


2

L'inattività dell'utente può rilevare utilizzando il onUserInteraction()metodo di sostituzione in Android

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

    }

Ecco il codice di esempio, signout (HomeActivity -> LoginActivity) dopo 3 minuti quando l'utente non è attivo

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");

    }

}

2

Gestione dell'utente nel timeout di interazione in 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
}

1

Ecco una soluzione completa che gestisce l'inattività dell'utente dopo pochi minuti (ad esempio 3 minuti). Questo risolve i problemi comuni come l'attività che salta in primo piano quando l'app è in background al momento del timeout.

In primo luogo, creiamo una BaseActivity che tutte le altre attività possono estendere.

Questo è il codice 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();
        }

    }

}

Successivamente, creiamo un'interfaccia per il nostro "Listener di disconnessione"

package com.example.timeout;

public interface LogoutListener {

    void onSessionLogout();

}

Infine, creiamo una classe Java che estende "Application"

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: non dimenticare di aggiungere la classe "TimeOutApp" al tag dell'applicazione all'interno del file manifest

<application
        android:name=".TimeOutApp">
        </application>

0

Penso che debba essere combinando il timer con l'ora dell'ultima attività.

Quindi in questo modo:

  1. In onCreate (Bundle savedInstanceState) avvia un timer, diciamo 5 minuti

  2. In onUserInteraction () memorizza solo l'ora corrente

Finora abbastanza semplice.

Ora quando il timer pop fa così:

  1. Prendi l'ora corrente e sottrai il tempo di interazione memorizzato per ottenere timeDelta
  2. Se timeDelta è> = i 5 minuti, hai finito
  3. Se timeDelta è <i 5 minuti, riavvia il timer, ma questa volta usa 5 minuti - il tempo memorizzato. In altre parole, 5 minuti dall'ultima interazione

0

Ho avuto una situazione simile alla domanda SO, in cui dovevo tenere traccia dell'inattività dell'utente per 1 minuto, quindi reindirizzare l'utente per avviare l'attività, dovevo anche cancellare lo stack delle attività.

Sulla base della risposta di @gfrigon, ho trovato questa soluzione.

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();
    }
}

Risorse complementari

Android: cancella stack di attività

Questa classe Handler dovrebbe essere statica o potrebbero verificarsi perdite


0

La cosa migliore è gestirlo nell'intera app (supponendo che tu abbia più attività) registrandoti AppLifecycleCallbacksin Application calss. È possibile utilizzare registerActivityLifecycleCallbacks()nella classe Application con i seguenti callback (consiglio di creare una classe AppLifecycleCallbacks che estende 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);
}

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)
    }
}

Estendi la tua attività con

YourActivity:SubActivity(){}

per arrivare a MainActivity quando l'utente è inattivo dopo 3000 millisec su YourActivity

Ho usato una risposta precedente e l'ho convertita in kotlin.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.