1.需要修改的文件列表
frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java
frameworks/base/services/core/java/com/android/server/wm/SystemGesturesPointerEventListener.java
framework/base/core/java/com/android/server/settings/MySettingsInternal.java
packages/apps/Settings/res/xml/gestures.xml
packages/apps/src/com/android/settings/gestures/GesturesSettingPreferenceController.java
packages/apps/src/com/android/settings/gestures/ThreeFingersScreencapSwitchPreferenceController
2. 在SystemGesturesPointerEventListener.java文件中,构造函数中初始化控制三指下滑监听开关对象MySettingsInternal,监听三指下滑事件监听,新增三指下滑回调接口
// 三指下滑手势
private static final int SWIPE_FROM_CENTER_TO_DOWN = 5
private MySettingsManager mMySettingsManager
SystemGesturesPointerEventListener(Context context, Handler handler, Callbacks callbacks) {
mContext = checkNull("context", context)
mHandler = handler
mCallbacks = checkNull("callbacks", callbacks)
mMySettingsManager = new MySettingsManager(context)
onConfigurationChanged()
}
private int detectSwipe(int i, long time, float x, float y) {
final float fromX = mDownX[i]
final float fromY = mDownY[i]
final long elapsed = time - mDownTime[i]
// 1. 计算X轴和Y轴的绝对移动距离
final float xDistance = Math.abs(x - fromX)
final float yDistance = Math.abs(y - fromY)
// 2. 计算移动方向(正负值表示方向)
final float deltaY = y - fromY
if (DEBUG) Slog.d(TAG, "pointer " + mDownPointerId[i]
+ " moved (" + fromX + "->" + x + "," + fromY + "->" + y + ") in " + elapsed)
if (fromY <= mSwipeStartThreshold.top
&& y > fromY + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_TOP
}
if (fromY >= screenHeight - mSwipeStartThreshold.bottom
&& y < fromY - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_BOTTOM
}
if (fromX >= screenWidth - mSwipeStartThreshold.right
&& x < fromX - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_RIGHT
}
if (fromX <= mSwipeStartThreshold.left
&& x > fromX + mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_LEFT
}
// 添加三指下滑判断:起始位置在屏幕中部偏下,垂直向下滑动超过阈值,且在时间限制内
Slog.d(TAG, "fromY : " + fromY + " ,screenHeight : " + screenHeight + " ,deltaY : " + deltaY + " ,yDistance : " + yDistance
+ " , xDistance : " + xDistance + " ,mSwipeDistanceThreshold :" + mSwipeDistanceThreshold + " , elapsed : " + elapsed + " ,SWIPE_TIMEOUT_MS : " + SWIPE_TIMEOUT_MS)
int threeFingerSetting = mMySettingsManager.getMyCustomParam(1)
Slog.d(TAG, "threeFingerSetting : " + threeFingerSetting)
if (deltaY > 0 && // 确保是向下滑动
yDistance > xDistance && // Y轴移动距离大于X轴,判断为垂直滑动
yDistance > mSwipeDistanceThreshold &&
elapsed < SWIPE_TIMEOUT_MS
&& (1 == threeFingerSetting)) {
return SWIPE_FROM_CENTER_TO_DOWN
}
return SWIPE_NONE
}
@Override
public void onPointerEvent(MotionEvent event) {
if (mGestureDetector != null && event.isTouchEvent()) {
mGestureDetector.onTouchEvent(event)
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mSwipeFireable = true
mDebugFireable = true
mDownPointers = 0
captureDown(event, 0)
if (mMouseHoveringAtLeft) {
mMouseHoveringAtLeft = false
mCallbacks.onMouseLeaveFromLeft()
}
if (mMouseHoveringAtTop) {
mMouseHoveringAtTop = false
mCallbacks.onMouseLeaveFromTop()
}
if (mMouseHoveringAtRight) {
mMouseHoveringAtRight = false
mCallbacks.onMouseLeaveFromRight()
}
if (mMouseHoveringAtBottom) {
mMouseHoveringAtBottom = false
mCallbacks.onMouseLeaveFromBottom()
}
mCallbacks.onDown()
break
case MotionEvent.ACTION_POINTER_DOWN:
captureDown(event, event.getActionIndex())
if (mDebugFireable) {
mDebugFireable = event.getPointerCount() < 5
if (!mDebugFireable) {
if (DEBUG) Slog.d(TAG, "Firing debug")
mCallbacks.onDebug()
}
}
break
case MotionEvent.ACTION_MOVE:
if (mSwipeFireable) {
int trackpadSwipe = detectTrackpadThreeFingerSwipe(event)
mSwipeFireable = trackpadSwipe == TRACKPAD_SWIPE_NONE
if (!mSwipeFireable) {
if (trackpadSwipe == TRACKPAD_SWIPE_FROM_TOP) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop from trackpad")
mCallbacks.onSwipeFromTop()
} else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_BOTTOM) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom from trackpad")
mCallbacks.onSwipeFromBottom()
} else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_RIGHT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight from trackpad")
mCallbacks.onSwipeFromRight()
} else if (trackpadSwipe == TRACKPAD_SWIPE_FROM_LEFT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft from trackpad")
mCallbacks.onSwipeFromLeft()
}
break
}
final int swipe = detectSwipe(event)
mSwipeFireable = swipe == SWIPE_NONE
if (swipe == SWIPE_FROM_TOP) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromTop")
mCallbacks.onSwipeFromTop()
} else if (swipe == SWIPE_FROM_BOTTOM) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromBottom")
mCallbacks.onSwipeFromBottom()
} else if (swipe == SWIPE_FROM_RIGHT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight")
mCallbacks.onSwipeFromRight()
} else if (swipe == SWIPE_FROM_LEFT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft")
mCallbacks.onSwipeFromLeft()
} else if (swipe == SWIPE_FROM_CENTER_TO_DOWN) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromCenterToDown:" + event.getPointerCount())
// 关键:判断是否为三指,然后触发截图
if (event.getPointerCount() == 3) { // 确保是三指手势
mCallbacks.onThreeFingerSwipeDown()
}
}
}
break
case MotionEvent.ACTION_HOVER_MOVE:
if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
final float eventX = event.getX()
final float eventY = event.getY()
if (!mMouseHoveringAtLeft && eventX == 0) {
mCallbacks.onMouseHoverAtLeft()
mMouseHoveringAtLeft = true
} else if (mMouseHoveringAtLeft && eventX > 0) {
mCallbacks.onMouseLeaveFromLeft()
mMouseHoveringAtLeft = false
}
if (!mMouseHoveringAtTop && eventY == 0) {
mCallbacks.onMouseHoverAtTop()
mMouseHoveringAtTop = true
} else if (mMouseHoveringAtTop && eventY > 0) {
mCallbacks.onMouseLeaveFromTop()
mMouseHoveringAtTop = false
}
if (!mMouseHoveringAtRight && eventX >= screenWidth - 1) {
mCallbacks.onMouseHoverAtRight()
mMouseHoveringAtRight = true
} else if (mMouseHoveringAtRight && eventX < screenWidth - 1) {
mCallbacks.onMouseLeaveFromRight()
mMouseHoveringAtRight = false
}
if (!mMouseHoveringAtBottom && eventY >= screenHeight - 1) {
mCallbacks.onMouseHoverAtBottom()
mMouseHoveringAtBottom = true
} else if (mMouseHoveringAtBottom && eventY < screenHeight - 1) {
mCallbacks.onMouseLeaveFromBottom()
mMouseHoveringAtBottom = false
}
}
break
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mSwipeFireable = false
mDebugFireable = false
mCallbacks.onUpOrCancel()
break
default:
if (DEBUG) Slog.d(TAG, "Ignoring " + event)
}
}
interface Callbacks {
void onSwipeFromTop()
void onSwipeFromBottom()
void onSwipeFromRight()
void onSwipeFromLeft()
void onFling(int durationMs)
void onDown()
void onUpOrCancel()
void onMouseHoverAtLeft()
void onMouseHoverAtTop()
void onMouseHoverAtRight()
void onMouseHoverAtBottom()
void onMouseLeaveFromLeft()
void onMouseLeaveFromTop()
void onMouseLeaveFromRight()
void onMouseLeaveFromBottom()
void onThreeFingerSwipeDown()
void onDebug()
}
3.在DisplayPolicy.java文件中,实现三指下滑回调接口
@Override
public void onThreeFingerSwipeDown() {
synchronized (mLock) {
Slog.d(TAG, "three finger takeScreenshot");
takeScreenshot(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_VENDOR_GESTURE);
}
}
4.在framework/base/core/java/com/android/server/settings/MySettingsInternal.java文件中,定义控制开关三指下滑参数
package com.android.server.settings;
public final class MySettingsInternal {
private MySettingsInternal() {}
public static final String SYSTEM_KEY_MY_CUSTOM_PARAM = "my_custom_param";
}
5.在gestures.xml文件增加开关选项
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/gesture_preference_title">
<Preference
android:key="gesture_swipe_down_fingerprint_input_summary"
android:title="@string/fingerprint_swipe_for_notifications_title"
android:fragment="com.android.settings.gestures.SwipeToNotificationSettings"
settings:searchable="false"
settings:controller="com.android.settings.gestures.SwipeToNotificationPreferenceController" />
<Preference
android:key="gesture_double_tap_power_input_summary"
android:title="@string/double_tap_power_for_camera_title"
android:fragment="com.android.settings.gestures.DoubleTapPowerSettings"
settings:searchable="false"
settings:controller="com.android.settings.gestures.DoubleTapPowerPreferenceController" />
<Preference
android:key="gesture_double_twist_input_summary"
android:title="@string/double_twist_for_camera_mode_title"
android:fragment="com.android.settings.gestures.DoubleTwistGestureSettings"
settings:searchable="false"
settings:controller="com.android.settings.gestures.DoubleTwistPreferenceController" />
<Preference
android:key="gesture_system_navigation_input_summary"
android:title="@string/system_navigation_title"
android:fragment="com.android.settings.gestures.SystemNavigationGestureSettings"
settings:controller="com.android.settings.gestures.SystemNavigationPreferenceController"
settings:keywords="@string/keywords_system_navigation" />
<Preference
android:key="gesture_one_handed"
android:title="@string/one_handed_title"
android:fragment="com.android.settings.gestures.OneHandedSettings"
settings:controller="com.android.settings.gestures.OneHandedEnablePreferenceController" />
<Preference
android:key="gesture_tap_screen_input_summary"
android:title="@string/ambient_display_tap_screen_title"
android:fragment="com.android.settings.gestures.TapScreenGestureSettings"
settings:searchable="false"
settings:controller="com.android.settings.gestures.TapScreenGesturePreferenceController" />
<Preference
android:key="gesture_double_tap_screen_input_summary"
android:title="@string/ambient_display_title"
android:fragment="com.android.settings.gestures.DoubleTapScreenSettings"
settings:searchable="false"
settings:controller="com.android.settings.gestures.DoubleTapScreenPreferenceController" />
<Preference
android:key="gesture_pick_up_input_summary"
android:title="@string/ambient_display_pickup_title"
android:fragment="com.android.settings.gestures.PickupGestureSettings"
settings:searchable="false"
settings:controller="com.android.settings.gestures.PickupGesturePreferenceController" />
<Preference
android:key="gesture_power_menu_summary"
android:title="@string/power_menu_setting_name"
android:fragment="com.android.settings.gestures.PowerMenuSettings"
settings:controller="com.android.settings.gestures.PowerMenuPreferenceController" />
<com.android.settingslib.PrimarySwitchPreference
android:key="gesture_prevent_ringing_summary"
android:title="@string/gesture_prevent_ringing_screen_title"
android:fragment="com.android.settings.gestures.PreventRingingGestureSettings"
settings:controller="com.android.settings.gestures.PreventRingingParentPreferenceController" />
<com.android.settingslib.PrimarySwitchPreference
android:key="gesture_three_fingers_screencap_summary"
android:title="三指下滑截图"
android:summary="启用后,可以通过三指下滑手势截取屏幕"
settings:controller="com.android.settings.gestures.ThreeFingersScreencapSwitchPreferenceController"/>
</PreferenceScreen>
6.在GesturesSettingPreferenceController.java文件中添加ThreeFingersScreencapSwitchPreferenceController
private static List<AbstractPreferenceController> buildAllPreferenceControllers(
@NonNull Context context) {
final AmbientDisplayConfiguration ambientDisplayConfiguration =
new AmbientDisplayConfiguration(context);
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new SwipeToNotificationPreferenceController(context, FAKE_PREF_KEY));
controllers.add(new DoubleTwistPreferenceController(context, FAKE_PREF_KEY));
controllers.add(new DoubleTapPowerPreferenceController(context, FAKE_PREF_KEY));
controllers.add(new PickupGesturePreferenceController(context, FAKE_PREF_KEY)
.setConfig(ambientDisplayConfiguration));
controllers.add(new DoubleTapScreenPreferenceController(context, FAKE_PREF_KEY)
.setConfig(ambientDisplayConfiguration));
controllers.add(new PreventRingingParentPreferenceController(context, FAKE_PREF_KEY));
controllers.add(new ThreeFingersScreencapSwitchPreferenceController(context, FAKE_PREF_KEY));
return controllers;
}
7.新增加三指下滑控制器类ThreeFingersScreencapSwitchPreferenceController
package com.android.settings.gestures;
import android.content.Context;
import android.app.MySettingsManager;
import android.provider.Settings;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.os.Handler;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.widget.MainSwitchPreference;
import android.os.SystemProperties;
import android.util.Slog;
import com.android.settingslib.core.lifecycle.Lifecycle;
import android.text.TextUtils;
import com.android.settingslib.PrimarySwitchPreference;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.server.settings.MySettingsInternal;
import android.database.ContentObserver;
import android.net.Uri;
import android.content.ContentResolver;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
public class ThreeFingersScreencapSwitchPreferenceController extends TogglePreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop {
private static final String TAG = "ThreeFingersScreencapSwitchPreferenceController";
private static final String KEY = "gesture_three_fingers_screencap_summary";
private PrimarySwitchPreference mPreference;
private MySettingsManager mMySettingsManager;
private SettingObserver mSettingObserver;
public ThreeFingersScreencapSwitchPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mMySettingsManager = new MySettingsManager(context);
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mPreference = screen.findPreference(getPreferenceKey());
mSettingObserver = new SettingObserver(mPreference);
super.displayPreference(screen);
}
@Override
public void updateState(Preference preference) {
int threeFingerSetting = mMySettingsManager.getMyCustomParam(1);
Slog.d(TAG, "threeFingerSetting 02 = " + threeFingerSetting);
boolean isChecked = (1==threeFingerSetting);
if (preference instanceof PrimarySwitchPreference) {
PrimarySwitchPreference switchPref = (PrimarySwitchPreference) preference;
switchPref.setChecked(isChecked);
}
setChecked(isChecked);
}
@Override
public boolean isChecked() {
int threeFingerSetting = mMySettingsManager.getMyCustomParam(1);
Slog.d(TAG, "threeFingerSetting 03 = " + threeFingerSetting);
return (1==threeFingerSetting);
}
@Override
public boolean setChecked(boolean isChecked) {
Slog.d(TAG, "setChecked isChecked = " + isChecked);
return mMySettingsManager.setMyCustomParam(isChecked
? 1
: 0);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
Slog.d(TAG, "handlePreferenceTreeClick true");
return true;
}
Slog.d(TAG, "handlePreferenceTreeClick false");
return super.handlePreferenceTreeClick(preference);
}
@Override
public int getSliceHighlightMenuRes() {
return R.string.menu_key_sound;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void onStart() {
int threeFingerSetting = mMySettingsManager.getMyCustomParam(1);
Slog.d(TAG, "onStart threeFingerSetting 04 = " + threeFingerSetting);
if (mSettingObserver != null) {
mSettingObserver.register(mContext.getContentResolver());
mSettingObserver.onChange(false, null);
}
}
@Override
public void onStop() {
int threeFingerSetting = mMySettingsManager.getMyCustomParam(1);
Slog.d(TAG, "onStop threeFingerSetting 05 = " + threeFingerSetting);
if (mSettingObserver != null) {
mSettingObserver.unregister(mContext.getContentResolver());
}
}
private class SettingObserver extends ContentObserver {
private final Uri mUri = Settings.System.getUriFor(
MySettingsInternal.SYSTEM_KEY_MY_CUSTOM_PARAM);
private final Preference mPreference;
SettingObserver(Preference preference) {
super(new Handler());
mPreference = preference;
}
public void register(ContentResolver cr) {
cr.registerContentObserver(mUri, false, this);
}
public void unregister(ContentResolver cr) {
cr.unregisterContentObserver(this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Slog.d(TAG, "handlePreferenceTreeClick uri = " + uri);
Slog.d(TAG, "handlePreferenceTreeClick mUri = " + mUri);
if (uri == null || mUri.equals(uri)) {
updateState(mPreference);
}
}
}
}