口袋模式的实现

口袋模式

口袋模式:带有黑屏手势功能的手机,一般都需要进行一个防误触的判断。防止手机放在口袋里或者包包(一些导电介质的东西可能带来辅助效果,比如钥匙)里别误触发点亮屏幕,甚至是打出电话。

口袋模式设计原理:接近式传感器的靠近与远离功能。可以参考我们手机打电话时,耳朵贴近手机的时候,往往屏幕是自动息屏,离开耳朵一小段距离,屏幕又亮起的这个功能。同理和我们放在口袋中一个意思。

概要设计

添加设置的各级菜单

获取距离传感器以及功能实现

功能实现

添加各级菜单

各级菜单的定义以及其界面的实现,主要是对交互界面的实现,除数据库的存储外,不涉及到逻辑的处理。

一、添加设置的各级菜单

添加模块:MtkSettings

添加菜单

路径: res/xml/gestures.xml

android:key="pocket_mode"

android:title="@string/pocket_mode"

android:summary="@string/pocket_mode_tip"

settings:controller="com.android.settings.gestures.PocketModePreferenceController" />

添加字符串资源

路径:res/values/strings.xml

Pocket Mode

It can help users avoid accidental touch of the screen in their pockets or bags

添加PocketModePreferenceController.java类

路径:这个类需要在gestures目录上自行添加。src/com/android/settings/gestures/PocketModePreferenceController.java

package com.android.settings.gestures;

import static android.provider.Settings.Secure.TURN_ON_BACKLIGHT_GESTURE;

import android.content.ContentResolver;

import android.content.Context;

import android.net.Uri;

import android.os.Handler;

import android.os.SystemProperties;

import android.provider.Settings;

import androidx.preference.Preference;

import androidx.preference.PreferenceScreen;

import com.android.settings.R;

import com.android.settings.core.TogglePreferenceController;

import com.android.settingslib.PrimarySwitchPreference;

import com.android.settingslib.core.lifecycle.LifecycleObserver;

import com.android.settingslib.core.lifecycle.events.OnStart;

import com.android.settingslib.core.lifecycle.events.OnStop;

import com.google.common.annotations.VisibleForTesting;

import java.io.DataOutputStream;

import java.io.IOException;

import android.database.ContentObserver;

public class PocketModePreferenceController extends TogglePreferenceController

implements LifecycleObserver, OnStart, OnStop {

@VisibleForTesting

static final int KEY_CHORD_POWER_VOLUME_UP_MUTE_TOGGLE = 1;

private Preference mPreference;

private SettingObserver mSettingObserver;

public PocketModePreferenceController(Context context, String preferenceKey) {

super(context, preferenceKey);

}

@Override

public void displayPreference(PreferenceScreen screen) {

super.displayPreference(screen);

mPreference = screen.findPreference(getPreferenceKey());

mSettingObserver = new SettingObserver(mPreference);

}

@Override

public boolean isChecked() {

final int status = Settings.Secure.getInt(mContext.getContentResolver(),

Settings.Secure.POCKET_MODE,0);

return status != 0;

}

@Override

public boolean setChecked(boolean isChecked) {

return Settings.Secure.putInt(mContext.getContentResolver(),

Settings.Secure.POCKET_MODE, isChecked ? 1 : 0);

}

@Override

public void updateState(Preference preference) {

super.updateState(preference);

CharSequence summary = mContext.getText(R.string.pocket_mode_tip);

preference.setSummary(summary);

}

@Override

public int getAvailabilityStatus() {

return SystemProperties.getBoolean("ro.config.wake_gesture", false) ? AVAILABLE : UNSUPPORTED_ON_DEVICE;

}

@Override

public void onStart() {

if (mSettingObserver != null) {

mSettingObserver.register(mContext.getContentResolver());

mSettingObserver.onChange(false, null);

}

}

@Override

public void onStop() {

if (mSettingObserver != null) {

mSettingObserver.unregister(mContext.getContentResolver());

}

}

private class SettingObserver extends ContentObserver {

private final Uri mGestureUri = Settings.Secure.getUriFor(

Settings.Secure.POCKET_MODE);

private final Preference mPreference;

SettingObserver(Preference preference) {

super(new Handler());

mPreference = preference;

}

public void register(ContentResolver cr) {

cr.registerContentObserver(mGestureUri, false, this);

}

public void unregister(ContentResolver cr) {

cr.unregisterContentObserver(this);

}

@Override

public void onChange(boolean selfChange, Uri uri) {

super.onChange(selfChange, uri);

if (uri == null || mGestureUri.equals(uri)) {

updateState(mPreference);

}

}

}

@Override

public int getSliceHighlightMenuRes() {

return 0;

}

}

在getAvailabilityStatus()这个方法中,使用了宏控,可以根据实际需求自行选择开关的状态。各级菜单的定义以及其界面的实现,主要是对交互界面的实现,除数据库的存储外,不涉及到逻辑的处理。

二、获取距离传感器以及功能实现

添加模块:framework/base

目前智能亮屏方式有两种,抬腕亮屏和双击亮屏。口袋模式的实现方式则是针对这两种亮屏方式进行实现。当口袋模式打开的时候,pSensor如果检测到有物体靠近,就使抬腕亮屏和双击亮屏功能失效,否则,抬腕亮屏和双击亮屏功能正常实现。

设置数据库属性

路径: core/java/android/provider/Settings.java

/**

* @hide

*/

public static final String POCKET_MODE =

"pocket_mode";

获取距离的传感器ProximityCheck.java类

路径:services/core/java/com/android/server/policy/ProximityCheck.java

package com.android.server.policy;

import android.view.KeyEvent;

import android.os.Handler;

import android.util.Slog;

import android.content.Context;

import android.hardware.Sensor;

import android.hardware.SensorManager;

import android.hardware.SensorEvent;

import android.hardware.SensorEventListener;

import android.os.PowerManager;

import android.os.PowerManager.WakeReason;

import android.os.SystemClock;

public class ProximityCheck {

private final SensorManager mSensorManager;

private KeyEvent event;

private boolean mRegistered;

private final Handler mHandler;

static final String TAG = "ProximityCheck";

PowerManager mPowerManager;

ProximityCheck(Context context, Handler handler) {

mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);

mPowerManager= (PowerManager) context.getSystemService(Context.POWER_SERVICE);

mHandler = handler;

}

private final SensorEventListener mSensorEventListener = new SensorEventListener() {

@Override

public void onSensorChanged(SensorEvent event) {

if (event.values.length == 0) {

Slog.w(TAG, "ProximityCheck Event has no values!");

finishWithResult(false);

} else {

boolean isNear = event.values[0] != 1.0f;

Slog.d(TAG, "ProximityCheck isNear:" + isNear+" "+event.values[0]);

finishWithResult(isNear);

}

}

@Override

public void onAccuracyChanged(Sensor sensor, int accuracy) {

}

};

private final Runnable mTimeoutRunnable = () -> {

Slog.w(TAG, "ProximityCheck timeout!");

finishWithResult(false);

};

public void onResult(boolean isNear) {

Slog.w(TAG, "ProximityCheck onResult:"+isNear);

if(isNear){

mPowerManager.goToSleep(SystemClock.uptimeMillis());

}

}

public void check() {

Slog.d(TAG, "ProximityCheck check");

mRegistered = true;

mSensorManager.registerListener(mSensorEventListener,

mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY), SensorManager.SENSOR_DELAY_NORMAL, mHandler);

mHandler.postDelayed(mTimeoutRunnable, 500);

}

private void finishWithResult(boolean isNear) {

onResult(isNear);

if (mRegistered) {

mRegistered = false;

mHandler.removeCallbacks(mTimeoutRunnable);

mSensorManager.unregisterListener(mSensorEventListener);

}

}

}

口袋模式功能的实现PhoneWindowManager.java

路径:services/core/java/com/android/server/policy/PhoneWindowManager.java

1、声明ProximityCheck。

private ProximityCheck proximityCheck;

2、抬腕唤醒时,检测是否有物体靠近

在抬腕亮屏的onWakeUp方法中创建ProximityCheck对象,并获取到口袋模式的开关是否打开,如果打开则是调用ProximityCheck对象中的check方法。

{

if(proximityCheck == null ){

proximityCheck = new ProximityCheck(mContext, mHandler);

}

if (shouldEnableWakeGestureLp()) {

performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,

"Wake Up");

wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,

PowerManager.WAKE_REASON_GESTURE, "android.policy:GESTURE");

boolean pocketMode = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.POCKET_MODE,0) != 0;

if(pocketMode) {

proximityCheck.check();

}

}

}

3、双击唤醒时,检测是否有物体靠近

在interceptKeyBeforeQueueing方法中上报各种keycode的下面添加一个双击亮屏的case KeyEvent.keycode,然后创建ProximityCheck对象,并获取到口袋模式的开关是否打开,如果打开则是调用ProximityCheck对象中的check方法。

case KeyEvent.KEYCODE_U:

if(proximityCheck == null ){

proximityCheck = new ProximityCheck(mContext, mHandler);

}

if(down){

boolean pocketMode = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.POCKET_MODE,0) != 0;

mPowerManager.wakeUp(event.getEventTime(), PowerManager.WAKE_REASON_WAKE_KEY, "android:policy:double_click");

if(pocketMode){

proximityCheck.check();

}

}

break;

相关文章

借呗靠谱吗?全面解析借呗的优势与风险

阿里云轻量应用服务器限峰值带宽和带宽的区别

英雄坛说初期攻略(ios英雄坛说详细攻略)