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博客

相关推荐
2501_915106328 分钟前
iOS 抓包工具有哪些?不同类型的抓包工具可以做什么
android·ios·小程序·https·uni-app·iphone·webview
芦半山1 小时前
2025:生活是个缓慢受锤的过程
android·年终总结
Kapaseker9 小时前
你不看会后悔的2025年终总结
android·kotlin
alexhilton12 小时前
务实的模块化:连接模块(wiring modules)的妙用
android·kotlin·android jetpack
ji_shuke13 小时前
opencv-mobile 和 ncnn-android 环境配置
android·前端·javascript·人工智能·opencv
sunnyday042615 小时前
Spring Boot 项目中使用 Dynamic Datasource 实现多数据源管理
android·spring boot·后端
幽络源小助理16 小时前
下载安装AndroidStudio配置Gradle运行第一个kotlin程序
android·开发语言·kotlin
inBuilder低代码平台16 小时前
浅谈安卓Webview从初级到高级应用
android·java·webview
豌豆学姐16 小时前
Sora2 短剧视频创作中如何保持人物一致性?角色创建接口教程
android·java·aigc·php·音视频·uniapp
白熊小北极17 小时前
Android Jetpack Compose折叠屏感知与适配
android