Jak wykryć brak aktywności użytkownika w systemie Android


101

Użytkownik uruchamia moją aplikację i loguje się.
Ustawia limit czasu sesji na 5 minut.
Wykonuje pewne operacje w aplikacji. (wszystko na pierwszym planie)
Teraz użytkownik przełącza Myapp w tło i uruchamia inną aplikację.
----> Odliczanie czasu uruchamia się i wylogowuje użytkownika po 5 minutach
LUB użytkownik wyłącza ekran.
----> Odliczanie czasu uruchamia się i wylogowuje użytkownika po 5 minutach

Chcę tego samego zachowania, nawet gdy aplikacja jest na pierwszym planie, ale użytkownik nie wchodzi w interakcję z aplikacją przez długi czas, powiedzmy 6-7 minut. Załóżmy, że ekran jest włączony przez cały czas. Chcę wykryć rodzaj braku aktywności użytkownika (brak interakcji z aplikacją, mimo że aplikacja jest na pierwszym planie) i uruchomić odliczanie czasu.


1
Czy mógłbyś zawsze mieć uruchomiony licznik czasu i resetować go, gdy użytkownik coś zrobi?
Kyle P

Odpowiedzi:


112

Na podstawie odpowiedzi Fredrika Walleniusa wymyśliłem rozwiązanie, które uważam za całkiem proste. Jest to podstawowa klasa działań, która musi zostać rozszerzona o wszystkie działania.

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
Spowoduje to utworzenie wielu wystąpień Handleri Runnabledla każdego Activityutworzonego. Jeśli zmienimy te dwa elementy na static, zostanie to uniknięte. Również może mi pan powiedzieć, dlaczego pan nazywa stopDisconnectTimer()się onStop()`?
Gaurav Bhor

@Gaurav W moim przypadku jest to zaimplementowane tylko w jednym działaniu (dlatego nie złapałem problemu z staticmodyfikatorem). Jeśli chodzi o to onStop(), z tego co pamiętam, wzywam onBackPressed()w celu powrotu do ekranu logowania w wywołaniu zwrotnym rozłączenia, które z kolei wywołuje onStop()metodę. Gdy użytkownik powraca do ekranu logowania ręcznie, wciskając z powrotem, potrzeby czasowe być zatrzymany, a tym samym stopDisconnectTimer()w onStop(). Myślę, że ta część zależy od Twoich potrzeb i realizacji.
gfrigon

@gfrigon czy można przekierować użytkownika do aktywności logowania?
Apostrofix

@Apostrifix, oczywiście, że jest to możliwe. W moim przypadku była tylko jedna czynność: onBackPressed()wystarczy wezwanie was . Jeśli masz więcej niż jedną aktywność w swoim stosie, musisz po prostu stworzyć intencję w tym zakresie. Możesz spojrzeć na następującą odpowiedź, aby wyczyścić zadanie działania (i zapobiec ponownemu łączeniu się użytkowników z tyłu): stackoverflow.com/questions/7075349/ ...
gfrigon

Świetna robota! Dodałem getter i setter dla runnable, a następnie ustawiłem go w klasie rozszerzającej zgodnie z potrzebą, używając metody onCreate ... doskonale, jeszcze raz dziękuję.
CrandellWS

91

Nie znam sposobu śledzenia braku aktywności, ale jest sposób na śledzenie aktywności użytkownika. Możesz przechwycić wywołanie zwrotne onUserInteraction()w działaniach, które jest wywoływane za każdym razem, gdy użytkownik wykonuje jakąkolwiek interakcję z aplikacją. Proponuję zrobić coś takiego:

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

Jeśli Twoja aplikacja zawiera kilka działań, dlaczego nie umieścić tej metody w abstrakcyjnej superklasie (rozszerzającej Activity), a następnie wszystkie działania rozszerzające ją.


1
Tak, to jeden ze sposobów, aby to zrobić ... ale moja aplikacja ma 30 różnych działań i byłoby zbyt dużo interakcji, gdy użytkownik jest aktywny ... więc za każdym razem resetowanie licznika czasu byłoby kosztowną operacją ... co w najgorszy przypadek może 50 do 60 razy na minutę.
Ach,

3
Nie mierzyłem czasu, ale powiedziałbym, że zresetowałem licznik czasu, taki jak ten lastInteraction = System.currentTimeMillis (); zajęłoby, powiedzmy, 2 ms. Zrób to 60 razy na minutę i „stracisz” 120 ms. Z 60000.
Fredrik Wallenius

1
Fredrik ... Korzystam również z Twojej sugestii, aby spełnić ten scenariusz. Limit czasu ekranu jest ustawiony na maksymalnie 30 minut na urządzeniu. MyApp shd timeout po 15 minutach ... Jeśli użytkownik nie dotknie niczego na ekranie od ponad 1 minuty, uruchomię 15-minutowy licznik czasu wylogowania .... W tym przypadku sprawdzę różnicę (lastInteractionTime i System.currentTimeMills ( )) trwa dłużej niż 1 min ... potem odpalić ..
Ach,

3
Funkcja onUserInteraction () nie jest jednak wywoływana w niektórych przypadkach (okna dialogowe tego nie wywołują, a przewijanie w pokrętłach) czy istnieje obejście takich sytuacji?
AndroidNoob

czy możesz udostępnić swoją MyTimerClass?
Sibelius Seraphini

20

Myślę, że powinieneś przejść z tym kodem, to jest na 5 minut bezczynności sesji: ->

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 
}

Uratowałeś mi życie dzięki 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 ===> logika nie jest tutaj pokazana, ponieważ jest poza zakresem pytania

Możesz obudzić zamek do procesora, używając poniższego kodu urządzenia:

  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: W takim razie proszę wyjaśnij, jak to zrobić. Twój komentarz jest niejasny i niezdecydowany.
Ach,

2
@AKh: Pozostałe odpowiedzi już pokazują możliwości. W Twoim rozwiązaniu nie widzę żadnej korzyści z odpytywania co 15 sekund. Miałoby to taki sam efekt, jak uruchomienie licznika czasu na „ACTION_SCREEN_OFF” z losowym czasem trwania od 0 do 15 sekund. To po prostu nie ma sensu ...
Nappy

1
@Nappy: Co 15 sekund sprawdzam nie tylko SCREEN_ON lub SCREEN_OFF, ale także czas ostatniej interakcji użytkownika i status aplikacji na pierwszym planie. W oparciu o te trzy czynniki podejmuję logiczną decyzję o tym, jak aktywny użytkownik korzysta z aplikacji.
Ach,

Uzupełnij swój komentarz. .... „jeśli wartość logiczna isScreenof to?” Należy również wziąć pod uwagę stan zaawansowania aplikacji.
Ach,

1
Ten kod jest pełen błędów, niektóre zmienne nie są inicjalizowane.
Duże dziecko

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

To jest podstawa rozwiązania, resztę można modyfikować w zależności od konkretnych potrzeb i złożoności architektury aplikacji! Dziękuję za odpowiedź!
Hack06,

Jak zastosować to w klasie aplikacji
Gaju Kollur

6

Nie ma pojęcia „bezczynności użytkownika” na poziomie systemu operacyjnego poza rozgłoszeniami ACTION_SCREEN_OFFi ACTION_USER_PRESENT. Będziesz musiał jakoś zdefiniować „brak aktywności” we własnej aplikacji.


6

Nawet Ty możesz zarządzać swoimi wymaganiami dzięki rozwiązaniom @gfrigon lub @AKh .

Ale tutaj jest bezpłatne rozwiązanie Timer i Handlers do tego. Mam już dobrze zarządzane rozwiązanie Timer do tego. Ale udało mi się wdrożyć bezpłatne rozwiązanie Timer and Handler.

Najpierw powiem ci, czym musisz zarządzać, jeśli używasz timera lub handlerów.

  • Jeśli Twoja aplikacja zostanie zabita przez użytkownika lub optymalizator, nigdy nie wyloguje się ona automatycznie, ponieważ wszystkie wywołania zwrotne zostaną zniszczone. ( Zarządzasz menedżerem alarmów lub usługą? )
  • Czy dobrze jest mieć timer w każdej klasie bazowej? Tworzysz wiele wątków dla samego wywołania procesu wylogowania ( Zarządzaj statyczną obsługą lub zegarem na poziomie aplikacji? ).
  • Co się stanie, jeśli użytkownik jest w tle, Twój Handler rozpocznie aktywność logowania, jeśli użytkownik wykonuje inną pracę poza aplikacją. ( Zarządzaj pierwszym planem lub tłem aplikacji? ).
  • Co jeśli ekran wyłączy się automatycznie? (Czy wyłączyć ekran w odbiorniku telewizyjnym? )

W końcu wdrożyłem rozwiązanie, którym jest

  1. NO Handler lub Timer.
  2. NO Menedżer alarmów.
  3. NIE zarządza cyklem życia aplikacji.
  4. NIE ACTION_SCREEN_ON/ ACTION_SCREEN_OFFOdbiornik transmisji.

Najłatwiejsze niezawodne rozwiązanie

Nie będziemy obserwować braku aktywności użytkownika na podstawie liczników czasu, zamiast sprawdzać czas ostatniej aktywności na aktywności użytkownika. Kiedy więc następnym razem aplikacja interakcji użytkownika, sprawdzam czas ostatniej interakcji.

Oto, BaseActivity.classco będziesz rozszerzać z każdej klasy aktywności zamiast LoginActivity. Czas wylogowania określisz w polu TIMEOUT_IN_MILLIw tej klasie.

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

1
W jaki sposób dostęp do wspólnych preferencji w głównym wątku przy każdej interakcji użytkownika jest lepszy niż wywoływanie wielu wątków.
Nishita,

@Nishita w momencie wysyłania tej odpowiedzi nie byłem świadomy tej wady. Dziękuję za skomentowanie mojej 1 złej odpowiedzi. Masz rację, to nie jest właściwy sposób. Ukryję tę odpowiedź.
Khemraj

2

W mojej klasie bazowej aktywności utworzyłem klasę chronioną:

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

Więc w metodzie onResume - możesz określić akcję w swoim wywołaniu zwrotnym, co chcesz z nią zrobić ...

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

jak sprawdzić, czy użytkownik jest nieaktywny? jakieś wejście z systemu?
MohsinSyd

2

Podczas wyszukiwania znalazłem wiele odpowiedzi, ale to najlepsza odpowiedź, jaką uzyskałem. Ale ograniczenie tego kodu polega na tym, że działa on tylko dla aktywności, a nie dla całej aplikacji. Potraktuj to jako punkt odniesienia.

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

np. użyłeś 8000, zadanie zostanie wykonane po 8 sekundach braku aktywności użytkownika.


2

Brak aktywności użytkownika można wykryć za pomocą onUserInteraction()metody zastępowania w systemie Android

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

    }

Oto przykładowy kod, wyloguj się (HomeActivity -> LoginActivity) po 3 minutach, gdy użytkownik jest nieaktywny

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

Obsługa limitu czasu interakcji użytkownika w 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

Oto kompletne rozwiązanie, które obsługuje brak aktywności użytkownika po kilku minutach (np. 3 minutach). Rozwiązuje to typowe problemy, takie jak przeskakiwanie aktywności na pierwszy plan, gdy aplikacja jest w tle po przekroczeniu limitu czasu.

Po pierwsze, tworzymy BaseActivity, które może rozszerzać wszystkie inne Activity.

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

    }

}

Następnie tworzymy interfejs dla naszego „Odbiornika wylogowania”

package com.example.timeout;

public interface LogoutListener {

    void onSessionLogout();

}

Na koniec tworzymy klasę Java, która rozszerza „Aplikację”

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


}

Uwaga: nie zapomnij dodać klasy „TimeOutApp” do tagu aplikacji w pliku manifestu

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

0

Myślę, że musi to być połączenie timera z czasem ostatniej aktywności.

Więc tak:

  1. W onCreate (pakiet zapisanyInstanceState) uruchom licznik, powiedzmy 5 minut

  2. W onUserInteraction () po prostu zapisz aktualny czas

Jak dotąd całkiem proste.

Teraz, gdy wyskakuje timer, wykonaj następujące czynności:

  1. Weź bieżący czas i odejmij przechowywany czas interakcji, aby uzyskać timeDelta
  2. Jeśli timeDelta wynosi> = 5 minut, to wszystko
  3. Jeśli timeDelta jest <5 minut, uruchom ponownie stoper, ale tym razem użyj 5 minut - zapisanego czasu. Innymi słowy, 5 minut od ostatniej interakcji

0

Miałem podobną sytuację do pytania SO, gdzie musiałem śledzić brak aktywności użytkownika przez 1 minutę, a następnie przekierować użytkownika do rozpoczęcia działania, musiałem również wyczyścić stos aktywności.

Na podstawie odpowiedzi @gfrigon wymyślam to rozwiązanie.

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

Zasoby uzupełniające

Android: Wyczyść stos aktywności

Ta klasa obsługi powinna być statyczna, w przeciwnym razie mogą wystąpić wycieki


0

Najlepiej jest załatwić to w całej aplikacji (zakładając, że masz wiele działań), rejestrując się AppLifecycleCallbacksw plikach Application. Możesz użyć registerActivityLifecycleCallbacks()w klasie Application z następującymi wywołaniami zwrotnymi (polecam utworzenie klasy AppLifecycleCallbacks, która rozszerza 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)
    }
}

Rozszerz swoją aktywność dzięki

YourActivity:SubActivity(){}

aby dostać się do MainActivity, gdy użytkownik jest nieaktywny po 3000 milisekundach YourActivity

Użyłem poprzedniej odpowiedzi i przekonwertowałem ją na kotlin.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.