Android 15 实现三指下滑截屏

108 阅读5分钟

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文件中,定义控制开关三指下滑参数

// 文件:frameworks/base/services/core/java/com/android/server/settings/MySettingsInternal.java
package com.android.server.settings;

/** @hide */
public final class MySettingsInternal {
    private MySettingsInternal() {} // 私有构造,规避 StaticUtils 错误

    /** @hide */
    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);
            }
        }
    }
}