实现思路:
1、发送广播
WindowManagerService循环读取下面按键消息并分发给窗口,在消息分发前会在PhoneWindowManager.interceptKeyBeforeQueueing方法中进行消息的过滤。因此该实现方式为在消息分发前的interceptKeyBeforeQueueing方法中监听当前按键为音量下键,如果当前状态为锁屏状态,并按键为音量下键,且两次按键间隔时间小于800ms时发送广播--"com.custom.volume_down"
具体实现方式如下:
sys\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
java
private long lastTime = 0;
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
final int keyCode = event.getKeyCode();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
......
switch (keyCode) {
......
case KeyEvent.KEYCODE_VOLUME_DOWN:
// add start
String volume_down = SystemProperties.get("persist.sys.double.volume_down");
if (DEBUG_INPUT) {
Log.i(TAG, "interceptKeyBeforeQueueing:"
+ " VOLUME KEYCODE_VOLUME_DOWN"
+ " volume_down = " + volume_down
+ " keyguardActive = " + keyguardActive
+ " lastTime = " + lastTime
);
}
if (down) { //按键按下
if (volume_down != null && !volume_down.equals("0")) {
if (keyguardActive) {//锁屏
if (System.currentTimeMillis() - lastTime <= 800) {
mContext.sendBroadcast(new Intent("com.custom.volume_down"));
lastTime = 0;
} else {
lastTime = System.currentTimeMillis();
}
return 0;
}
}
}
// add end
case KeyEvent.KEYCODE_VOLUME_UP:
......
2、接收广播
接收到广播"com.custom.volume_down"后打开或关闭闪光灯
java
private CameraManager mCameraManager;
private boolean mFlashlightEnabled = false;
private String mCameraId;
private Handler mHandler;
private void initBroadcastReceiver(Context context) {
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
tryInitCamera();
IntentFilter filter = new IntentFilter();
filter.addAction("com.custom.volume_down");
context.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
LogUtils.i(TAG, "action = " + action);
if (action != null) {
if (action.equals("com.custom.volume_down")) {
String volume_down = SysProUtils.get("persist.sys.double.volume_down");
LogUtils.i(TAG, "volume_down = " + volume_down);
if (volume_down != null) {
if (volume_down.equals("1")) {
setFlashlight(!mFlashlightEnabled);
}
}
}
}
}
}, filter);
}
private synchronized void ensureHandler() {
if (mHandler == null) {
HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
mHandler = new Handler(thread.getLooper());
}
}
public void setFlashlight(boolean enabled) {
synchronized (this) {
if (mCameraId == null) return;
if (mFlashlightEnabled != enabled) {
mFlashlightEnabled = enabled;
try {
mCameraManager.setTorchMode(mCameraId, enabled);
} catch (CameraAccessException e) {
Log.e(TAG, "Couldn't set torch mode", e);
mFlashlightEnabled = false;
}
}
}
}
private String getCameraId() throws CameraAccessException {
String[] ids = mCameraManager.getCameraIdList();
for (String id : ids) {
CameraCharacteristics c = mCameraManager.getCameraCharacteristics(id);
Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
Integer lensFacing = c.get(CameraCharacteristics.LENS_FACING);
if (flashAvailable != null && flashAvailable
&& lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
return id;
}
}
return null;
}
private void tryInitCamera() {
try {
mCameraId = getCameraId();
} catch (Throwable e) {
Log.e(TAG, "Couldn't initialize.", e);
return;
}
if (mCameraId != null) {
ensureHandler();
mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
}
}
private final CameraManager.TorchCallback mTorchCallback =
new CameraManager.TorchCallback() {
@Override
@WorkerThread
public void onTorchModeChanged(String cameraId, boolean enabled) {
if (TextUtils.equals(cameraId, mCameraId)) {
setTorchMode(enabled);
}
}
};
private void setTorchMode(boolean enabled) {
synchronized (CommModule.this) {
mFlashlightEnabled = enabled;
}
}
3、在settings源码中添加控制
在settings中添加开关按钮。
如果打开开关,双击音量下键,打开或关闭闪光灯。
如果关闭开关,双击音量下键,不做任何处理。
在res/xml/accessibility_settings.xml中添加
java
+ <SwitchPreference
+ android:key="accessibility_flashlight"
+ android:persistent="false"
+ android:icon="@drawable/ic_flashlight"
+ android:summary="@string/accessibility_settings_flashlight_summary"
+ android:title="@string/accessibility_settings_flashlight_preference_title"
+ settings:searchable="true"
+ settings:controller="com.android.settings.accessibility.FlashlightPreferenceController"/>
在app\src\main\java\com\android\settings\accessibility目录下新建FlashlightPreferenceController.java
java
package com.android.settings.accessibility;
import android.content.Context;
import android.os.SystemProperties;
import android.text.TextUtils;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
/**
* A toggle preference controller for audio description
*/
public class FlashlightPreferenceController extends TogglePreferenceController {
static final String PREF_KEY = "accessibility_flashlight";
public FlashlightPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public boolean isChecked() {
String volume_down = SystemProperties.get("persist.sys.double.volume_down");
if (!TextUtils.isEmpty(volume_down)) {
if (volume_down.equals("1")) {
return true;
}
}
return false;
}
@Override
public boolean setChecked(boolean isChecked) {
SystemProperties.set("persist.sys.double.volume_down", isChecked ? "1" : "0");
return true;
}
@Override
public int getAvailabilityStatus() {
String volume_down = SystemProperties.get("persist.sys.double.volume_down");
return TextUtils.isEmpty(volume_down) ? UNSUPPORTED_ON_DEVICE : AVAILABLE;
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_accessibility;
}
}