Как обнаружить неактивность пользователя в Android
Пользователь запускает мое приложение и входит в систему.
Выбирает время ожидания сеанса 5 минут.
Делает некоторые операции над приложением. (все на переднем плане)
Теперь пользователь переводит Myapp в фоновый режим и запускает другое приложение.
----> Таймер обратного отсчета запускается и выходит из системы через 5 минут
ИЛИ пользователь выключает экран.
----> Таймер обратного отсчета запускается и выходит из системы через 5 минут
Мне нужно такое же поведение, даже когда приложение находится на переднем плане, но пользователь долгое время не взаимодействует с приложением, скажем, 6-7 минут. Предположим, что экран включен все время. Я хочу определить тип неактивности пользователя (нет взаимодействия с приложением, даже если приложение находится на переднем плане) и запустить таймер обратного отсчета.
21 ответ
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);
}
}
Я нашел решение, которое я считаю довольно простым, основываясь на ответе Фредрика Валлениуса. Это базовый класс деятельности, который должен быть расширен всеми действиями.
public class MyBaseActivity extends Activity {
public static final long DISCONNECT_TIMEOUT = 300000; // 5 min = 5 * 60 * 1000 ms
private Handler disconnectHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// todo
return true;
}
});
private Runnable disconnectCallback = new Runnable() {
@Override
public void run() {
// Perform any required operation on disconnect
}
};
public void resetDisconnectTimer(){
disconnectHandler.removeCallbacks(disconnectCallback);
disconnectHandler.postDelayed(disconnectCallback, DISCONNECT_TIMEOUT);
}
public void stopDisconnectTimer(){
disconnectHandler.removeCallbacks(disconnectCallback);
}
@Override
public void onUserInteraction(){
resetDisconnectTimer();
}
@Override
public void onResume() {
super.onResume();
resetDisconnectTimer();
}
@Override
public void onStop() {
super.onStop();
stopDisconnectTimer();
}
}
Я не знаю, как отслеживать неактивность, но есть способ отслеживать активность пользователей. Вы можете поймать обратный звонок под названием onUserInteraction()
в вашей деятельности, которая вызывается каждый раз, когда пользователь выполняет какое-либо взаимодействие с приложением. Я бы предложил сделать что-то вроде этого:
@Override
public void onUserInteraction(){
MyTimerClass.getInstance().resetTimer();
}
Если ваше приложение содержит несколько действий, почему бы не поместить этот метод в абстрактный суперкласс (расширяющий Activity
) и тогда все ваши действия расширяют его.
Я думаю, что вы должны пойти с этим кодом, это для 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
}
@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));
}
На уровне ОС не существует понятия "бездействия пользователя", кроме ACTION_SCREEN_OFF
а также ACTION_USER_PRESENT
трансляций. Вам придется как-то определить "неактивность" в вашем собственном приложении.
Лучше поздно, чем никогда
Даже вы можете управлять своими требованиями с помощью решений @gfrigon или @AKh.
Но я думал о некоторых бесплатных решениях Timer и Handlers для этого. У меня уже есть хорошо управляемое решение для этого. Но я успешно внедрил бесплатное решение Timer и Handler.
Сначала я расскажу вам, что вам нужно делать, если вы используете Timer или Handlers.
- Если ваше приложение убито пользователем или оптимизатором, оно никогда не выйдет из системы автоматически, потому что все ваши обратные вызовы уничтожены. (Управлять каким-нибудь Alarm Manager или сервисом?)
- Хорошо ли иметь таймер в каждом базовом классе? Вы создаете много потоков только для вызова процесса выхода из системы (Управление статическим обработчиком или таймером на уровне приложения?).
- Что если пользователь находится в фоновом режиме, ваш обработчик запустит активность входа в систему, если пользователь выполняет какую-то другую работу вне вашего приложения. (Управление передним планом или фоном приложения?).
- Что делать, если экран отключается автоматически. (Управлять выключением экрана на приемнике вещания?)
Наконец я реализовал решение, которое
- Вам не нужно использовать Hander или Timer.
- Вам не нужно использовать Alarm Manager.
- Вам не нужно использовать App Lifecycle.
- Вам не нужно использовать
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.
}
}
Во время поиска я нашел много ответов, но это лучший ответ, который я получил. Но ограничением этого кода является то, что он работает только для активности, а не для всего приложения. Возьмите это как ссылку.
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 секунд бездействия пользователя.
Обработка пользователя во время ожидания взаимодействия в 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 reset() {
timeoutHandler!!.removeCallbacks(interactionTimeoutRunnable);
timeoutHandler!!.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second
}
// start countdown
fun startHandler() {
timeoutHandler!!.postDelayed(interactionTimeoutRunnable, 10*1000); //for 10 second
}
Пользователь бездействия может обнаружить с помощью 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");
}
}
В базовом классе своей деятельности я создал защищенный класс:
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();
Вот полное решение, которое обрабатывает бездействие пользователя через несколько минут (например, 3 минуты). Это решает общие проблемы, такие как переход активности на передний план, когда приложение находится в фоновом режиме по истечении времени ожидания.
Во-первых, мы создаем BaseActivity, которую могут расширять все остальные Activity.
Это код 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();
}
}
}
Затем мы создаем интерфейс для нашего "Logout Listener".
package com.example.timeout;
public interface LogoutListener {
void onSessionLogout();
}
Наконец, мы создаем класс Java, который расширяет "Приложение"
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>
Лучше всего справиться с этим во всем приложении (если у вас есть несколько действий), зарегистрировавшись AppLifecycleCallbacks
в приложении кальс. Ты можешь использовать registerActivityLifecycleCallbacks()
в классе Application со следующими обратными вызовами (я рекомендую создать класс 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);
}
Я знаю, что уже слишком поздно, чтобы добавить этот ответ, но это для всех новых зрителей впереди, я прошел большинство кодов выше и попробовал себя. Но никто из них не работал для меня. Приведенный ниже код работает именно так, как будет искать новый зритель. Этот код будет работать:
public class MainActivity extends AppCompatActivity {
private Timer timer;
private Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
@Override
protected void onPause() {
super.onPause();
timer = new Timer();
Log.i("Main", "Invoking logout timer");
LogOutTimerTask logoutTimeTask = new LogOutTimerTask();
timer.schedule(logoutTimeTask, 300000); //auto logout in 5 minutes
}
@Override
protected void onResume() {
super.onResume();
if (timer != null) {
timer.cancel();
Log.i("Main", "cancel timer");
timer = null;
}
}
private class LogOutTimerTask extends TimerTask {
@Override
public void run() {
//redirect user to login screen
Intent i = new Intent(MainActivity.this, YourActivity.class); //where
you want to load when inactive
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
}
}
Итак, основываясь на верхнем ответе и комментариях:
public class SActivity extends AppCompatActivity {
public static Runnable dcCallback = null;
public static long dcTimeout = 60000;
private static Handler dcHandler = new Handler();
public static void resetDisconnectTimer() {
dcHandler.removeCallbacks(dcCallback);
dcHandler.postDelayed(dcCallback, dcTimeout);
}
public static void stopDisconnectTimer() {
dcHandler.removeCallbacks(dcCallback);
}
@Override
public void onUserInteraction() {
resetDisconnectTimer();
}
@Override
public void onResume() {
super.onResume();
resetDisconnectTimer();
}
}
Вот некоторые случаи, которые, я думаю, стоит упомянуть:
- AlertDialog показывает и
onUserInteraction()
больше не работает? -> Поскольку мы сделалиresetDisconnectTimer()
static, мы можем просто вызвать это в любом месте нашей деятельности (например, вам нужно добавитьaddTextChangedListener
в EditTexts вашего AlertDialog и вызовите его там) - Несколько экземпляров вашего изображения отображаются после автоматической блокировки приложения? или в вашем приложении есть опция, позволяющая пользователю заблокировать приложение вручную? -> как и в предыдущем случае, вызовите
stopDisconnectTimer()
перед запуском. - Не забудьте спроектировать свой
LockscreenActivity
каким-то образом проверяется, заблокировано ли приложение внутри или пользователь впервые пытается войти в систему; и если оно заблокировано самим приложением, переопределитеonBackPressed
и закройте приложение, чтобы не столкнуться с мусорной безопасностью: P Вот пример того, как это сделать:
// In LockscreenActitivty's onCreate:
innerLock = getIntent().getBooleanExtra("innerLock", false);
@Override
public void onBackPressed() {
if (innerLock) finishAffinity();
else super.onBackPressed();
}
Таким образом, в основном расширьте приведенный выше класс на все ваши действия, которые вы хотите защитить (за исключением экрана блокировки). И помните, поскольку мы сделали Runnable статическим, мы должны инициализировать его с помощью нашего самого первого расширяющегося действия (того, которое открывается после входа пользователя в систему). Это будет выглядеть примерно так:
dcCallback = () -> {
stopDisconnectTimer();
startActivity(new Intent(context, LockscreenActivity.class).putExtra("innerLock", true))
};
И вы можете получить доступ и изменитьdcTimeout
непосредственно в любой деятельности, которая простираетсяSActivity
Хотя может быть уже слишком поздно и могут быть решения получше этого, но надеюсь, что это кому-то поможет :)
Настоящий способ
Вы можете использовать этот метод, чтобы определить, как долго пользователь был неактивен (даже когда приложение находится в фоновом режиме).
- Создать
SharedPreference
& его объект Editor. Затем объявите 3 длинных варибала, например:
mMillisUntilFinished = pref.getLong("millisUntilFinished",60*1000); // Replace with your time
long userExitedMillis = pref.getLong("userExitedMillis",0);
long timeLeft = mMillisUntilFinished - (System.currentTimeMillis() - userExitedMillis);
- Проходить
timeLeft
как millisInFuture. Внутри таймера назначьте millisUntilFinished общедоступной переменной в каждом тике.
new CountDownTimer(timeLeft,1000){
@Override
public void onTick(long millisUntilFinished) {
Log.d("TAG", "Time left : " + millisUntilFinished/1000 + " sec");
mMillisUntilFinished = millisUntilFinished;
}
@Override
public void onFinish() {
// Timer completed
}
}.start();
- Сохранить это
mMillisUntilFinished
переменная и текущее время в общих предпочтениях в onStop () .
@Override
protected void onStop() {
super.onStop();
editor.putLong("millisUntilFinished",mMillisUntilFinished);
editor.putLong("userExitedMillis",System.currentTimeMillis());
editor.apply();
}
Совет : если вычесть
userExitedMillis
из
System.currentTimeMillis()
тогда вы получите неактивное время активности (в миллисекундах) .
переопределите метод sendTouchEvent() в своей активности
, и вы будете получать уведомления о каждом касании в вашем приложении.
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
ev?.let { event ->
if (event.action == ACTION_DOWN) {
//start counting user's inactivity
//or launch a coroutine with delay before screen dimming
}
}
return super.dispatchTouchEvent(ev)
}
Я запустил сопрограмму с задержкой() перед неактивным действием оттуда
Решение для нескольких действий, которое можно использовать, даже если приложение находится в фоновом режиме.
Итак, я увидел, что принятый ответ поддерживает только 1 действие, а также, если пользователь сворачивает приложение, обратный вызов DC не происходит. Итак, я закончил тем, что изменил ответ gfrigon и исправил их:
public class SActivity extends AppCompatActivity {
private static Handler dcHandler = new Handler();
public static Runnable dcCallback = null;
public static long dcTimeout = 30000; // 30 seconds here for example, you can update it just by calling dcTimeout = x; in your activities that extend this class
public static boolean internalCause;
public static boolean dcRequestWhileInBackground;
public static boolean activityFocusGone;
public static void resetDisconnectTimer() {
dcHandler.removeCallbacks(dcCallback);
dcHandler.postDelayed(dcCallback, dcTimeout);
}
public static void stopDisconnectTimer() {
dcHandler.removeCallbacks(dcCallback);
}
@Override
public void onUserInteraction() {
resetDisconnectTimer();
}
@Override
protected void onPause() {
super.onPause();
activityFocusGone = true;
}
@Override
public void onResume() {
super.onResume();
resetDisconnectTimer();
activityFocusGone = false;
if (dcRequestWhileInBackground && !internalCause) {
dcRequestWhileInBackground = false;
dcCallback.run();
}
internalCause = false;
}
}
В основном, что вам нужно сделать, это:
- Расширьте все свои действия из вышеуказанного класса (кроме вашего)
- Настройка
dcCallback
в самом первом действии, которое открывается после (которое должно продолжатьсяSActivity
кстати)
dcCallback = () -> {
if (activityFocusGone)
SActivity.dcRequestWhileInBackground = true;
startActivity(new Intent(getApplicationContext(), Lockscreen.class));
};
- Поместив эти 2 строки во все остальные оставшиеся классы
onCreate
метод, который вы хотите защитить (чтобы избежать запуска несколькихLockscreenActivity
, а также предотвращение блокировки приложения после того, как пользователь переключает действия)
internalCause = true;
resetDisconnectTimer();
Возможно, уже слишком поздно, и могут быть лучшие решения, чем это, но надеюсь, что это кому-то поможет :)
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
Я использовал предыдущий ответ и преобразовал его в котлин.
Я думаю, что это должно быть, комбинируя таймер с последним временем активности.
Ну вот так:
В onCreate(Bundle saveInstanceState) запустите таймер, скажем, 5 минут
В onUserInteraction () просто сохраните текущее время
Довольно просто пока.
Теперь, когда таймер всплывет, сделайте так:
- Возьмите текущее время и вычтите сохраненное время взаимодействия, чтобы получить timeDelta
- Если timeDelta> = 5 минут, все готово
- Если timeDelta <5 минут, запустите таймер снова, но на этот раз используйте 5 минут - сохраненное время. Другими словами, 5 минут от последнего взаимодействия
У меня была похожая ситуация с вопросом SO, где мне нужно было отслеживать неактивность пользователя в течение 1 минуты, а затем перенаправлять пользователя для запуска Activity, мне также нужно было очистить стек активности.
Основываясь на ответе @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: Очистить стек активности
Этот класс обработчика должен быть статическим, иначе могут возникнуть утечки