diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml
index ede6353ef..6f646b913 100644
--- a/flutter/android/app/src/main/AndroidManifest.xml
+++ b/flutter/android/app/src/main/AndroidManifest.xml
@@ -11,22 +11,24 @@
-
+
+ android:supportsRtl="true">
+ android:enabled="true"
+ android:exported="true">
+
+
@@ -53,8 +55,6 @@
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize">
-
-
@@ -62,6 +62,11 @@
+
+
-
+
\ No newline at end of file
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
index 328701567..a49dcc326 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
@@ -4,18 +4,25 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
+import android.util.Log
import android.widget.Toast
+const val DEBUG_BOOT_COMPLETED = "com.carriez.flutter_hbb.DEBUG_BOOT_COMPLETED"
+
class BootReceiver : BroadcastReceiver() {
+ private val logTag = "tagBootReceiver"
+
override fun onReceive(context: Context, intent: Intent) {
- if ("android.intent.action.BOOT_COMPLETED" == intent.action){
- val it = Intent(context,MainService::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ Log.d(logTag, "onReceive ${intent.action}")
+
+ if (Intent.ACTION_BOOT_COMPLETED == intent.action || DEBUG_BOOT_COMPLETED == intent.action) {
+ val it = Intent(context, MainService::class.java).apply {
+ action = ACT_INIT_MEDIA_PROJECTION_AND_SERVICE
}
- Toast.makeText(context, "RustDesk is Open", Toast.LENGTH_LONG).show();
+ Toast.makeText(context, "RustDesk is Open", Toast.LENGTH_LONG).show()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(it)
- }else{
+ } else {
context.startService(it)
}
}
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
index fd340f7ed..73dbd2dad 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainActivity.kt
@@ -7,12 +7,10 @@ package com.carriez.flutter_hbb
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
*/
-import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
-import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.IBinder
import android.provider.Settings
@@ -23,7 +21,6 @@ import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
-const val MEDIA_REQUEST_CODE = 42
class MainActivity : FlutterActivity() {
companion object {
@@ -32,7 +29,6 @@ class MainActivity : FlutterActivity() {
private val channelTag = "mChannel"
private val logTag = "mMainActivity"
- private var mediaProjectionResultIntent: Intent? = null
private var mainService: MainService? = null
@RequiresApi(Build.VERSION_CODES.M)
@@ -58,7 +54,7 @@ class MainActivity : FlutterActivity() {
result.success(false)
return@setMethodCallHandler
}
- getMediaProjection()
+ requestMediaProjection()
result.success(true)
}
"start_capture" -> {
@@ -153,35 +149,6 @@ class MainActivity : FlutterActivity() {
}
}
- private fun getMediaProjection() {
- val mMediaProjectionManager =
- getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
- val mIntent = mMediaProjectionManager.createScreenCaptureIntent()
- startActivityForResult(mIntent, MEDIA_REQUEST_CODE)
- }
-
- private fun initService() {
- if (mediaProjectionResultIntent == null) {
- Log.w(logTag, "initService fail,mediaProjectionResultIntent is null")
- return
- }
- Log.d(logTag, "Init service")
- val serviceIntent = Intent(this, MainService::class.java)
- serviceIntent.action = INIT_SERVICE
- serviceIntent.putExtra(EXTRA_MP_DATA, mediaProjectionResultIntent)
-
- launchMainService(serviceIntent)
- }
-
- private fun launchMainService(intent: Intent) {
- // TEST api < O
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(intent)
- } else {
- startService(intent)
- }
- }
-
private fun initInput() {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
if (intent.resolveActivity(packageManager) != null) {
@@ -200,15 +167,17 @@ class MainActivity : FlutterActivity() {
}
}
+ private fun requestMediaProjection() {
+ val intent = Intent(this, PermissionRequestTransparentActivity::class.java).apply {
+ action = ACT_REQUEST_MEDIA_PROJECTION
+ }
+ startActivityForResult(intent, REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION)
+ }
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
- if (requestCode == MEDIA_REQUEST_CODE) {
- if (resultCode == Activity.RESULT_OK && data != null) {
- mediaProjectionResultIntent = data
- initService()
- } else {
- flutterMethodChannel.invokeMethod("on_media_projection_canceled", null)
- }
+ if (requestCode == REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION && resultCode == RES_FAILED) {
+ flutterMethodChannel.invokeMethod("on_media_projection_canceled", null)
}
}
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
index cf8e12e92..e28311964 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
@@ -43,10 +43,6 @@ import java.nio.ByteBuffer
import kotlin.math.max
import kotlin.math.min
-const val EXTRA_MP_DATA = "mp_intent"
-const val INIT_SERVICE = "init_service"
-const val ACTION_LOGIN_REQ_NOTIFY = "ACTION_LOGIN_REQ_NOTIFY"
-const val EXTRA_LOGIN_REQ_NOTIFY = "EXTRA_LOGIN_REQ_NOTIFY"
const val DEFAULT_NOTIFY_TITLE = "RustDesk"
const val DEFAULT_NOTIFY_TEXT = "Service is running"
@@ -195,6 +191,7 @@ class MainService : Service() {
override fun onCreate() {
super.onCreate()
+ Log.d(logTag,"MainService onCreate")
HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
serviceLooper = looper
@@ -203,6 +200,7 @@ class MainService : Service() {
updateScreenInfo(resources.configuration.orientation)
initNotification()
startServer()
+ createForegroundNotification()
}
override fun onDestroy() {
@@ -277,22 +275,25 @@ class MainService : Service() {
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
- Log.d("whichService", "this service:${Thread.currentThread()}")
+ Log.d("whichService", "this service: ${Thread.currentThread()}")
super.onStartCommand(intent, flags, startId)
- if (intent?.action == INIT_SERVICE) {
- Log.d(logTag, "service starting:${startId}:${Thread.currentThread()}")
- createForegroundNotification()
- val mMediaProjectionManager =
+ if (intent?.action == ACT_INIT_MEDIA_PROJECTION_AND_SERVICE) {
+ Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}")
+ val mediaProjectionManager =
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
- intent.getParcelableExtra(EXTRA_MP_DATA)?.let {
+
+ intent.getParcelableExtra(EXT_MEDIA_PROJECTION_RES_INTENT)?.let {
mediaProjection =
- mMediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it)
+ mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it)
checkMediaPermission()
init(this)
_isReady = true
+ } ?: let {
+ Log.d(logTag, "getParcelableExtra intent null, invoke requestMediaProjection")
+ requestMediaProjection()
}
}
- return START_NOT_STICKY // don't use sticky (auto restart),the new service (from auto restart) will lose control
+ return START_NOT_STICKY // don't use sticky (auto restart), the new service (from auto restart) will lose control
}
override fun onConfigurationChanged(newConfig: Configuration) {
@@ -300,6 +301,14 @@ class MainService : Service() {
updateScreenInfo(newConfig.orientation)
}
+ private fun requestMediaProjection() {
+ val intent = Intent(this, PermissionRequestTransparentActivity::class.java).apply {
+ action = ACT_REQUEST_MEDIA_PROJECTION
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK
+ }
+ startActivity(intent)
+ }
+
@SuppressLint("WrongConstant")
private fun createSurface(): Surface? {
return if (useVP9) {
@@ -653,8 +662,8 @@ class MainService : Service() {
@SuppressLint("UnspecifiedImmutableFlag")
private fun genLoginRequestPendingIntent(res: Boolean): PendingIntent {
val intent = Intent(this, MainService::class.java).apply {
- action = ACTION_LOGIN_REQ_NOTIFY
- putExtra(EXTRA_LOGIN_REQ_NOTIFY, res)
+ action = ACT_LOGIN_REQ_NOTIFY
+ putExtra(EXT_LOGIN_REQ_NOTIFY, res)
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getService(this, 111, intent, FLAG_IMMUTABLE)
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/PermissionRequestTransparentActivity.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/PermissionRequestTransparentActivity.kt
new file mode 100644
index 000000000..3beb7ec6b
--- /dev/null
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/PermissionRequestTransparentActivity.kt
@@ -0,0 +1,54 @@
+package com.carriez.flutter_hbb
+
+import android.app.Activity
+import android.content.Intent
+import android.media.projection.MediaProjectionManager
+import android.os.Build
+import android.os.Bundle
+import android.util.Log
+
+class PermissionRequestTransparentActivity: Activity() {
+ private val logTag = "permissionRequest"
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ Log.d(logTag, "onCreate PermissionRequestTransparentActivity: intent.action: ${intent.action}")
+
+ when (intent.action) {
+ ACT_REQUEST_MEDIA_PROJECTION -> {
+ val mediaProjectionManager =
+ getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
+ val intent = mediaProjectionManager.createScreenCaptureIntent()
+ startActivityForResult(intent, REQ_REQUEST_MEDIA_PROJECTION)
+ }
+ else -> finish()
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ if (requestCode == REQ_REQUEST_MEDIA_PROJECTION) {
+ if (resultCode == RESULT_OK && data != null) {
+ launchService(data)
+ } else {
+ setResult(RES_FAILED)
+ }
+ }
+
+ finish()
+ }
+
+ private fun launchService(mediaProjectionResultIntent: Intent) {
+ Log.d(logTag, "Launch MainService")
+ val serviceIntent = Intent(this, MainService::class.java)
+ serviceIntent.action = ACT_INIT_MEDIA_PROJECTION_AND_SERVICE
+ serviceIntent.putExtra(EXT_MEDIA_PROJECTION_RES_INTENT, mediaProjectionResultIntent)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startForegroundService(serviceIntent)
+ } else {
+ startService(serviceIntent)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt
index 4bf244a06..a812686ec 100644
--- a/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt
+++ b/flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/common.kt
@@ -12,8 +12,7 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.PowerManager
-import android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
-import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
+import android.provider.Settings.*
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getSystemService
import com.hjq.permissions.Permission
@@ -22,6 +21,21 @@ import java.nio.ByteBuffer
import java.util.*
+// intent action, extra
+const val ACT_REQUEST_MEDIA_PROJECTION = "REQUEST_MEDIA_PROJECTION"
+const val ACT_INIT_MEDIA_PROJECTION_AND_SERVICE = "INIT_MEDIA_PROJECTION_AND_SERVICE"
+const val ACT_LOGIN_REQ_NOTIFY = "LOGIN_REQ_NOTIFY"
+const val EXT_MEDIA_PROJECTION_RES_INTENT = "MEDIA_PROJECTION_RES_INTENT"
+const val EXT_LOGIN_REQ_NOTIFY = "LOGIN_REQ_NOTIFY"
+
+// Activity requestCode
+const val REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION = 101
+const val REQ_REQUEST_MEDIA_PROJECTION = 201
+
+// Activity responseCode
+const val RES_FAILED = -100
+
+
@SuppressLint("ConstantLocale")
val LOCAL_NAME = Locale.getDefault().toString()
val SCREEN_INFO = Info(0, 0, 1, 200)
@@ -59,9 +73,8 @@ fun requestPermission(context: Context, type: String) {
}
"application_details_settings" -> {
try {
- context.startActivity(Intent().apply {
+ context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- action = "android.settings.APPLICATION_DETAILS_SETTINGS"
data = Uri.parse("package:" + context.packageName)
})
} catch (e:Exception) {
diff --git a/flutter/android/app/src/main/res/values/styles.xml b/flutter/android/app/src/main/res/values/styles.xml
index d74aa35c2..146267c91 100644
--- a/flutter/android/app/src/main/res/values/styles.xml
+++ b/flutter/android/app/src/main/res/values/styles.xml
@@ -15,4 +15,12 @@
+