gpt4 book ai didi

android - Android 服务有没有办法检测设备何时被锁定?

转载 作者:行者123 更新时间:2023-11-29 15:44:26 26 4
gpt4 key购买 nike

我有一个 Android 服务,我想在设备锁定时采取行动。

我想澄清一下:

  • 我对屏幕开/关状态不感兴趣。
  • 我知道如何使用带有 Intent.ACTION_USER_PRESENT 和 KeyguardManager.inKeyguardRestrictedInputMode 的 BroadcastReceiver 来检查设备何时解锁。
  • 我对需要不断检查锁状态的解决方案不感兴趣。
  • 我不想自己锁定或解锁设备。
  • 我不能依赖前台的 Activity 来处理 onResume。

我添加这些要点是因为我发现无数人问同样的问题并得到其中一个回答。我很高兴他们这样做了,因为答案很有用和/或有教育意义,但它们不是我现在要找的。

最佳答案

我已经提出了一个可能的解决方案,但它有一些重要的注意事项。

一般方法:检测“屏幕关闭”事件,然后定期检查设备是否已锁定。这远非理想,但似乎没有任何方法可以检测设备何时被锁定。基本上,“没有正确的方法来做到这一点,所以你需要一起破解一些东西”。

信用:这是基于@Markus 在评论中的建议,结合来自链接问题答案的一些代码以及我自己的一些额外的繁重工作。

注意事项:

  • 其他制造商可能有不同的锁定期。
  • 设备政策(例如:Android for Work)可能会更改以在我们之前确定设备在该时间段内不会锁定(例如:设备可能会突然锁定并且我们在几分钟内检测不到)之后强制执行该时间段。<
  • Android 设备管理器可以远程锁定设备。
  • 设备可能被另一个应用程序锁定(例如:基于蓝牙的锁定机制)。
  • 未经测试,但我怀疑如果用户转动设备,此代码中会出现问题快速开关几次。
  • 未使用 Doze 进行测试。
  • 未经测试,但怀疑切换用户时可能会出现问题。
  • 我没有在愤怒中测试过这个,很可能还有其他问题。
  • 任何实际使用这种方法的人都应该做一些重新架构;下面介绍的只是概念证明。

AndroidManifest.xml

添加启动 Activity :

<activity android:name=".StartLockMonitorActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

添加一个广播接收器:

<receiver android:name=".StateReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

添加主要服务:

<service
android:name=".LockMonitor"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.sample.screenmonitor.LockMonitor.ACTION_START_SERVICE"/>
</intent-filter>
</service>

添加权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

res/values/styles.xml

添加透明样式:

<style name="Theme.Transparent" parent="android:Theme">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>

res/values/colors.xml

添加透明色:

<color name="transparent">#00000000</color>

StartLockMonitorActivity.java

这是主入口点,它只是启动服务:

package com.sample.screenmonitor;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

public class StartLockMonitorActivity extends AppCompatActivity {

public static final String TAG = "LockMonitor-SLM";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.w(TAG, "Starting service...");
final Intent newIntent = new Intent(this, LockMonitor.class);
newIntent.setAction(LockMonitor.ACTION_CHECK_LOCK);
startService(newIntent);
Toast.makeText(this, "Starting Lock Monitor Service", Toast.LENGTH_LONG).show();
finish();
}
}

StateReceiver.java

这会在设备重新启动时重新启动服务。服务第一次启动时,它会添加一些额外的过滤器(请参阅 LockMonitor.java 中的注释,其中描述了为什么 list 中没有这样做)。

package com.sample.screenmonitor;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class StateReceiver extends BroadcastReceiver {

public static final String TAG = "LockMonitor-SR";

@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "onReceive: redirect intent to LockMonitor");
final Intent newIntent = new Intent(context, LockMonitor.class);
newIntent.setAction(LockMonitor.ACTION_CHECK_LOCK);
newIntent.putExtra(LockMonitor.EXTRA_STATE, intent.getAction());
context.startService(newIntent);
}
}

LockMonitor.java

package com.sample.screenmonitor;

import android.app.KeyguardManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.util.Log;

import java.util.Timer;
import java.util.TimerTask;

public class LockMonitor extends Service {

public static final String TAG = "LockMonitor";

public static final String ACTION_CHECK_LOCK = "com.sample.screenmonitor.LockMonitor.ACTION_CHECK_LOCK";
public static final String EXTRA_CHECK_LOCK_DELAY_INDEX = "com.sample.screenmonitor.LockMonitor.EXTRA_CHECK_LOCK_DELAY_INDEX";
public static final String EXTRA_STATE = "com.sample.screenmonitor.LockMonitor.EXTRA_STATE";

BroadcastReceiver receiver = null;
static final Timer timer = new Timer();
CheckLockTask checkLockTask = null;

public LockMonitor() {
Log.d(TAG, "LockMonitor constructor");
}

@Override
public void onDestroy() {
Log.d(TAG, "LM.onDestroy");
super.onDestroy();

if (receiver != null) {
unregisterReceiver(receiver);
receiver = null;
}
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "LM.onStartCommand");

if (intent != null && intent.getAction() == ACTION_CHECK_LOCK) {
checkLock(intent);
}

if (receiver == null) {
// Unlike other broad casted intents, for these you CANNOT declare them in the Android Manifest;
// instead they must be registered in an IntentFilter.
receiver = new StateReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
registerReceiver(receiver, filter);
}

return START_STICKY;
}

void checkLock(final Intent intent) {
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);

final boolean isProtected = keyguardManager.isKeyguardSecure();
final boolean isLocked = keyguardManager.inKeyguardRestrictedInputMode();
final boolean isInteractive = powerManager.isInteractive();
final int delayIndex = getSafeCheckLockDelay(intent.getIntExtra(EXTRA_CHECK_LOCK_DELAY_INDEX, -1));
Log.i(TAG,
String.format("LM.checkLock with state=%s, isProtected=%b, isLocked=%b, isInteractive=%b, delay=%d",
intent != null ? intent.getStringExtra(EXTRA_STATE) : "",
isProtected, isLocked, isInteractive, checkLockDelays[delayIndex])
);

if (checkLockTask != null) {
Log.i(TAG, String.format("LM.checkLock: cancelling CheckLockTask[%x]", System.identityHashCode(checkLockTask)));
checkLockTask.cancel();
}

if (isProtected && !isLocked && !isInteractive) {
checkLockTask = new CheckLockTask(this, delayIndex);
Log.i(TAG, String.format("LM.checkLock: scheduling CheckLockTask[%x] for %d ms", System.identityHashCode(checkLockTask), checkLockDelays[delayIndex]));
timer.schedule(checkLockTask, checkLockDelays[delayIndex]);
} else {
Log.d(TAG, "LM.checkLock: no need to schedule CheckLockTask");
if (isProtected && isLocked) {
Log.e(TAG, "Do important stuff here!");
}
}
}

static final int SECOND = 1000;
static final int MINUTE = 60 * SECOND;
// This tracks the deltas between the actual options of 5s, 15s, 30s, 1m, 2m, 5m, 10m
// It also includes an initial offset and some extra times (for safety)
static final int[] checkLockDelays = new int[] { 1*SECOND, 5*SECOND, 10*SECOND, 20*SECOND, 30*SECOND, 1*MINUTE, 3*MINUTE, 5*MINUTE, 10*MINUTE, 30*MINUTE };
static int getSafeCheckLockDelay(final int delayIndex) {
final int safeDelayIndex;
if (delayIndex >= checkLockDelays.length) {
safeDelayIndex = checkLockDelays.length - 1;
} else if (delayIndex < 0) {
safeDelayIndex = 0;
} else {
safeDelayIndex = delayIndex;
}
Log.v(TAG, String.format("getSafeCheckLockDelay(%d) returns %d", delayIndex, safeDelayIndex));
return safeDelayIndex;
}

class CheckLockTask extends TimerTask {
final int delayIndex;
final Context context;
CheckLockTask(final Context context, final int delayIndex) {
this.context = context;
this.delayIndex = delayIndex;
}
@Override
public void run() {
Log.i(TAG, String.format("CLT.run [%x]: redirect intent to LockMonitor", System.identityHashCode(this)));
final Intent newIntent = new Intent(context, LockMonitor.class);
newIntent.setAction(ACTION_CHECK_LOCK);
newIntent.putExtra(EXTRA_CHECK_LOCK_DELAY_INDEX, getSafeCheckLockDelay(delayIndex + 1));
context.startService(newIntent);
}
}

@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "LM.onBind");
return null;
}
}

关于android - Android 服务有没有办法检测设备何时被锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35139125/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com