实现思路:
1、发送广播
WindowManagerService循环读取下面按键消息并分发给窗口,在消息分发前会在PhoneWindowManager.interceptKeyBeforeQueueing方法中进行消息的过滤。因此该实现方式为在消息分发前的interceptKeyBeforeQueueing方法中监听当前按键为音量下键,如果当前状态为锁屏状态,并按键为音量下键,且两次按键间隔时间小于800ms时发送广播--“com.custom.volume_down”
具体实现方式如下:
sys\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.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 startString 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 endcase KeyEvent.KEYCODE_VOLUME_UP:......
2、接收广播
接收到广播“com.custom.volume_down”后打开或关闭闪光灯
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() {@Overridepublic 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@WorkerThreadpublic 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中添加
+ <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
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);}@Overridepublic 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;}@Overridepublic boolean setChecked(boolean isChecked) {SystemProperties.set("persist.sys.double.volume_down", isChecked ? "1" : "0");return true;}@Overridepublic int getAvailabilityStatus() {String volume_down = SystemProperties.get("persist.sys.double.volume_down");return TextUtils.isEmpty(volume_down) ? UNSUPPORTED_ON_DEVICE : AVAILABLE;}@Overridepublic int getSliceHighlightMenuRes() {return R.string.menu_key_accessibility;}
}
InputDispatcher拦截逻辑_interceptkeybeforequeueing-CSDN博客
Android事件拦截_interceptkeybeforequeueing-CSDN博客