实现功能如下:在背景运行app,检测到自由落体状态时,熄灭屏幕,可重复测试。
1. 检测自由落体动作
需要使用到加速度感应器 TYPE_ACCELEROMETER
1SensorManager mSensorManager; 2private float mLastX; 3private float mLastY; 4private float mLastZ; 5private double force; 6 7 @Override 8 public void onCreate() { 9 super.onCreate(); 10 mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE); 11 mSensorManager.registerListener(sensorListener, 12 mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), 13 SensorManager.SENSOR_DELAY_GAME); 14 } 15 16 private final SensorEventListener sensorListener = new SensorEventListener() { 17 @Override 18 public void onSensorChanged(SensorEvent event) { 19 try { 20 if(event.sensor == null){ 21 return; 22 } 23 }catch (Exception ex){ 24 25 } 26 27 if(event.sensor.getType() == Sensor.TYPE_ACCELEROMETER){ 28 mLastX = event.values[0]; 29 mLastY = event.values[1]; 30 mLastZ = event.values[2]; 31 force = Math.sqrt(mLastX*mLastX+mLastY*mLastY+mLastZ*mLastZ); 32 33 } 34 if(force < 1 ){ 35 Log.i("Kunkka","force < 1 START-------------"); 36 new Handler().postDelayed(new Runnable() { 37 @Override 38 public void run() { 39 if (force < 1) { 40 Log.i("Kunkka","force < 1 END, SCREEN OFF"); 41 screenOff(); 42 } 43 } 44 }, 20); 45 } 46 } 47 48 @Override 49 public void onAccuracyChanged(Sensor sensor, int accuracy) { 50 51 } 52 };
标黄的是判断手机失重的依据,即三个方向的合力为0。由于不是那么精确,让其合力<1即可。
另外为了防止手机平时突然出现符合这个结果的,当第一次出现符合条件的合力时,延迟20ms再检测一次,(最好连续检测多次来确定连续处于失重状态),假如依旧符合失重,就认定此时在失重状态。再做下一步的处理。
2. 关闭屏幕
熄灭屏幕代码:
1private void screenOff(){ 2 DevicePolicyManager policyManager = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE); 3 ComponentName adminReceiver = new ComponentName(DeviceFallDetectService.this, MyAdminReceiver.class); 4 boolean admin = policyManager.isAdminActive(adminReceiver); 5 if (admin) { 6 policyManager.lockNow(); 7 } else { 8 Toast.makeText(this,"没有设备管理权限", 9 Toast.LENGTH_LONG).show(); 10 } 11}
息屏主要使用的是DevicePolicyManager 类,此外MyAdminReceiver是一个息屏管理的广播接收器,该接受器非常重要,系统设置中正是通过该接收器才找到的应用程序,该广播接收器在AndroidManifest.xml中的声明如下:
1<receiver 2 android:name=".MyAdminReceiver" 3 android:permission="android.permission.BIND_DEVICE_ADMIN"> 4 <meta-data 5 android:name="android.app.device_admin" 6 android:resource="@xml/admin"/> 7 8 <intent-filter> 9 <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/> 10 </intent-filter> 11</receiver> 12 13xml/admin.xml文件内容如下: 14 15<?xml version="1.0" encoding="utf-8"?> 16<device-admin xmlns:android="http://schemas.android.com/apk/res/android" > 17 <uses-policies> 18 <force-lock /> 19 </uses-policies> 20</device-admin>
Receiver的源码空的就可以,只需要继承DeviceAdminReceiver:
1public class MyAdminReceiver extends DeviceAdminReceiver { 2 3}
网上说还需要加权限
<uses-permission android:name="android.permission.USES_POLICY_FORCE_LOCK" />
但我没加去运行也没出什么问题。
3. Forground service实现重复运行
由于运行在background service中的话,屏幕关了再打开,好像background service就被停下了。
为了每次屏幕亮了都可以继续运行,把service改成O之后的foreground service:
首先startservice的地方,改成foreground方式启动:
1Intent intent = new Intent(MainActivity.this, 2 DeviceFallDetectService.class); 3startForegroundService(intent); 4MainActivity.this.finish();
然后在service的onStartCommand中,立刻声明startForeground。
并且android O 以后每个Notification都需要依附一个channel,要不然就报错。加一个简单的channel:
1 @Override 2 public int onStartCommand(Intent intent, int flags, int startId) { 3 initChanel(); 4 return super.onStartCommand(intent, flags, startId); 5 } 6 7 private void initChanel(){ 8 NotificationChannel channel = new NotificationChannel(CHANNEL_ID,"FallDetect", 9 NotificationManager.IMPORTANCE_HIGH); 10 11 NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 12 manager.createNotificationChannel(channel); 13 14 Notification notification = new Notification.Builder(getApplicationContext(),CHANNEL_ID).build(); 15 startForeground(3210, notification); 16 }
大功告成