mirror of
https://github.com/rustdesk/rustdesk.git
synced 2024-11-25 13:19:04 +08:00
android input add wheel;fix UI/service sync
This commit is contained in:
parent
839062ee6b
commit
569b102f99
@ -9,6 +9,20 @@ import android.util.Log
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import androidx.annotation.Keep
|
||||
import androidx.annotation.RequiresApi
|
||||
import java.util.*
|
||||
|
||||
const val LIFT_DOWN = 9
|
||||
const val LIFT_MOVE = 8
|
||||
const val LIFT_UP = 10
|
||||
const val RIGHT_UP = 18
|
||||
const val WHEEL_BUTTON_DOWN = 33
|
||||
const val WHEEL_BUTTON_UP = 34
|
||||
const val WHEEL_DOWN = 523331
|
||||
const val WHEEL_UP = 963
|
||||
|
||||
const val WHEEL_STEP = 120
|
||||
const val WHEEL_DURATION = 50L
|
||||
const val LONG_TAP_DELAY = 200L
|
||||
|
||||
class InputService : AccessibilityService() {
|
||||
|
||||
@ -26,10 +40,15 @@ class InputService : AccessibilityService() {
|
||||
|
||||
private val logTag = "input service"
|
||||
private var leftIsDown = false
|
||||
private var mPath = Path()
|
||||
private var mLastGestureStartTime = 0L
|
||||
private var touchPath = Path()
|
||||
private var lastTouchGestureStartTime = 0L
|
||||
private var mouseX = 0
|
||||
private var mouseY = 0
|
||||
private var timer = Timer()
|
||||
private var recentActionTask: TimerTask? = null
|
||||
|
||||
private val wheelActionsQueue = LinkedList<GestureDescription>()
|
||||
private var isWheelActionsPolling = false
|
||||
|
||||
@Keep
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
@ -46,13 +65,13 @@ class InputService : AccessibilityService() {
|
||||
_y
|
||||
}
|
||||
|
||||
if (!(mask == 9 || mask == 10)) {
|
||||
if (mask == 0 || mask == LIFT_MOVE) {
|
||||
mouseX = x * SCREEN_INFO.scale
|
||||
mouseY = y * SCREEN_INFO.scale
|
||||
}
|
||||
|
||||
// left button down ,was up
|
||||
if (mask == 9) {
|
||||
if (mask == LIFT_DOWN) {
|
||||
leftIsDown = true
|
||||
startGesture(mouseX, mouseY)
|
||||
return
|
||||
@ -64,43 +83,118 @@ class InputService : AccessibilityService() {
|
||||
}
|
||||
|
||||
// left up ,was down
|
||||
if (mask == 10) {
|
||||
if (mask == LIFT_UP) {
|
||||
leftIsDown = false
|
||||
endGesture(mouseX, mouseY)
|
||||
return
|
||||
}
|
||||
|
||||
if (mask == 18) {
|
||||
if (mask == RIGHT_UP) {
|
||||
performGlobalAction(GLOBAL_ACTION_BACK)
|
||||
return
|
||||
}
|
||||
|
||||
if (mask == 34) {
|
||||
performGlobalAction(GLOBAL_ACTION_HOME)
|
||||
// long WHEEL_BUTTON_DOWN -> GLOBAL_ACTION_RECENTS
|
||||
if (mask == WHEEL_BUTTON_DOWN) {
|
||||
timer.purge()
|
||||
recentActionTask = object : TimerTask() {
|
||||
override fun run() {
|
||||
performGlobalAction(GLOBAL_ACTION_RECENTS)
|
||||
recentActionTask = null
|
||||
}
|
||||
}
|
||||
timer.schedule(recentActionTask, LONG_TAP_DELAY)
|
||||
}
|
||||
|
||||
// wheel button up
|
||||
if (mask == WHEEL_BUTTON_UP) {
|
||||
if (recentActionTask != null) {
|
||||
recentActionTask!!.cancel()
|
||||
performGlobalAction(GLOBAL_ACTION_HOME)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (mask == WHEEL_DOWN) {
|
||||
if (mouseY < WHEEL_STEP) {
|
||||
return
|
||||
}
|
||||
val path = Path()
|
||||
path.moveTo(mouseX.toFloat(), mouseY.toFloat())
|
||||
path.lineTo(mouseX.toFloat(), (mouseY - WHEEL_STEP).toFloat())
|
||||
val stroke = GestureDescription.StrokeDescription(
|
||||
path,
|
||||
0,
|
||||
WHEEL_DURATION
|
||||
)
|
||||
val builder = GestureDescription.Builder()
|
||||
builder.addStroke(stroke)
|
||||
wheelActionsQueue.offer(builder.build())
|
||||
consumeWheelActions()
|
||||
|
||||
}
|
||||
|
||||
if (mask == WHEEL_UP) {
|
||||
if (mouseY < WHEEL_STEP) {
|
||||
return
|
||||
}
|
||||
val path = Path()
|
||||
path.moveTo(mouseX.toFloat(), mouseY.toFloat())
|
||||
path.lineTo(mouseX.toFloat(), (mouseY + WHEEL_STEP).toFloat())
|
||||
val stroke = GestureDescription.StrokeDescription(
|
||||
path,
|
||||
0,
|
||||
WHEEL_DURATION
|
||||
)
|
||||
val builder = GestureDescription.Builder()
|
||||
builder.addStroke(stroke)
|
||||
wheelActionsQueue.offer(builder.build())
|
||||
consumeWheelActions()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun consumeWheelActions() {
|
||||
if (isWheelActionsPolling) {
|
||||
return
|
||||
} else {
|
||||
isWheelActionsPolling = true
|
||||
}
|
||||
wheelActionsQueue.poll()?.let {
|
||||
dispatchGesture(it, null, null)
|
||||
timer.purge()
|
||||
timer.schedule(object : TimerTask() {
|
||||
override fun run() {
|
||||
isWheelActionsPolling = false
|
||||
consumeWheelActions()
|
||||
}
|
||||
}, WHEEL_DURATION + 10)
|
||||
} ?: let {
|
||||
isWheelActionsPolling = false
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
private fun startGesture(x: Int, y: Int) {
|
||||
mPath = Path()
|
||||
mPath.moveTo(x.toFloat(), y.toFloat())
|
||||
mLastGestureStartTime = System.currentTimeMillis()
|
||||
touchPath = Path()
|
||||
touchPath.moveTo(x.toFloat(), y.toFloat())
|
||||
lastTouchGestureStartTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private fun continueGesture(x: Int, y: Int) {
|
||||
mPath.lineTo(x.toFloat(), y.toFloat())
|
||||
touchPath.lineTo(x.toFloat(), y.toFloat())
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun endGesture(x: Int, y: Int) {
|
||||
try {
|
||||
mPath.lineTo(x.toFloat(), y.toFloat())
|
||||
var duration = System.currentTimeMillis() - mLastGestureStartTime
|
||||
touchPath.lineTo(x.toFloat(), y.toFloat())
|
||||
var duration = System.currentTimeMillis() - lastTouchGestureStartTime
|
||||
if (duration <= 0) {
|
||||
duration = 1
|
||||
}
|
||||
val stroke = GestureDescription.StrokeDescription(
|
||||
mPath,
|
||||
touchPath,
|
||||
0,
|
||||
duration
|
||||
)
|
||||
|
@ -30,6 +30,11 @@ class MainActivity : FlutterActivity() {
|
||||
@RequiresApi(Build.VERSION_CODES.M)
|
||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||
super.configureFlutterEngine(flutterEngine)
|
||||
if (MainService.isReady) {
|
||||
Intent(activity, MainService::class.java).also {
|
||||
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
}
|
||||
flutterMethodChannel = MethodChannel(
|
||||
flutterEngine.dartExecutor.binaryMessenger,
|
||||
channelTag
|
||||
@ -41,7 +46,7 @@ class MainActivity : FlutterActivity() {
|
||||
Intent(activity, MainService::class.java).also {
|
||||
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
if (mainService?.isReady == true) {
|
||||
if (MainService.isReady) {
|
||||
result.success(false)
|
||||
return@setMethodCallHandler
|
||||
}
|
||||
@ -93,7 +98,7 @@ class MainActivity : FlutterActivity() {
|
||||
)
|
||||
flutterMethodChannel.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "media", "value" to mainService?.isReady.toString())
|
||||
mapOf("name" to "media", "value" to MainService.isReady.toString())
|
||||
)
|
||||
result.success(true)
|
||||
}
|
||||
|
@ -47,8 +47,6 @@ const val DEFAULT_NOTIFY_TEXT = "Service is running"
|
||||
const val DEFAULT_NOTIFY_ID = 1
|
||||
const val NOTIFY_ID_OFFSET = 100
|
||||
|
||||
const val NOTIFY_TYPE_START_CAPTURE = "NOTIFY_TYPE_START_CAPTURE"
|
||||
|
||||
const val MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_VP9
|
||||
|
||||
// video const
|
||||
@ -141,15 +139,19 @@ class MainService : Service() {
|
||||
return translateLocale(LOCAL_NAME, input)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var _isReady = false
|
||||
private var _isStart = false
|
||||
val isReady: Boolean
|
||||
get() = _isReady
|
||||
val isStart: Boolean
|
||||
get() = _isStart
|
||||
}
|
||||
|
||||
private val logTag = "LOG_SERVICE"
|
||||
private val useVP9 = false
|
||||
private val binder = LocalBinder()
|
||||
private var _isReady = false
|
||||
private var _isStart = false
|
||||
val isReady: Boolean
|
||||
get() = _isReady
|
||||
val isStart: Boolean
|
||||
get() = _isStart
|
||||
|
||||
|
||||
// video
|
||||
private var mediaProjection: MediaProjection? = null
|
||||
@ -177,6 +179,15 @@ class MainService : Service() {
|
||||
startServer()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
InputService.ctx?.disableSelf()
|
||||
}
|
||||
InputService.ctx = null
|
||||
checkMediaPermission()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun updateScreenInfo() {
|
||||
var w: Int
|
||||
var h: Int
|
||||
@ -210,7 +221,6 @@ class MainService : Service() {
|
||||
refreshScreen()
|
||||
startCapture()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -242,10 +252,7 @@ class MainService : Service() {
|
||||
checkMediaPermission()
|
||||
init(this)
|
||||
_isReady = true
|
||||
} ?: let {
|
||||
}
|
||||
// } else if (intent?.action == ACTION_LOGIN_REQ_NOTIFY) {
|
||||
// val notifyLoginRes = intent.getBooleanExtra(EXTRA_LOGIN_REQ_NOTIFY, false)
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
@ -345,7 +352,10 @@ class MainService : Service() {
|
||||
|
||||
mediaProjection = null
|
||||
checkMediaPermission()
|
||||
stopService(Intent(this, InputService::class.java)) // close input service maybe not work
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
InputService.ctx?.disableSelf()
|
||||
}
|
||||
InputService.ctx = null
|
||||
stopForeground(true)
|
||||
stopSelf()
|
||||
}
|
||||
@ -357,6 +367,12 @@ class MainService : Service() {
|
||||
mapOf("name" to "media", "value" to isReady.toString())
|
||||
)
|
||||
}
|
||||
Handler(Looper.getMainLooper()).post {
|
||||
MainActivity.flutterMethodChannel.invokeMethod(
|
||||
"on_state_changed",
|
||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||
)
|
||||
}
|
||||
return isReady
|
||||
}
|
||||
|
||||
|
@ -248,6 +248,8 @@ class AccessibilityListener extends StatelessWidget {
|
||||
pointer: evt.pointer + offset,
|
||||
size: 0.1,
|
||||
position: evt.position));
|
||||
GestureBinding.instance!.handlePointerEvent(PointerRemovedEvent(
|
||||
pointer: evt.pointer + offset, position: evt.position));
|
||||
}
|
||||
},
|
||||
onPointerMove: (evt) {
|
||||
|
@ -182,6 +182,7 @@ class ServerModel with ChangeNotifier {
|
||||
await FFI.invokeMethod("init_service");
|
||||
FFI.setByName("start_service");
|
||||
getIDPasswd();
|
||||
updateClientState();
|
||||
}
|
||||
|
||||
Future<Null> stopService() async {
|
||||
@ -281,12 +282,13 @@ class ServerModel with ChangeNotifier {
|
||||
try {
|
||||
final List clientsJson = jsonDecode(res);
|
||||
for (var clientJson in clientsJson) {
|
||||
final client = Client.fromJson(jsonDecode(clientJson));
|
||||
final client = Client.fromJson(clientJson);
|
||||
_clients[client.id] = client;
|
||||
}
|
||||
|
||||
notifyListeners();
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
debugPrint("Failed to updateClientState:$e");
|
||||
}
|
||||
}
|
||||
|
||||
loginRequest(Map<String, dynamic> evt) {
|
||||
|
Loading…
Reference in New Issue
Block a user