原生全局手势在systemui模块,提供了滑动手势功能,如果不能修改framework代码,只能自己手撸了
1、监听系统全局触摸事件
private fun startGlobalInputMonitoring() { try { val obj = getSystemService(INPUT_SERVICE) as? InputManager ?: return inputManager = obj val monitorMethod = InputManager::class.java.getMethod( "monitorGestureInput", String::class.java, Int::class.javaPrimitiveType ) mInputMonitor = monitorMethod.invoke( inputManager, "SystemUI-Global-Gesture", Display.DEFAULT_DISPLAY ) if (mInputMonitor == null) { Log.w(TAG, "Failed to get InputMonitor instance.") return } val getInputChannelMethod = mInputMonitor?.javaClass?.getMethod("getInputChannel") val inputChannel = getInputChannelMethod?.invoke(mInputMonitor) as? InputChannel mInputThread = HandlerThread("Input-Thread", Process.THREAD_PRIORITY_FOREGROUND).apply { start() } mInputHandler = Handler(mInputThread!!.looper) handleReceiver(inputChannel) } catch (e: Exception) { } } fun handleReceiver(inputChannel: InputChannel?) { if (mInputThread?.looper == null) return mInputEventReceiver = object : InputEventReceiver(inputChannel, mInputThread!!.looper) { override fun onInputEvent(event: InputEvent) { if (event !is MotionEvent) { finishInputEvent(event, true) return } val copiedEvent = MotionEvent.obtain(event) finishInputEvent(event, true) try { if (isSkipInputEvent(copiedEvent)) return touch(copiedEvent) } catch (e: Exception) { Log.w(TAG, "handleReceiver:", e) } finally { copiedEvent.recycle() } } } }
2、判断手势方向
const val EDGE_ZONE_WIDTH_PX = 60 // 左右边缘触发触控宽度 /** 手势处理 */ private fun touch(event: MotionEvent) { val isTouchEvent = (event.source and android.view.InputDevice.SOURCE_TOUCHSCREEN) != 0 val action = event.actionMasked if (isTouchEvent && action != MotionEvent.ACTION_HOVER_ENTER && action != MotionEvent.ACTION_HOVER_MOVE && action != MotionEvent.ACTION_HOVER_EXIT ) { if (action == MotionEvent.ACTION_DOWN) { mPendingThreeFingerGesture = GlobalGestureManager.VSwipeDirection.NONE mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.NONE mIsThreeFingerTracking = false mThreeFingerDispatched = false } val threeFingerHandled = checkThreeFingerGesture(event) if (!threeFingerHandled && !mIsThreeFingerTracking) { mGestureDetector?.onTouchEvent(event) } if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (action == MotionEvent.ACTION_UP && !threeFingerHandled) { when (mPendingSingleGesture) { GlobalGestureManager.VSwipeDirection.UP -> { onSwipeUp() } GlobalGestureManager.VSwipeDirection.DOWN -> { onSwipeDown() } GlobalGestureManager.VSwipeDirection.LEFT -> { onSwipeLeft() } GlobalGestureManager.VSwipeDirection.RIGHT -> { onSwipeRight() } else -> {} } } mPendingThreeFingerGesture = GlobalGestureManager.VSwipeDirection.NONE mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.NONE mIsThreeFingerTracking = false mThreeFingerDispatched = false mIsMoving = false } } } private fun isInEdgeZone(x: Float, y: Float): Boolean { if (y < statusHeight) return true if (y > mScreenHeight - barHeight) return true val inMiddleVertical = y > statusHeight && y < mScreenHeight - barHeight if (inMiddleVertical && x < GlobalGestureManager.EDGE_ZONE_WIDTH_PX) return true if (inMiddleVertical && x > mScreenWidth - GlobalGestureManager.EDGE_ZONE_WIDTH_PX) return true return false }
如果是上下滑,希望起点在systembar,否则不响应,如果是左右滑,希望起点从屏幕边缘开始,否则不响应
3、手势拦截
/** 拦截手势 */ private fun isSkipInputEvent(event: MotionEvent): Boolean { val action = event.actionMasked log("isSkipInputEvent action=$action, x=${event.x} y=${event.y}") if (mIsInterceptActive) { if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mIsInterceptActive = false isFullScreenSkip = false log("isSkipInputEvent: intercept session end") } return true } when (action) { MotionEvent.ACTION_DOWN -> { isForbidGlobalGesture = InteractionAPIManager.isGlobalGestureDisabled() mInterceptDownX = event.x mInterceptDownY = event.y isFullScreenSkip = !SystemBarManager.isShowSystemBar() log("isSkipInputEvent isFullScreenSkip=$isFullScreenSkip, isForbidGlobalGesture=$isForbidGlobalGesture") if ((isForbidGlobalGesture || isFullScreenSkip) && isInEdgeZone(mInterceptDownX, mInterceptDownY) ) { mIsInterceptActive = true log("isSkipInputEvent: activate intercept session") return true } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { mIsInterceptActive = false isFullScreenSkip = false } } return false }
根据条件拦截手势响应
4、事件拦截
private fun pilferThreeFingerPointers() { try { if (!isSplit()) { Log.w(TAG, "pilferThreeFingerPointers: isSplit is false") return } val monitor = mInputMonitor ?: run { Log.w(TAG, "pilferThreeFingerPointers: mInputMonitor is null") return } val pilferMethod = monitor.javaClass.getMethod("pilferPointers") pilferMethod.invoke(monitor) } catch (e: Exception) { Log.w(TAG, "pilferThreeFingerPointers: ", e) } } if (pointerCount >= 3 && action == MotionEvent.ACTION_POINTER_DOWN) { pilferThreeFingerPointers() return true }
屏幕需要所有app都禁止响应三指操作,所以需要根据条件判断,在触摸时调用 mInputMonitor.pilferPointers()
package com.test.systemui.gesture import android.app.Service import android.content.Intent import android.hardware.input.InputManager import android.os.Handler import android.os.HandlerThread import android.os.IBinder import android.os.Process import android.os.RemoteCallbackList import android.provider.Settings import android.util.Log import android.view.Display import android.view.GestureDetector import android.view.InputChannel import android.view.InputEvent import android.view.InputEventReceiver import android.view.MotionEvent import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch import kotlin.math.abs class GlobalGestureService : Service() { companion object { private const val TAG = "GlobalGestureService" private const val GLOBAL_LAUNCHER_SPLIT_STATE = "global_launcher_split_state" } private var mInputMonitor: Any? = null private var mInputEventReceiver: InputEventReceiver? = null private var mInputThread: HandlerThread? = null lateinit var inputManager: InputManager private var mInputHandler: Handler? = null private var mGestureDetector: GestureDetector? = null private var mIsMoving = false private var mScreenWidth = 0 private var mScreenHeight = 0 // --- 三指手势追踪变量 --- private var mIsThreeFingerTracking = false private val mThreeFingerStartX = FloatArray(3) private val mThreeFingerStartY = FloatArray(3) private var mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.NONE private var mPendingThreeFingerGesture = GlobalGestureManager.VSwipeDirection.NONE private var mThreeFingerDispatched = false private var barHeight = 0 private var statusHeight = 0 private var isForbidGlobalGesture = false private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var mJob: Job? = null private val callbackList = RemoteCallbackList<ISystemGestureCallBack>() private var mInterceptDownX = 0f private var mInterceptDownY = 0f private var mIsInterceptActive = false private var isFullScreenSkip = false private val isMainProcess by lazy { Process.myUserHandle().hashCode() == 0 } override fun onCreate() { super.onCreate() log("GlobalGestureService 启动!${Process.myUserHandle()}") if (!isMainProcess) { log("GlobalGestureService stopSelf") stopSelf() return } barHeight = resources.getDimensionPixelSize(R.dimen.bottom_nav_bar_total_height) statusHeight = resources.getDimensionPixelSize(R.dimen.status_bar_height) resources.displayMetrics.let { mScreenWidth = it.widthPixels mScreenHeight = it.heightPixels } try { initGestureDetector() startGlobalInputMonitoring() } catch (e: Exception) { Log.w(TAG, "手势服务初始化失败", e) } try { InteractionAPIManager.setGlobalGestureDisabled(false) } catch (e: Exception) { Log.w(TAG, "InteractionAPI.setGlobalGestureDisabled(false) error: ${e.message}") } } override fun onStartCommand( intent: Intent?, flags: Int, startId: Int ): Int { log("GlobalGestureService onStartCommand") return START_STICKY } private fun initGestureDetector() { mGestureDetector = GestureDetector( this, object : GestureDetector.SimpleOnGestureListener() { override fun onScroll( eventStart: MotionEvent?, eventMove: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { if (eventStart == null) return false val deltaX = eventMove.x - eventStart.x val deltaY = eventMove.y - eventStart.y if (abs(deltaX) > abs(deltaY)) { // 横向滑动判定 if (abs(deltaX) > GlobalGestureManager.GESTURE_THRESHOLD_PX) { if (deltaX > 0) { // 从左边缘往内滑动 if (eventStart.x < GlobalGestureManager.EDGE_ZONE_WIDTH_PX && (eventStart.y > statusHeight && eventStart.y < (mScreenHeight - barHeight)) ) { // 在状态栏导航栏内滑动 mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.RIGHT return true } } else { // 从右边缘往内滑动 if (eventStart.x > (mScreenWidth - GlobalGestureManager.EDGE_ZONE_WIDTH_PX) && (eventStart.y > statusHeight && eventStart.y < (mScreenHeight - barHeight)) ) { // 在状态栏导航栏内滑动 mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.LEFT return true } } } } else { // 纵向滑动判定 if (abs(deltaY) > GlobalGestureManager.GESTURE_THRESHOLD_PX) { if (deltaY < 0) { if (eventStart.y < (mScreenHeight) && eventStart.y > (mScreenHeight - barHeight) ) { mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.UP return true } } else { if (eventStart.y < statusHeight ) { mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.DOWN return true } } } } return false } override fun onSingleTapConfirmed(e: MotionEvent): Boolean = false } ) } /** * 三指左右滑 */ private fun checkThreeFingerGesture(event: MotionEvent): Boolean { val action = event.actionMasked val pointerCount = event.pointerCount if (pointerCount >= 3 && action == MotionEvent.ACTION_POINTER_DOWN) { if (!mIsThreeFingerTracking) { mIsThreeFingerTracking = true for (i in 0 until 3) { mThreeFingerStartX[i] = event.getX(i) mThreeFingerStartY[i] = event.getY(i) } pilferThreeFingerPointers() } return true } if (mIsThreeFingerTracking && action == MotionEvent.ACTION_MOVE && pointerCount >= 3) { var totalDeltaX = 0f var totalDeltaY = 0f for (i in 0 until 3) { totalDeltaX += event.getX(i) - mThreeFingerStartX[i] totalDeltaY += event.getY(i) - mThreeFingerStartY[i] } val avgDeltaX = totalDeltaX / 3f val avgDeltaY = totalDeltaY / 3f if (abs(avgDeltaX) > abs(avgDeltaY)) { val dir = when { avgDeltaX > GlobalGestureManager.THREE_FINGER_THRESHOLD_PX -> GlobalGestureManager.VSwipeDirection.THREE_FINGER_RIGHT avgDeltaX < -GlobalGestureManager.THREE_FINGER_THRESHOLD_PX -> GlobalGestureManager.VSwipeDirection.THREE_FINGER_LEFT else -> GlobalGestureManager.VSwipeDirection.NONE } if (dir != GlobalGestureManager.VSwipeDirection.NONE) { mPendingThreeFingerGesture = dir if (!mThreeFingerDispatched) { mThreeFingerDispatched = true onThreeFingerSwipe(dir) pilferThreeFingerPointers() mIsThreeFingerTracking = false mThreeFingerDispatched = false return true } } } } if (action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (mIsThreeFingerTracking) { if (!mThreeFingerDispatched) { onThreeFingerSwipe(mPendingThreeFingerGesture) } mIsThreeFingerTracking = false mThreeFingerDispatched = false } } return mIsThreeFingerTracking } private fun pilferThreeFingerPointers() { try { if (!isSplit()) { Log.w(TAG, "pilferThreeFingerPointers: isSplit is false") return } val monitor = mInputMonitor ?: run { Log.w(TAG, "pilferThreeFingerPointers: mInputMonitor is null") return } val pilferMethod = monitor.javaClass.getMethod("pilferPointers") pilferMethod.invoke(monitor) } catch (e: Exception) { Log.w(TAG, "pilferThreeFingerPointers: ", e) } } private fun onSwipeRight() { log("单指边缘手势: 从左往右滑动 ") onSwipeDirection(GlobalGestureManager.VSwipeDirection.RIGHT) if (!ActivityStatusManager.isCanNotBackClearTopActivity()) { ReflectionUtil.injectGlobalBackKey(inputManager) } globalGestureSendSwipeDirection(GlobalGestureManager.VSwipeDirection.RIGHT) } private fun onSwipeLeft() { log("单指边缘手势: 从右往左滑动") onSwipeDirection(GlobalGestureManager.VSwipeDirection.LEFT) if (!ActivityStatusManager.isCanNotBackClearTopActivity()) { ReflectionUtil.injectGlobalBackKey(inputManager) } globalGestureSendSwipeDirection(GlobalGestureManager.VSwipeDirection.LEFT) } private fun onSwipeUp() { log("单指底边手势: 向上滑动 ${QS.isShowQS.value}") if (QS.isShowQS.value == 1f) { StatusBarManager.collapseQSPanel() } else { globalGestureSendSwipeDirection(GlobalGestureManager.VSwipeDirection.UP) } onSwipeDirection(GlobalGestureManager.VSwipeDirection.UP) } private fun globalGestureSendSwipeDirection( swipeDirection: GlobalGestureManager.VSwipeDirection ) { mJob?.cancel() mJob = serviceScope.launch { GlobalGestureManager.sendSwipeDirection( swipeDirection ) } } private fun onSwipeDown() { log("单指底边手势: 向下滑动") if (!mIsMoving) { onSwipeDirection(GlobalGestureManager.VSwipeDirection.DOWN) mIsMoving = true } } private fun onSwipeDirection(what: GlobalGestureManager.VSwipeDirection) { Log.w(TAG, "onSwipeDirection direction=${what.name}") if (what == GlobalGestureManager.VSwipeDirection.NONE) return val count = callbackList.beginBroadcast() log("onSwipeDirection direction=${what.name} count=$count") val cbs = ArrayList<ISystemGestureCallBack>(count) for (i in 0 until count) { cbs.add(callbackList.getBroadcastItem(i)) } callbackList.finishBroadcast() serviceScope.launch { cbs.forEach { try { it.onSwipe(what.value) } catch (e: Exception) { Log.w(TAG, "onSwipeDirection:", e) } } log("onSwipeDirection direction=${what.name} end") } } private fun onThreeFingerSwipe(what: GlobalGestureManager.VSwipeDirection) { Log.w(TAG, "onThreeFingerSwipe direction=${what.name}") if (what == GlobalGestureManager.VSwipeDirection.NONE) return val direction = when (what) { GlobalGestureManager.VSwipeDirection.THREE_FINGER_LEFT -> { GlobalGestureManager.THREE_FINGER_LEFT } GlobalGestureManager.VSwipeDirection.THREE_FINGER_RIGHT -> { GlobalGestureManager.THREE_FINGER_RIGHT } else -> return } val count = callbackList.beginBroadcast() log("onThreeFingerSwipe direction=${what.name} count=$count") val cbs = ArrayList<ISystemGestureCallBack>(count) for (i in 0 until count) { cbs.add(callbackList.getBroadcastItem(i)) } callbackList.finishBroadcast() serviceScope.launch { cbs.forEach { try { it.onThreeFingerSwipe( direction, mThreeFingerStartX[0], mThreeFingerStartY[0], mThreeFingerStartX[1], mThreeFingerStartY[1], mThreeFingerStartX[2], mThreeFingerStartY[2] ) } catch (e: Exception) { Log.w(TAG, "onThreeFingerSwipe:", e) } } log("onThreeFingerSwipe direction=${what.name} end") } } private fun startGlobalInputMonitoring() { try { val obj = getSystemService(INPUT_SERVICE) as? InputManager ?: return inputManager = obj val monitorMethod = InputManager::class.java.getMethod( "monitorGestureInput", String::class.java, Int::class.javaPrimitiveType ) mInputMonitor = monitorMethod.invoke( inputManager, "SystemUI-Global-Gesture", Display.DEFAULT_DISPLAY ) if (mInputMonitor == null) { Log.w(TAG, "Failed to get InputMonitor instance.") return } val getInputChannelMethod = mInputMonitor?.javaClass?.getMethod("getInputChannel") val inputChannel = getInputChannelMethod?.invoke(mInputMonitor) as? InputChannel mInputThread = HandlerThread("Input-Thread", Process.THREAD_PRIORITY_FOREGROUND).apply { start() } mInputHandler = Handler(mInputThread!!.looper) handleReceiver(inputChannel) } catch (e: Exception) { Log.w(TAG, "反射初始化 InputMonitor 失败,检查系统签名权限:", e) } } fun handleReceiver(inputChannel: InputChannel?) { if (mInputThread?.looper == null) return mInputEventReceiver = object : InputEventReceiver(inputChannel, mInputThread!!.looper) { override fun onInputEvent(event: InputEvent) { if (event !is MotionEvent) { finishInputEvent(event, true) return } val copiedEvent = MotionEvent.obtain(event) finishInputEvent(event, true) try { if (isSkipInputEvent(copiedEvent)) return touch(copiedEvent) } catch (e: Exception) { Log.w(TAG, "handleReceiver:", e) } finally { copiedEvent.recycle() } } } } /** 手势处理 */ private fun touch(event: MotionEvent) { val isTouchEvent = (event.source and android.view.InputDevice.SOURCE_TOUCHSCREEN) != 0 val action = event.actionMasked if (isTouchEvent && action != MotionEvent.ACTION_HOVER_ENTER && action != MotionEvent.ACTION_HOVER_MOVE && action != MotionEvent.ACTION_HOVER_EXIT ) { if (action == MotionEvent.ACTION_DOWN) { mPendingThreeFingerGesture = GlobalGestureManager.VSwipeDirection.NONE mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.NONE mIsThreeFingerTracking = false mThreeFingerDispatched = false } val threeFingerHandled = checkThreeFingerGesture(event) if (!threeFingerHandled && !mIsThreeFingerTracking) { mGestureDetector?.onTouchEvent(event) } if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { if (action == MotionEvent.ACTION_UP && !threeFingerHandled) { when (mPendingSingleGesture) { GlobalGestureManager.VSwipeDirection.UP -> { onSwipeUp() } GlobalGestureManager.VSwipeDirection.DOWN -> { onSwipeDown() } GlobalGestureManager.VSwipeDirection.LEFT -> { onSwipeLeft() } GlobalGestureManager.VSwipeDirection.RIGHT -> { onSwipeRight() } else -> {} } } mPendingThreeFingerGesture = GlobalGestureManager.VSwipeDirection.NONE mPendingSingleGesture = GlobalGestureManager.VSwipeDirection.NONE mIsThreeFingerTracking = false mThreeFingerDispatched = false mIsMoving = false } } } /** 拦截手势 */ private fun isSkipInputEvent(event: MotionEvent): Boolean { val action = event.actionMasked log("isSkipInputEvent action=$action, x=${event.x} y=${event.y}") if (mIsInterceptActive) { if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { mIsInterceptActive = false isFullScreenSkip = false log("isSkipInputEvent: intercept session end") } return true } when (action) { MotionEvent.ACTION_DOWN -> { isForbidGlobalGesture = InteractionAPIManager.isGlobalGestureDisabled() mInterceptDownX = event.x mInterceptDownY = event.y isFullScreenSkip = !SystemBarManager.isShowSystemBar() log("isSkipInputEvent isFullScreenSkip=$isFullScreenSkip, isForbidGlobalGesture=$isForbidGlobalGesture") if ((isForbidGlobalGesture || isFullScreenSkip) && isInEdgeZone(mInterceptDownX, mInterceptDownY) ) { mIsInterceptActive = true log("isSkipInputEvent: activate intercept session") return true } } MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { mIsInterceptActive = false isFullScreenSkip = false } } return false } private fun isInEdgeZone(x: Float, y: Float): Boolean { if (y < statusHeight) return true if (y > mScreenHeight - barHeight) return true val inMiddleVertical = y > statusHeight && y < mScreenHeight - barHeight if (inMiddleVertical && x < GlobalGestureManager.EDGE_ZONE_WIDTH_PX) return true if (inMiddleVertical && x > mScreenWidth - GlobalGestureManager.EDGE_ZONE_WIDTH_PX) return true return false } private fun isSplit() = Settings.Global.getInt( contentResolver, GLOBAL_LAUNCHER_SPLIT_STATE, 0 ) == 1 private val binder = object : ISystemUIGestureProxy.Stub() { override fun registerCallBack(callback: ISystemGestureCallBack?) { if (callback != null) { callbackList.register(callback) log("Launcher 注册回调成功") } } override fun unRegisterCallBack(callback: ISystemGestureCallBack?) { if (callback != null) { callbackList.unregister(callback) log("Launcher 注销回调成功") } } } override fun onBind(intent: Intent?): IBinder? { log("onBind isMainProcess=$isMainProcess") return if (isMainProcess) { binder } else { stopSelf() null } } override fun onDestroy() { log("GlobalGestureService 停止!") serviceScope.cancel() callbackList.kill() mInputHandler?.post { mInputEventReceiver?.dispose() mInputEventReceiver = null } mInputThread?.quitSafely() mInputThread = null mInputHandler = null mInputMonitor?.let { try { val disposeMethod = it.javaClass.getMethod("dispose") disposeMethod.invoke(it) } catch (e: Exception) { Log.w(TAG, "onDestroy $e") } mInputMonitor = null } super.onDestroy() } private fun log(string: String) = Log.i(TAG, string) }
浙公网安备 33010602011771号