Як виявити бездіяльність користувача в Android


101

Користувач запускає мою програму та входить у систему.
Вибирає час очікування на сеанс 5 хвилин.
Деякі операції над додатком. (все на передньому плані)
Тепер Користувач виводить Myapp на другий план і запускає якусь іншу програму.
----> Відлік таймера запускається та виходить із системи через 5 хвилин
АБО користувач вимикає екран.
----> Відлік таймера починається та виходить із системи через 5 хвилин

Я хочу, щоб ця поведінка була навіть у тому випадку, коли програма перебуває на передньому плані, але користувач не взаємодіє з програмою протягом тривалого часу, скажімо, 6-7 хвилин. Припустимо, що екран весь час увімкнено. Я хочу виявити вид бездіяльності користувача (Немає взаємодії з додатком, навіть якщо програма знаходиться на передньому плані) і натиснути запуск мого таймера зворотного відліку.


1
Чи завжди у вас може працювати цей таймер і скидати його, коли користувач щось робить?
Кайл П

Відповіді:


112

Я придумав рішення, яке вважаю досить простим на основі відповіді Фредріка Валленія. Це базовий клас діяльності, який потрібно розширити всіма видами діяльності.

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
Це створить кілька примірників Handlerі Runnableдля кожного Activityствореного. Якщо ми перетворимо ці два члени static, цього буде уникнути. Крім того , могли б ви сказати мені , чому ви назвали stopDisconnectTimer()в onStop()`?
Gaurav Bhor

@Gaurav У моєму випадку це реалізується лише в одній діяльності (тому я не спіймав проблему з staticмодифікатором). Що стосується того onStop(), що я пам'ятаю, я дзвоню onBackPressed(), щоб повернутися до екрана входу в відключений зворотний виклик, який, в свою чергу, викликає onStop()метод. Коли користувач повертається до екрану входу в систему вручну, натиснувши тому, потреби таймер повинен бути зупинений, а отже, stopDisconnectTimer()в onStop(). Я думаю, ця частина залежить від ваших потреб та реалізації.
gfrigon

@gfrigon чи можна перенаправити користувача на активність входу?
Апострофікс

@Apostrifix, Звичайно, це можливо. У моєму випадку було лише одне заняття: викликати onBackPressed()васу достатньо. Якщо у вашому стеку є кілька дій, вам просто потрібно створити намір із цього питання. Ви можете подивитися на таку відповідь, щоб очистити завдання Activity (і заборонити користувачам повторного з'єднання на спині): stackoverflow.com/questions/7075349 / ...
gfrigon

Чудова робота! Я додав getter та setter для запуску, а потім встановив його у розширеному класі за потребою, використовуючи метод onCreate ... ідеально, ще раз дякую.
CrandellWS

90

Я не знаю способу відстеження бездіяльності, але є спосіб відстеження активності користувачів. Ви можете зафіксувати зворотний дзвінок, який викликається onUserInteraction()у вашій діяльності, який викликається кожен раз, коли користувач здійснює будь-яку взаємодію з програмою. Я б запропонував зробити щось подібне:

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

Якщо у вашій програмі є кілька видів діяльності, чому б не поставити цей метод у абстрактний суперклас (розширення Activity), а потім розпочати його всі дії.


1
Так, це один із способів зробити це ... але в моєму додатку є 30 різних видів діяльності, і було б занадто багато взаємодії, коли користувач активний ... тому кожен раз, коли скидання таймера може бути дорогою операцією ... що на найгірший випадок може 50 - 60 разів за хвилину.
Ах

3
Я ще не приурочив її, але скажу, що скидання таймера, як це lastInteraction = System.currentTimeMillis (); зайняло б, скажімо, 2 мс. Робіть це 60 разів на хвилину, і ви «розпушуєте» 120мс. З 60000.
Фредрік Валленіус

1
Фредрік ... Я також використовую вашу пропозицію для виконання цього сценарію. Час очікування екрана встановлено на пристрої максимум 30 хв. Час очікування MyApp shd через 15 хвилин ... Якщо користувач не торкається нічого на екрані більше 1 хвилини, тоді я запускаю 15-хвилинний таймер виходу .... У цьому випадку я перевіряю різницю (lastInteractionTime та System.currentTimeMills ( )) більше 1 хв ... то вогонь ..
Ах

3
onUserInteraction () не викликається в деяких випадках, однак (діалоги не називають це, і прокрутка в спінерах), чи існує вирішення цих ситуацій?
AndroidNoob

Ви можете поділитися своїм MyTimerClass?
Сібеліус Серафіні

19

Я думаю, ви повинні піти з цим кодом, це для 5-хвилинного очікування сеансу в режимі очікування: ->

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 
}

Ви врятували мені життя за допомогоюUUInInteraction
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 ===> тут не відображається логіка, оскільки вона виходить за межі питання

Ви можете розбудити блокування процесора, скориставшись кодом пристрою нижче,

  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: Тоді поясніть, як правильно це зробити. Ваш коментар розпливчастий і нерішучий.
Ах

2
@AKh: Інші відповіді вже показують можливості. У вашому рішенні я не бачу ніякої користі від опитування кожні 15 секунд. Це матиме такий же ефект, як ви запускаєте таймер на "ACTION_SCREEN_OFF" із випадковою тривалістю від 0-15 секунд. Це просто не має сенсу ..
Наппі

1
@Nappy: кожні 15 секунд я перевіряю не лише SCREEN_ON або SCREEN_OFF, а й час останньої взаємодії користувача та стан переднього плану програми. На основі цих трьох факторів я приймаю логічне рішення про те, наскільки активно користувач взаємодіє з додатком.
Ах

Будь ласка, доповніть свій коментар. .... "якщо ваш isScreenof булева?" А також потрібно враховувати статус програми програми.
Ах

1
Цей код повний помилок, деякі змінні не ініціалізуються.
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));
}

Це основа рішення, решта можна змінювати залежно від ваших особливих потреб та складності архітектури програми! Дякую за відповідь!
Hack06

Як застосувати це в класі додатків
Gaju Kollur

6

Не існує поняття "бездіяльність користувача" на рівні ОС, поза межами ACTION_SCREEN_OFFта ACTION_USER_PRESENTмовлення. Вам доведеться якось визначати "бездіяльність" у межах власної програми.


6

Навіть ви можете керувати своєю вимогою за допомогою рішень @gfrigon або @AKh .

Але ось для цього Timer and Handlers безкоштовне рішення . Я вже добре керував тимчасовим рішенням для цього. Але я успішно реалізував безкоштовне рішення для таймеру та обробника.

Спочатку я скажу вам, що вам потрібно керувати, якщо ви користуєтесь таймером або обробниками.

  • Якщо ваш додаток убитий користувачем або оптимізатором, ваш додаток ніколи не виходитиме автоматично, оскільки всі ваші зворотні дзвінки знищені. ( Керуйте деяким диспетчером тривог або службою? )
  • Чи добре мати таймер у кожному базовому класі? Ви створюєте багато потоків для лише виклику процесу виходу ( Керуйте статичним обробником або Таймером на рівні програми? ).
  • Що робити, якщо користувач перебуває у фоновому режимі, ваш обробник розпочне активність входу, якщо користувач виконує якусь іншу роботу поза вашим додатком. ( Керування переднім планом або фоном програми? ).
  • Що робити, якщо екран вимикається автоматично. ( Керування екраном вимкнено на приймачі широкомовної передачі? )

Нарешті я реалізував рішення, яке є

  1. НЕ Оброблювач або Таймер.
  2. NO Manager.
  3. НЕ керування додатком LifeCycle.
  4. НІ ACTION_SCREEN_ON/ ACTION_SCREEN_OFFТрансляція приймача.

Найпростіше надійне рішення

Ми не будемо спостерігати бездіяльність користувачів таймерами, а не перевірятимемо час останньої активності на активності користувача. Отже, коли користувач взаємодіє з додатком наступного разу, я перевіряю час останньої взаємодії.

Ось BaseActivity.classякий ви будете використовувати з кожного класу своєї діяльності, а не з нього LoginActivity. Ви визначите час свого виходу в поле TIMEOUT_IN_MILLIцього класу.

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

Як краще отримати доступ до спільних уподобань у головному потоці під час кожної взаємодії користувача, ніж викликати декілька потоків.
Нішита

@Nishita на момент опублікування цієї відповіді я не знав цього розчарування. Дякую, що коментуєте мою 1 погану відповідь. Ви маєте рацію, це не правда зробити це. Я приховую цю відповідь.
Хемраж

2

У своєму базовому класі діяльності я створив захищений клас:

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

Отже, у методі onResume - ви можете вказати дію у зворотному дзвінку, що ви хочете робити з ним ...

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

як перевірити користувач неактивний ?? будь-який вхід від системи?
MohsinSyd

2

Під час пошуку я знайшов багато відповідей, але це найкраща відповідь, яку я отримав. Але обмеження цього коду полягає в тому, що він працює лише для діяльності, а не для цілого застосування. Візьміть це як орієнтир.

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

наприклад, ви використовували 8000, завдання буде виконано через 8 секунд бездіяльності користувача.


2

Неактивність користувача можна виявити, використовуючи onUserInteraction()метод переосмислення в android

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

    }

Ось зразок коду, вихід (HomeActivity -> LoginActivity) через 3 хвилини, коли користувач неактивний

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

Керування користувачем у режимі очікування взаємодії в 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

Ось повне рішення, яке обробляє бездіяльність користувача через декілька хвилин (наприклад, 3 хвилини). Це вирішує поширені проблеми, такі як активність, стрибки на перший план, коли додаток перебуває у фоновому режимі.

По-перше, ми створюємо BaseActivity, яку може розширити вся інша діяльність.

Це код 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();
        }

    }

}

Далі ми створюємо інтерфейс для нашого "слухача виходу"

package com.example.timeout;

public interface LogoutListener {

    void onSessionLogout();

}

Нарешті, ми створюємо клас Java, який розширює "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();
    }


}

Примітка. Не забудьте додати клас "TimeOutApp" до тегу програми всередині файлу маніфесту

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

0

Я думаю, що це має бути, поєднуючи таймер з останнім часом активності.

Так ось так:

  1. У режимі onCreate (Пакет збереженийInstanceState) запустіть таймер, скажімо, 5 хвилин

  2. У режимі onUserInteraction () просто зберігайте поточний час

Досить простий поки що.

Тепер, коли таймер поп роби так:

  1. Візьміть поточний час і відніміть збережений час взаємодії, щоб отримати часDelta
  2. Якщо timeDelta дорівнює> = 5 хвилин, ви закінчите
  3. Якщо timeDelta <5 хвилин, запустіть таймер знову, але на цей раз використовуйте 5 хвилин - збережений час. Іншими словами, 5 хвилин формують останню взаємодію

0

У мене була подібна ситуація з питанням SO, коли мені потрібно було відстежувати бездіяльність користувача протягом 1 хвилини, а потім перенаправляти користувача, щоб почати Діяльність, мені також потрібно було очистити стек активності.

На основі @gfrigon відповіді я придумав це рішення.

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

Додаткові ресурси

Android: очистити стек активності

Цей клас обробника повинен стати статичним або можуть виникнути витоки


0

Найкраще це вирішувати в усьому додатку (якщо у вас є кілька дій), зареєструвавшись AppLifecycleCallbacksу кассі програми. Ви можете використовувати registerActivityLifecycleCallbacks()в класі додатків такі зворотні виклики (рекомендую створити клас AppLifecycleCallbacks, який розширює 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)
    }
}

Розширіть свою діяльність за допомогою

YourActivity:SubActivity(){}

дістатися до MainActivity, коли користувач неактивний після 3000 мілісек на YourActivity

Я використав попередню відповідь і перетворив її на котлін.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.