Como detectar a inatividade do usuário no Android


101

O usuário inicia meu aplicativo e faz login.
Seleciona o tempo limite da sessão como 5 minutos.
Realiza algumas operações no aplicativo. (tudo em primeiro plano)
Agora o usuário traz Myapp para segundo plano e inicia algum outro aplicativo.
----> O cronômetro de contagem regressiva é iniciado e desconecta o usuário após 5 minutos
OU o usuário DESLIGA a tela.
----> O cronômetro de contagem regressiva inicia e desconecta o usuário após 5 minutos

Desejo o mesmo comportamento mesmo quando o aplicativo está em primeiro plano, mas o usuário não interage com o aplicativo por um longo tempo, digamos de 6 a 7 minutos. Suponha que a tela esteja LIGADA o tempo todo. Desejo detectar o tipo de inatividade do usuário (sem interação com o aplicativo, embora o aplicativo esteja em primeiro plano) e iniciar meu cronômetro de contagem regressiva.


1
Você poderia sempre ter esse cronômetro em execução e reiniciá-lo sempre que o usuário fizer algo?
Kyle P

Respostas:


111

Eu encontrei uma solução que considero bastante simples com base na resposta de Fredrik Wallenius. Esta é uma classe de atividade básica que precisa ser estendida por todas as atividades.

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
Isso criará várias instâncias de Handlere Runnablepara cada Activitycriado. Se convertermos esses dois membros para static, isso será evitado. Além disso, você poderia me dizer por que você chamou stopDisconnectTimer()no onStop()`?
Gaurav Bhor

@Gaurav No meu caso, isso só é implementado em uma atividade (portanto, não peguei o problema com o staticmodificador). Quanto ao onStop(), pelo que me lembro, chamo onBackPressed()para retornar a uma tela de login no callback de desconexão que por sua vez chama o onStop()método. Quando o utilizador regressa à tela de login manualmente, pressionando para trás, as necessidades de temporizador para ser interrompido, bem assim o stopDisconnectTimer()em onStop(). Acho que essa parte depende de suas necessidades e implementação.
gfrigon

@gfrigon é possível redirecionar o usuário para a atividade de login?
Apostrofix

@Apostrifix, claro que é possível. No meu caso, havia apenas uma atividade: chamar onBackPressed()vas suficiente. Se você tiver mais de uma atividade em sua pilha, basta criar um intent para esse assunto. Você pode querer olhar a seguinte resposta para limpar a tarefa Activity (e evitar que os usuários se reconectem): stackoverflow.com/questions/7075349/…
gfrigon

Ótimo trabalho! Eu adicionei getter e setter para o executável e, em seguida, configurei-o na classe de extensão conforme necessário usando o método onCreate ... perfeito, novamente obrigado.
CrandellWS

90

Não conheço uma maneira de rastrear a inatividade, mas existe uma maneira de rastrear a atividade do usuário. Você pode capturar um retorno de chamada chamado onUserInteraction()em suas atividades que é chamado sempre que o usuário faz qualquer interação com o aplicativo. Eu sugiro fazer algo assim:

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

Se seu aplicativo contém várias atividades, por que não colocar esse método em uma superclasse abstrata (estendendo Activity) e ter todas as atividades estendendo-o.


1
Sim, esta é uma maneira de fazer isso ... mas meu aplicativo tem 30 atividades diferentes e haveria muita interação quando o usuário está ativo ... então, cada vez que zerar o cronômetro, seria uma operação cara ... que no o pior caso pode ser de 50 a 60 vezes em um minuto.
Akh

3
Não cronometrei, mas diria que zera um cronômetro como este lastInteraction = System.currentTimeMillis (); levaria, digamos, 2 ms. Faça isso 60 vezes por minuto e você "perderá" 120 ms. Out of 60000.
Fredrik Wallenius

1
Fredrik ... Também estou usando sua sugestão para atender a este cenário. O tempo limite da tela está definido para no máximo 30 minutos no dispositivo. MyApp shd timeout após 15 minutos ... Se o usuário não tocar em nada na tela por mais de 1 minuto, então irei iniciar o temporizador de logout de 15 minutos .... Neste caso, eu verificaria a diferença (lastInteractionTime e System.currentTimeMills ( )) é mais de 1 min ... então dispare ..
Akh

3
onUserInteraction () não é chamado em alguns casos (as caixas de diálogo não o chamam e a rolagem nos botões giratórios) existe uma solução alternativa para essas situações?
AndroidNoob

você poderia compartilhar sua MyTimerClass?
Sibelius Seraphini de

19

Eu acho que você deveria ir com este código, isto é para tempo limite de sessão inativa 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 
}

Você salvou minha vida com 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 ===> a lógica não é mostrada aqui, pois está fora do escopo da questão

Você pode ativar o bloqueio da CPU usando o código do dispositivo abaixo

  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: Então, explique a maneira certa de fazer isso. Seu comentário é vago e indeciso.
Akh

2
@AKh: As outras respostas já mostram as possibilidades. Em sua solução, não vejo nenhum benefício em pesquisar a cada 15 segundos. Teria o mesmo efeito, pois você inicia um cronômetro em "ACTION_SCREEN_OFF" com uma duração aleatória de 0-15 segundos. Isso simplesmente não faz sentido ..
Nappy

1
@Nappy: a cada 15 segundos, não só verifico SCREEN_ON ou SCREEN_OFF, mas também o tempo da última interação do usuário e o status de primeiro plano do aplicativo. Com base nesses três fatores, tomo uma decisão lógica sobre o quão ativo o usuário está interagindo com o aplicativo.
Akh

Por favor, complete seu comentário. .... "se o seu isScreenof booleano for?" E também o status anterior do aplicativo deve ser levado em consideração.
Akh

1
Este código está cheio de erros, algumas variáveis ​​não foram inicializadas.
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));
}

Esta é a base da solução, o resto pode ser modificado dependendo de suas necessidades particulares e complexidade da arquitetura de aplicação! Obrigado pela resposta!
Hack06

Como aplicar isso na aula de aplicação
Gaju Kollur

6

Não existe o conceito de "inatividade do usuário" no nível do sistema operacional, além das transmissões ACTION_SCREEN_OFFe ACTION_USER_PRESENT. Você terá que definir "inatividade" de alguma forma em seu próprio aplicativo.


6

Até você pode gerenciar seus requisitos com @gfrigon ou @AKh soluções.

Mas aqui está a solução gratuita de cronômetro e manipuladores para isso. Já gerenciei bem a solução Timer para isso. Mas eu implementei com sucesso a solução gratuita Timer and Handler.

Em primeiro lugar, direi o que você deve gerenciar se usar Timer ou Handlers.

  • Se o seu aplicativo for encerrado pelo usuário ou por um otimizador, ele nunca será desconectado automaticamente, porque todos os seus callbacks são destruídos. ( Gerencia algum gerenciador de alarme ou serviço? )
  • É bom ter um cronômetro em todas as classes básicas? Você está criando muitos threads apenas para invocar o processo de logout ( Gerenciar manipulador estático ou cronômetro no nível do aplicativo? ).
  • E se o usuário estiver em segundo plano, seu manipulador iniciará a atividade de login se o usuário estiver fazendo algum outro trabalho fora do seu aplicativo. ( Gerenciar primeiro plano ou plano de fundo do aplicativo? ).
  • E se a tela desligar automaticamente. ( Gerenciar tela desligada no receptor de transmissão? )

Finalmente implementei uma solução que é

  1. SEM manipulador ou temporizador.
  2. SEM gerenciador de alarmes.
  3. NÃO gerencia o App LifeCycle.
  4. NÃO ACTION_SCREEN_ON/ ACTION_SCREEN_OFFReceptor de transmissão.

Solução confiável mais fácil

Não observaremos a inatividade do usuário por temporizadores, em vez de verificar o tempo da última atividade na atividade do usuário. Então, quando o usuário interagir com o aplicativo da próxima vez, eu verifico o tempo da última interação.

Aqui está o BaseActivity.classque você estenderá de todas as suas classes de atividades ao invés de LoginActivity. Você definirá seu tempo de logout no campo TIMEOUT_IN_MILLInesta aula.

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

Como acessar preferências compartilhadas no encadeamento principal em cada interação do usuário é melhor do que invocar vários encadeamentos.
Nishita

@Nishita no momento de postar esta resposta, eu não estava ciente dessa desvantagem. Obrigado por comentar sobre minha 1 má resposta. Você está certo, essa não é a maneira certa de fazer isso. Vou esconder essa resposta.
Khemraj

2

Na minha classe base de atividades, criei uma classe 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();
}

Portanto, no método onResume - você pode especificar a ação em seu retorno de chamada o que deseja fazer com ela ...

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

como verificar se o usuário está inativo ?? alguma entrada do sistema?
MohsinSyd

2

Durante a minha pesquisa encontrei muitas respostas, mas esta é a melhor resposta que recebi. Mas a limitação desse código é que ele funciona apenas para atividades, não para o aplicativo inteiro. Tome isso como referência.

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 exemplo, você usou 8000, a tarefa será realizada após 8 segundos de inatividade do usuário.


2

A inatividade do usuário pode detectar usando o onUserInteraction()método de substituição no Android

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

    }

Aqui está o código de amostra, sair (HomeActivity -> LoginActivity) após 3 minutos quando o usuário inativo

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

Lidando com o usuário em tempo limite de interação em 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

Aqui está uma solução completa que lida com a inatividade do usuário após alguns minutos (por exemplo, 3 minutos). Isso resolve os problemas comuns, como a atividade saltando para o primeiro plano quando o aplicativo está em segundo plano após o tempo limite.

Em primeiro lugar, criamos uma BaseActivity que todas as outras Activity podem estender.

Este é o 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 seguir, criamos uma interface para nosso "Logout Listener"

package com.example.timeout;

public interface LogoutListener {

    void onSessionLogout();

}

Finalmente, criamos uma classe Java que 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();
    }


}

Observação: não se esqueça de adicionar a classe "TimeOutApp" à tag do seu aplicativo dentro do arquivo de manifesto

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

0

Acho que precisa ser combinando o cronômetro com o tempo da última atividade.

Assim:

  1. Em onCreate (Bundle savedInstanceState) inicie um cronômetro, digamos 5 minutos

  2. Em onUserInteraction () apenas armazene a hora atual

Muito simples até agora.

Agora, quando o cronômetro estourar, faça assim:

  1. Pegue o tempo atual e subtraia o tempo de interação armazenado para obter timeDelta
  2. Se timeDelta for> = 5 minutos, você terminou
  3. Se timeDelta for <5 minutos, inicie o cronômetro novamente, mas desta vez use 5 minutos - o tempo armazenado. Em outras palavras, 5 minutos da última interação

0

Eu tive uma situação semelhante à questão do SO, onde eu precisava rastrear a inatividade do usuário por 1 minuto e, em seguida, redirecionar o usuário para iniciar a atividade, eu precisava também limpar a pilha de atividades.

Com base na resposta de @gfrigon, proponho esta solução.

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 complementares

Android: Limpar pilha de atividades

Esta classe Handler deve ser estática ou podem ocorrer vazamentos


0

A melhor coisa é lidar com isso em todo o aplicativo (supondo que você tenha várias atividades), registrando-se AppLifecycleCallbacksnos calss do aplicativo. Você pode usar registerActivityLifecycleCallbacks()na classe Application com os seguintes retornos de chamada (eu recomendo criar uma classe AppLifecycleCallbacks que estenda 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)
    }
}

Estenda sua atividade com

YourActivity:SubActivity(){}

para obter a MainActivity quando o usuário está inativo após 3.000 milissegundos em YourActivity

Usei uma resposta anterior e a converti para kotlin.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.