1. enable BootReceiver.

2. add PermissionRequestTransparentActivity.
3. opt const.
This commit is contained in:
csf 2023-02-27 23:07:52 +09:00
parent 7f8b6f656e
commit 63185a5bcb
7 changed files with 137 additions and 72 deletions

@ -11,22 +11,24 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />--> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application <application
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="RustDesk" android:label="RustDesk"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true" android:supportsRtl="true">
android:requestLegacyExternalStorage="true">
<receiver <receiver
android:name=".BootReceiver" android:name=".BootReceiver"
android:enabled="false" android:enabled="true"
android:exported="false"> android:exported="true">
<intent-filter android:priority="1000"> <intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" /> <action android:name="android.intent.action.QUICKBOOT_POWERON" />
<!--ACTION_BOOT_COMPLETED for debug test on no root device-->
<action android:name="com.carriez.flutter_hbb.DEBUG_BOOT_COMPLETED" />
</intent-filter> </intent-filter>
</receiver> </receiver>
@ -53,8 +55,6 @@
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -62,6 +62,11 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".PermissionRequestTransparentActivity"
android:excludeFromRecents="true"
android:theme="@style/Transparent" />
<service <service
android:name=".MainService" android:name=".MainService"
android:enabled="true" android:enabled="true"

@ -4,18 +4,25 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import android.util.Log
import android.widget.Toast import android.widget.Toast
const val DEBUG_BOOT_COMPLETED = "com.carriez.flutter_hbb.DEBUG_BOOT_COMPLETED"
class BootReceiver : BroadcastReceiver() { class BootReceiver : BroadcastReceiver() {
private val logTag = "tagBootReceiver"
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if ("android.intent.action.BOOT_COMPLETED" == intent.action){ Log.d(logTag, "onReceive ${intent.action}")
val it = Intent(context,MainService::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(it) context.startForegroundService(it)
}else{ } else {
context.startService(it) context.startService(it)
} }
} }

@ -7,12 +7,10 @@ package com.carriez.flutter_hbb
* Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG * Inspired by [droidVNC-NG] https://github.com/bk138/droidVNC-NG
*/ */
import android.app.Activity
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.media.projection.MediaProjectionManager
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import android.provider.Settings import android.provider.Settings
@ -23,7 +21,6 @@ import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
const val MEDIA_REQUEST_CODE = 42
class MainActivity : FlutterActivity() { class MainActivity : FlutterActivity() {
companion object { companion object {
@ -32,7 +29,6 @@ class MainActivity : FlutterActivity() {
private val channelTag = "mChannel" private val channelTag = "mChannel"
private val logTag = "mMainActivity" private val logTag = "mMainActivity"
private var mediaProjectionResultIntent: Intent? = null
private var mainService: MainService? = null private var mainService: MainService? = null
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
@ -58,7 +54,7 @@ class MainActivity : FlutterActivity() {
result.success(false) result.success(false)
return@setMethodCallHandler return@setMethodCallHandler
} }
getMediaProjection() requestMediaProjection()
result.success(true) result.success(true)
} }
"start_capture" -> { "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() { private fun initInput() {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
if (intent.resolveActivity(packageManager) != null) { 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?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == MEDIA_REQUEST_CODE) { if (requestCode == REQ_INVOKE_PERMISSION_ACTIVITY_MEDIA_PROJECTION && resultCode == RES_FAILED) {
if (resultCode == Activity.RESULT_OK && data != null) { flutterMethodChannel.invokeMethod("on_media_projection_canceled", null)
mediaProjectionResultIntent = data
initService()
} else {
flutterMethodChannel.invokeMethod("on_media_projection_canceled", null)
}
} }
} }

@ -43,10 +43,6 @@ import java.nio.ByteBuffer
import kotlin.math.max import kotlin.math.max
import kotlin.math.min 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_TITLE = "RustDesk"
const val DEFAULT_NOTIFY_TEXT = "Service is running" const val DEFAULT_NOTIFY_TEXT = "Service is running"
@ -195,6 +191,7 @@ class MainService : Service() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Log.d(logTag,"MainService onCreate")
HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply { HandlerThread("Service", Process.THREAD_PRIORITY_BACKGROUND).apply {
start() start()
serviceLooper = looper serviceLooper = looper
@ -203,6 +200,7 @@ class MainService : Service() {
updateScreenInfo(resources.configuration.orientation) updateScreenInfo(resources.configuration.orientation)
initNotification() initNotification()
startServer() startServer()
createForegroundNotification()
} }
override fun onDestroy() { override fun onDestroy() {
@ -277,22 +275,25 @@ class MainService : Service() {
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { 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) super.onStartCommand(intent, flags, startId)
if (intent?.action == INIT_SERVICE) { if (intent?.action == ACT_INIT_MEDIA_PROJECTION_AND_SERVICE) {
Log.d(logTag, "service starting:${startId}:${Thread.currentThread()}") Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}")
createForegroundNotification() val mediaProjectionManager =
val mMediaProjectionManager =
getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
intent.getParcelableExtra<Intent>(EXTRA_MP_DATA)?.let {
intent.getParcelableExtra<Intent>(EXT_MEDIA_PROJECTION_RES_INTENT)?.let {
mediaProjection = mediaProjection =
mMediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it) mediaProjectionManager.getMediaProjection(Activity.RESULT_OK, it)
checkMediaPermission() checkMediaPermission()
init(this) init(this)
_isReady = true _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) { override fun onConfigurationChanged(newConfig: Configuration) {
@ -300,6 +301,14 @@ class MainService : Service() {
updateScreenInfo(newConfig.orientation) 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") @SuppressLint("WrongConstant")
private fun createSurface(): Surface? { private fun createSurface(): Surface? {
return if (useVP9) { return if (useVP9) {
@ -653,8 +662,8 @@ class MainService : Service() {
@SuppressLint("UnspecifiedImmutableFlag") @SuppressLint("UnspecifiedImmutableFlag")
private fun genLoginRequestPendingIntent(res: Boolean): PendingIntent { private fun genLoginRequestPendingIntent(res: Boolean): PendingIntent {
val intent = Intent(this, MainService::class.java).apply { val intent = Intent(this, MainService::class.java).apply {
action = ACTION_LOGIN_REQ_NOTIFY action = ACT_LOGIN_REQ_NOTIFY
putExtra(EXTRA_LOGIN_REQ_NOTIFY, res) putExtra(EXT_LOGIN_REQ_NOTIFY, res)
} }
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.getService(this, 111, intent, FLAG_IMMUTABLE) PendingIntent.getService(this, 111, intent, FLAG_IMMUTABLE)

@ -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)
}
}
}

@ -12,8 +12,7 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS import android.provider.Settings.*
import android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat.getSystemService import androidx.core.content.ContextCompat.getSystemService
import com.hjq.permissions.Permission import com.hjq.permissions.Permission
@ -22,6 +21,21 @@ import java.nio.ByteBuffer
import java.util.* 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") @SuppressLint("ConstantLocale")
val LOCAL_NAME = Locale.getDefault().toString() val LOCAL_NAME = Locale.getDefault().toString()
val SCREEN_INFO = Info(0, 0, 1, 200) val SCREEN_INFO = Info(0, 0, 1, 200)
@ -59,9 +73,8 @@ fun requestPermission(context: Context, type: String) {
} }
"application_details_settings" -> { "application_details_settings" -> {
try { try {
context.startActivity(Intent().apply { context.startActivity(Intent(ACTION_APPLICATION_DETAILS_SETTINGS).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
action = "android.settings.APPLICATION_DETAILS_SETTINGS"
data = Uri.parse("package:" + context.packageName) data = Uri.parse("package:" + context.packageName)
}) })
} catch (e:Exception) { } catch (e:Exception) {

@ -15,4 +15,12 @@
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item> <item name="android:windowBackground">?android:colorBackground</item>
</style> </style>
<style name="Transparent" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
</resources> </resources>