mirror of
https://github.com/rustdesk/rustdesk.git
synced 2025-01-19 00:13:01 +08:00
update input
This commit is contained in:
parent
668b34c228
commit
288825f007
@ -33,7 +33,7 @@ apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
// ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF]
|
||||
ndkVersion '22.1.7171670' // * 仅个人使用 存在多版本NDK无法自动选择 需要使用此配置指定NDK版本 [CSF]
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
}
|
||||
|
@ -1,51 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.carriez.flutter_hbb">
|
||||
<application
|
||||
android:label="RustDesk"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<!-- Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name=".MainService"
|
||||
android:enabled="true"
|
||||
android:foregroundServiceType="mediaProjection"/>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
</manifest>
|
||||
|
||||
<application
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="RustDesk">
|
||||
<service
|
||||
android:name=".InputService"
|
||||
android:enabled="true"
|
||||
android:exported="false"
|
||||
android:label="RustDesk Input"
|
||||
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.accessibilityservice.AccessibilityService" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accessibilityservice"
|
||||
android:resource="@xml/accessibility_service_config" />
|
||||
</service>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:theme="@style/LaunchTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
||||
<!--
|
||||
Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme" />
|
||||
<!--
|
||||
Displays an Android View that continues showing the launch screen
|
||||
Drawable until Flutter paints its first frame, then this splash
|
||||
screen fades out. A splash screen is useful to avoid any visual
|
||||
gap between the end of Android's launch screen and the painting of
|
||||
Flutter's first frame.
|
||||
-->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.SplashScreenDrawable"
|
||||
android:resource="@drawable/launch_background" />
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".MainService"
|
||||
android:enabled="true"
|
||||
android:foregroundServiceType="mediaProjection" />
|
||||
<!--
|
||||
Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java
|
||||
-->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -0,0 +1,106 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.accessibilityservice.GestureDescription
|
||||
import android.content.Context
|
||||
import android.graphics.Path
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import androidx.annotation.RequiresApi
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class InputService : AccessibilityService() {
|
||||
// companion object {
|
||||
// var inputService:InputService? = null
|
||||
// }
|
||||
private val logTag = "input service"
|
||||
private var leftIsDown = false
|
||||
private var mPath = Path()
|
||||
private var mLastGestureStartTime = 0L
|
||||
private var mouseX = 0
|
||||
private var mouseY = 0
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun mouseInput(mask: Int, _x: Int, _y: Int) {
|
||||
Log.w(logTag, "got mouse input:x:$_x ,y:$_y ,mask:$mask ")
|
||||
|
||||
// TODO 临时倍数
|
||||
// TODO 按键抬起按下时候 x y 都是0
|
||||
if ( !(mask == 9 || mask == 10) ) {
|
||||
mouseX = _x * 2
|
||||
mouseY = _y * 2
|
||||
}
|
||||
|
||||
// left button down ,was up
|
||||
if (mask == 9){
|
||||
leftIsDown = true
|
||||
startGesture(mouseX,mouseY)
|
||||
}
|
||||
|
||||
// left down ,was down
|
||||
if (mask == 9){
|
||||
continueGesture(mouseX,mouseY)
|
||||
}
|
||||
|
||||
// left up ,was down
|
||||
if (mask == 10){
|
||||
leftIsDown = false
|
||||
endGesture(mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startGesture(x: Int, y: Int) {
|
||||
mPath = Path()
|
||||
mPath.moveTo(x.toFloat(), y.toFloat())
|
||||
mLastGestureStartTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private fun continueGesture(x: Int, y: Int) {
|
||||
mPath.lineTo(x.toFloat(), y.toFloat())
|
||||
}
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
private fun endGesture(x: Int, y: Int) {
|
||||
mPath.lineTo(x.toFloat(), y.toFloat())
|
||||
val stroke = GestureDescription.StrokeDescription(
|
||||
mPath,
|
||||
0,
|
||||
System.currentTimeMillis() - mLastGestureStartTime
|
||||
)
|
||||
val builder = GestureDescription.Builder()
|
||||
builder.addStroke(stroke)
|
||||
Log.d(logTag, "end gesture $x $y")
|
||||
dispatchGesture(builder.build(), object : GestureResultCallback() {
|
||||
override fun onCompleted(gestureDescription: GestureDescription) {
|
||||
super.onCompleted(gestureDescription)
|
||||
Log.d(logTag, "滑动成功")
|
||||
}
|
||||
|
||||
override fun onCancelled(gestureDescription: GestureDescription) {
|
||||
super.onCancelled(gestureDescription)
|
||||
Log.d(logTag, "滑动失败 ")
|
||||
}
|
||||
}, null)
|
||||
}
|
||||
|
||||
external fun init(ctx: Context)
|
||||
|
||||
init {
|
||||
System.loadLibrary("rustdesk")
|
||||
}
|
||||
|
||||
private val LOG_TAG = "INPUT_LOG"
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
override fun onServiceConnected() {
|
||||
super.onServiceConnected()
|
||||
Log.d(LOG_TAG,"onServiceConnected!")
|
||||
init(this)
|
||||
}
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun onInterrupt() {
|
||||
// TODO("Not yet implemented")
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
package com.carriez.flutter_hbb
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.media.projection.MediaProjectionManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.PersistableBundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.annotation.RequiresApi
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
@ -50,6 +52,10 @@ class MainActivity : FlutterActivity() {
|
||||
mStopService()
|
||||
result.success(true)
|
||||
}
|
||||
"checkInput" ->{
|
||||
checkInput()
|
||||
result.success(true)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@ -96,6 +102,25 @@ class MainActivity : FlutterActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInput() {
|
||||
AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setTitle("检查Input服务")
|
||||
.setMessage("请开启相关服务")
|
||||
.setPositiveButton("Yes") { dialog, which ->
|
||||
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null) startActivityForResult(
|
||||
intent,
|
||||
11
|
||||
) else AlertDialog.Builder(this)
|
||||
.setTitle("错误")
|
||||
.setMessage("无法启动服务")
|
||||
.show()
|
||||
}
|
||||
.setNegativeButton("No") { dialog, which -> }
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (resultCode == Activity.RESULT_OK && data != null) {
|
||||
|
@ -34,11 +34,11 @@ const val M_KEY_FRAME_RATE = 30
|
||||
|
||||
class MainService : Service() {
|
||||
|
||||
fun rustGetRaw():ByteArray{
|
||||
fun rustGetRaw(): ByteArray {
|
||||
return rawByteArray!!
|
||||
}
|
||||
|
||||
external fun init(ctx:Context)
|
||||
external fun init(ctx: Context)
|
||||
|
||||
init {
|
||||
System.loadLibrary("rustdesk")
|
||||
@ -49,7 +49,7 @@ class MainService : Service() {
|
||||
private var surface: Surface? = null
|
||||
private val singleThread = Executors.newSingleThreadExecutor()
|
||||
private var mEncoder: MediaCodec? = null
|
||||
private var rawByteArray :ByteArray? = null
|
||||
private var rawByteArray: ByteArray? = null
|
||||
|
||||
override fun onBind(intent: Intent): IBinder? {
|
||||
return null
|
||||
@ -90,7 +90,8 @@ class MainService : Service() {
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
lateinit var mImageReader:ImageReader // * 注意 这里要成为成员变量,防止被回收 https://www.cnblogs.com/yongdaimi/p/11004560.html
|
||||
lateinit var mImageReader: ImageReader // * 注意 这里要成为成员变量,防止被回收 https://www.cnblogs.com/yongdaimi/p/11004560.html
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private fun startRecorder() {
|
||||
@ -100,23 +101,23 @@ class MainService : Service() {
|
||||
mImageReader =
|
||||
ImageReader.newInstance(FIXED_WIDTH, FIXED_HEIGHT, PixelFormat.RGBA_8888, 2) // 至少是2
|
||||
mImageReader.setOnImageAvailableListener({ imageReader: ImageReader ->
|
||||
Log.d(logTag, "on image")
|
||||
try {
|
||||
imageReader.acquireLatestImage().use { image ->
|
||||
if (image == null) return@setOnImageAvailableListener
|
||||
val planes = image.planes
|
||||
val buffer = planes[0].buffer
|
||||
buffer.rewind()
|
||||
// 这里注意 处理不当会引发OOM
|
||||
if (rawByteArray == null){
|
||||
rawByteArray = ByteArray(buffer.capacity())
|
||||
buffer.get(rawByteArray!!)
|
||||
}else{
|
||||
buffer.get(rawByteArray!!)
|
||||
}
|
||||
// Log.d(logTag, "on image")
|
||||
try {
|
||||
imageReader.acquireLatestImage().use { image ->
|
||||
if (image == null) return@setOnImageAvailableListener
|
||||
val planes = image.planes
|
||||
val buffer = planes[0].buffer
|
||||
buffer.rewind()
|
||||
// 这里注意 处理不当会引发OOM
|
||||
if (rawByteArray == null) {
|
||||
rawByteArray = ByteArray(buffer.capacity())
|
||||
buffer.get(rawByteArray!!)
|
||||
} else {
|
||||
buffer.get(rawByteArray!!)
|
||||
}
|
||||
} catch (ignored: java.lang.Exception) {
|
||||
}
|
||||
} catch (ignored: java.lang.Exception) {
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
imageReader.discardFreeBuffers()
|
||||
}
|
||||
|
4
android/app/src/main/res/values/strings.xml
Normal file
4
android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<resources>
|
||||
<string name="app_name">RustDesk</string>
|
||||
<string name="accessibility_service_description">测试服务 输入服务</string>
|
||||
</resources>
|
@ -0,0 +1,6 @@
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeWindowsChanged"
|
||||
android:accessibilityFlags="flagDefault"
|
||||
android:notificationTimeout="50"
|
||||
android:description="@string/accessibility_service_description"
|
||||
android:canPerformGestures="true"/>
|
@ -7,7 +7,7 @@ buildscript {
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.1.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||
classpath 'com.google.gms:google-services:4.3.3'
|
||||
}
|
||||
}
|
||||
|
@ -100,7 +100,8 @@ class _HomePageState extends State<HomePage> {
|
||||
getPeers(),
|
||||
ElevatedButton(onPressed:_toAndroidGetPer, child: Text("获取权限事件")),
|
||||
ElevatedButton(onPressed:_toAndroidStartSer, child: Text("开启录屏服务")),
|
||||
ElevatedButton(onPressed:_toAndroidStopSer, child: Text("停止录屏服务"))
|
||||
ElevatedButton(onPressed:_toAndroidStopSer, child: Text("停止录屏服务")),
|
||||
ElevatedButton(onPressed:_toAndroidCheckInput, child: Text("检查输入权限")),
|
||||
]),
|
||||
));
|
||||
}
|
||||
@ -116,6 +117,10 @@ class _HomePageState extends State<HomePage> {
|
||||
bool res = await toAndroidChannel.invokeMethod("stopSer");
|
||||
debugPrint("_toAndroidStopSer:$res");
|
||||
}
|
||||
Future<Null> _toAndroidCheckInput() async{
|
||||
bool res = await toAndroidChannel.invokeMethod("checkInput");
|
||||
debugPrint("_toAndroidStopSer:$res");
|
||||
}
|
||||
|
||||
void onConnect() {
|
||||
var id = _idController.text.trim();
|
||||
|
Loading…
Reference in New Issue
Block a user