Android13锁屏或灭屏状态下,快速按两次音量下键实现打开闪光灯功能

实现思路:

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;
    }
}

InputDispatcher拦截逻辑_interceptkeybeforequeueing-CSDN博客

Android事件拦截_interceptkeybeforequeueing-CSDN博客

相关推荐
雨白8 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹9 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空11 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭12 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日12 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安13 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑13 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟17 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡18 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0018 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体