mirror of
https://github.com/weyne85/rustdesk.git
synced 2025-10-29 17:00:05 +00:00
Merge pull request #3425 from Heap-Hop/android_start_on_boot
Android start on boot
This commit is contained in:
commit
b10e76f67b
@ -11,22 +11,25 @@
|
|||||||
<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" />
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||||
|
|
||||||
<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 +56,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 +63,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"
|
||||||
@ -75,4 +81,4 @@
|
|||||||
android:value="2" />
|
android:value="2" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
@ -1,21 +1,45 @@
|
|||||||
package com.carriez.flutter_hbb
|
package com.carriez.flutter_hbb
|
||||||
|
|
||||||
|
import android.Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||||
|
import android.Manifest.permission.SYSTEM_ALERT_WINDOW
|
||||||
import android.content.BroadcastReceiver
|
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
|
||||||
|
import com.hjq.permissions.XXPermissions
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// check SharedPreferences config
|
||||||
|
val prefs = context.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE)
|
||||||
|
if (!prefs.getBoolean(KEY_START_ON_BOOT_OPT, false)) {
|
||||||
|
Log.d(logTag, "KEY_START_ON_BOOT_OPT is false")
|
||||||
|
return
|
||||||
}
|
}
|
||||||
Toast.makeText(context, "RustDesk is Open", Toast.LENGTH_LONG).show();
|
// check pre-permission
|
||||||
|
if (!XXPermissions.isGranted(context, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, SYSTEM_ALERT_WINDOW)){
|
||||||
|
Log.d(logTag, "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS or SYSTEM_ALERT_WINDOW is not granted")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val it = Intent(context, MainService::class.java).apply {
|
||||||
|
action = ACT_INIT_MEDIA_PROJECTION_AND_SERVICE
|
||||||
|
putExtra(EXT_INIT_FROM_BOOT, true)
|
||||||
|
}
|
||||||
|
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,35 +7,29 @@ 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.util.Log
|
import android.util.Log
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.annotation.RequiresApi
|
import com.hjq.permissions.XXPermissions
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
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 {
|
||||||
lateinit var flutterMethodChannel: MethodChannel
|
var flutterMethodChannel: MethodChannel? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
if (MainService.isReady) {
|
if (MainService.isReady) {
|
||||||
@ -46,169 +40,32 @@ class MainActivity : FlutterActivity() {
|
|||||||
flutterMethodChannel = MethodChannel(
|
flutterMethodChannel = MethodChannel(
|
||||||
flutterEngine.dartExecutor.binaryMessenger,
|
flutterEngine.dartExecutor.binaryMessenger,
|
||||||
channelTag
|
channelTag
|
||||||
).apply {
|
)
|
||||||
// make sure result is set, otherwise flutter will await forever
|
initFlutterChannel(flutterMethodChannel!!)
|
||||||
setMethodCallHandler { call, result ->
|
|
||||||
when (call.method) {
|
|
||||||
"init_service" -> {
|
|
||||||
Intent(activity, MainService::class.java).also {
|
|
||||||
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE)
|
|
||||||
}
|
|
||||||
if (MainService.isReady) {
|
|
||||||
result.success(false)
|
|
||||||
return@setMethodCallHandler
|
|
||||||
}
|
|
||||||
getMediaProjection()
|
|
||||||
result.success(true)
|
|
||||||
}
|
|
||||||
"start_capture" -> {
|
|
||||||
mainService?.let {
|
|
||||||
result.success(it.startCapture())
|
|
||||||
} ?: let {
|
|
||||||
result.success(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"stop_service" -> {
|
|
||||||
Log.d(logTag, "Stop service")
|
|
||||||
mainService?.let {
|
|
||||||
it.destroy()
|
|
||||||
result.success(true)
|
|
||||||
} ?: let {
|
|
||||||
result.success(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"check_permission" -> {
|
|
||||||
if (call.arguments is String) {
|
|
||||||
result.success(checkPermission(context, call.arguments as String))
|
|
||||||
} else {
|
|
||||||
result.success(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"request_permission" -> {
|
|
||||||
if (call.arguments is String) {
|
|
||||||
requestPermission(context, call.arguments as String)
|
|
||||||
result.success(true)
|
|
||||||
} else {
|
|
||||||
result.success(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"check_video_permission" -> {
|
|
||||||
mainService?.let {
|
|
||||||
result.success(it.checkMediaPermission())
|
|
||||||
} ?: let {
|
|
||||||
result.success(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"check_service" -> {
|
|
||||||
flutterMethodChannel.invokeMethod(
|
|
||||||
"on_state_changed",
|
|
||||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
|
||||||
)
|
|
||||||
flutterMethodChannel.invokeMethod(
|
|
||||||
"on_state_changed",
|
|
||||||
mapOf("name" to "media", "value" to MainService.isReady.toString())
|
|
||||||
)
|
|
||||||
result.success(true)
|
|
||||||
}
|
|
||||||
"init_input" -> {
|
|
||||||
initInput()
|
|
||||||
result.success(true)
|
|
||||||
}
|
|
||||||
"stop_input" -> {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
InputService.ctx?.disableSelf()
|
|
||||||
}
|
|
||||||
InputService.ctx = null
|
|
||||||
flutterMethodChannel.invokeMethod(
|
|
||||||
"on_state_changed",
|
|
||||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
|
||||||
)
|
|
||||||
result.success(true)
|
|
||||||
}
|
|
||||||
"cancel_notification" -> {
|
|
||||||
try {
|
|
||||||
val id = call.arguments as Int
|
|
||||||
mainService?.cancelNotification(id)
|
|
||||||
} finally {
|
|
||||||
result.success(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"enable_soft_keyboard" -> {
|
|
||||||
// https://blog.csdn.net/hanye2020/article/details/105553780
|
|
||||||
try {
|
|
||||||
if (call.arguments as Boolean) {
|
|
||||||
window.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
|
||||||
} else {
|
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
result.success(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
result.error("-1", "No such method", null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
val inputPer = InputService.isOpen
|
val inputPer = InputService.isOpen
|
||||||
activity.runOnUiThread {
|
activity.runOnUiThread {
|
||||||
flutterMethodChannel.invokeMethod(
|
flutterMethodChannel?.invokeMethod(
|
||||||
"on_state_changed",
|
"on_state_changed",
|
||||||
mapOf("name" to "input", "value" to inputPer.toString())
|
mapOf("name" to "input", "value" to inputPer.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,4 +89,138 @@ class MainActivity : FlutterActivity() {
|
|||||||
mainService = null
|
mainService = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initFlutterChannel(flutterMethodChannel: MethodChannel) {
|
||||||
|
flutterMethodChannel.setMethodCallHandler { call, result ->
|
||||||
|
// make sure result will be invoked, otherwise flutter will await forever
|
||||||
|
when (call.method) {
|
||||||
|
"init_service" -> {
|
||||||
|
Intent(activity, MainService::class.java).also {
|
||||||
|
bindService(it, serviceConnection, Context.BIND_AUTO_CREATE)
|
||||||
|
}
|
||||||
|
if (MainService.isReady) {
|
||||||
|
result.success(false)
|
||||||
|
return@setMethodCallHandler
|
||||||
|
}
|
||||||
|
requestMediaProjection()
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
"start_capture" -> {
|
||||||
|
mainService?.let {
|
||||||
|
result.success(it.startCapture())
|
||||||
|
} ?: let {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"stop_service" -> {
|
||||||
|
Log.d(logTag, "Stop service")
|
||||||
|
mainService?.let {
|
||||||
|
it.destroy()
|
||||||
|
result.success(true)
|
||||||
|
} ?: let {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"check_permission" -> {
|
||||||
|
if (call.arguments is String) {
|
||||||
|
result.success(XXPermissions.isGranted(context, call.arguments as String))
|
||||||
|
} else {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"request_permission" -> {
|
||||||
|
if (call.arguments is String) {
|
||||||
|
requestPermission(context, call.arguments as String)
|
||||||
|
result.success(true)
|
||||||
|
} else {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
START_ACTION -> {
|
||||||
|
if (call.arguments is String) {
|
||||||
|
startAction(context, call.arguments as String)
|
||||||
|
result.success(true)
|
||||||
|
} else {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"check_video_permission" -> {
|
||||||
|
mainService?.let {
|
||||||
|
result.success(it.checkMediaPermission())
|
||||||
|
} ?: let {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"check_service" -> {
|
||||||
|
Companion.flutterMethodChannel?.invokeMethod(
|
||||||
|
"on_state_changed",
|
||||||
|
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||||
|
)
|
||||||
|
Companion.flutterMethodChannel?.invokeMethod(
|
||||||
|
"on_state_changed",
|
||||||
|
mapOf("name" to "media", "value" to MainService.isReady.toString())
|
||||||
|
)
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
"stop_input" -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
InputService.ctx?.disableSelf()
|
||||||
|
}
|
||||||
|
InputService.ctx = null
|
||||||
|
Companion.flutterMethodChannel?.invokeMethod(
|
||||||
|
"on_state_changed",
|
||||||
|
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||||
|
)
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
"cancel_notification" -> {
|
||||||
|
if (call.arguments is Int) {
|
||||||
|
val id = call.arguments as Int
|
||||||
|
mainService?.cancelNotification(id)
|
||||||
|
} else {
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"enable_soft_keyboard" -> {
|
||||||
|
// https://blog.csdn.net/hanye2020/article/details/105553780
|
||||||
|
if (call.arguments as Boolean) {
|
||||||
|
window.clearFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||||
|
} else {
|
||||||
|
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM)
|
||||||
|
}
|
||||||
|
result.success(true)
|
||||||
|
|
||||||
|
}
|
||||||
|
GET_START_ON_BOOT_OPT -> {
|
||||||
|
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||||
|
result.success(prefs.getBoolean(KEY_START_ON_BOOT_OPT, false))
|
||||||
|
}
|
||||||
|
SET_START_ON_BOOT_OPT -> {
|
||||||
|
if (call.arguments is Boolean) {
|
||||||
|
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||||
|
val edit = prefs.edit()
|
||||||
|
edit.putBoolean(KEY_START_ON_BOOT_OPT, call.arguments as Boolean)
|
||||||
|
edit.apply()
|
||||||
|
result.success(true)
|
||||||
|
} else {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SYNC_APP_DIR_CONFIG_PATH -> {
|
||||||
|
if (call.arguments is String) {
|
||||||
|
val prefs = getSharedPreferences(KEY_SHARED_PREFERENCES, MODE_PRIVATE)
|
||||||
|
val edit = prefs.edit()
|
||||||
|
edit.putString(KEY_APP_DIR_CONFIG_PATH, call.arguments as String)
|
||||||
|
edit.apply()
|
||||||
|
result.success(true)
|
||||||
|
} else {
|
||||||
|
result.success(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
result.error("-1", "No such method", null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import androidx.annotation.RequiresApi
|
|||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
import org.json.JSONException
|
import org.json.JSONException
|
||||||
@ -43,10 +44,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"
|
||||||
@ -147,7 +144,11 @@ class MainService : Service() {
|
|||||||
|
|
||||||
// jvm call rust
|
// jvm call rust
|
||||||
private external fun init(ctx: Context)
|
private external fun init(ctx: Context)
|
||||||
private external fun startServer()
|
|
||||||
|
/// When app start on boot, app_dir will not be passed from flutter
|
||||||
|
/// so pass a app_dir here to rust server
|
||||||
|
private external fun startServer(app_dir: String)
|
||||||
|
private external fun startService()
|
||||||
private external fun onVideoFrameUpdate(buf: ByteBuffer)
|
private external fun onVideoFrameUpdate(buf: ByteBuffer)
|
||||||
private external fun onAudioFrameUpdate(buf: ByteBuffer)
|
private external fun onAudioFrameUpdate(buf: ByteBuffer)
|
||||||
private external fun translateLocale(localeName: String, input: String): String
|
private external fun translateLocale(localeName: String, input: String): String
|
||||||
@ -195,6 +196,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
|
||||||
@ -202,7 +204,13 @@ class MainService : Service() {
|
|||||||
}
|
}
|
||||||
updateScreenInfo(resources.configuration.orientation)
|
updateScreenInfo(resources.configuration.orientation)
|
||||||
initNotification()
|
initNotification()
|
||||||
startServer()
|
|
||||||
|
// keep the config dir same with flutter
|
||||||
|
val prefs = applicationContext.getSharedPreferences(KEY_SHARED_PREFERENCES, FlutterActivity.MODE_PRIVATE)
|
||||||
|
val configPath = prefs.getString(KEY_APP_DIR_CONFIG_PATH, "") ?: ""
|
||||||
|
startServer(configPath)
|
||||||
|
|
||||||
|
createForegroundNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
@ -277,22 +285,30 @@ 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()}")
|
|
||||||
createForegroundNotification()
|
createForegroundNotification()
|
||||||
val mMediaProjectionManager =
|
|
||||||
|
if (intent.getBooleanExtra(EXT_INIT_FROM_BOOT, false)) {
|
||||||
|
startService()
|
||||||
|
}
|
||||||
|
Log.d(logTag, "service starting: ${startId}:${Thread.currentThread()}")
|
||||||
|
val mediaProjectionManager =
|
||||||
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 +316,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) {
|
||||||
@ -400,13 +424,13 @@ class MainService : Service() {
|
|||||||
|
|
||||||
fun checkMediaPermission(): Boolean {
|
fun checkMediaPermission(): Boolean {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
MainActivity.flutterMethodChannel.invokeMethod(
|
MainActivity.flutterMethodChannel?.invokeMethod(
|
||||||
"on_state_changed",
|
"on_state_changed",
|
||||||
mapOf("name" to "media", "value" to isReady.toString())
|
mapOf("name" to "media", "value" to isReady.toString())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
MainActivity.flutterMethodChannel.invokeMethod(
|
MainActivity.flutterMethodChannel?.invokeMethod(
|
||||||
"on_state_changed",
|
"on_state_changed",
|
||||||
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
mapOf("name" to "input", "value" to InputService.isOpen.toString())
|
||||||
)
|
)
|
||||||
@ -653,8 +677,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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.carriez.flutter_hbb
|
package com.carriez.flutter_hbb
|
||||||
|
|
||||||
|
import android.Manifest.permission.*
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@ -12,8 +13,8 @@ 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 android.provider.Settings.*
|
||||||
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 +23,31 @@ 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_INIT_FROM_BOOT = "EXT_INIT_FROM_BOOT"
|
||||||
|
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
|
||||||
|
|
||||||
|
// Flutter channel
|
||||||
|
const val START_ACTION = "start_action"
|
||||||
|
const val GET_START_ON_BOOT_OPT = "get_start_on_boot_opt"
|
||||||
|
const val SET_START_ON_BOOT_OPT = "set_start_on_boot_opt"
|
||||||
|
const val SYNC_APP_DIR_CONFIG_PATH = "sync_app_dir"
|
||||||
|
|
||||||
|
const val KEY_SHARED_PREFERENCES = "KEY_SHARED_PREFERENCES"
|
||||||
|
const val KEY_START_ON_BOOT_OPT = "KEY_START_ON_BOOT_OPT"
|
||||||
|
const val KEY_APP_DIR_CONFIG_PATH = "KEY_APP_DIR_CONFIG_PATH"
|
||||||
|
|
||||||
@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)
|
||||||
@ -30,61 +56,13 @@ data class Info(
|
|||||||
var width: Int, var height: Int, var scale: Int, var dpi: Int
|
var width: Int, var height: Int, var scale: Int, var dpi: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
|
|
||||||
fun testVP9Support(): Boolean {
|
|
||||||
return true
|
|
||||||
val res = MediaCodecList(MediaCodecList.ALL_CODECS)
|
|
||||||
.findEncoderForFormat(
|
|
||||||
MediaFormat.createVideoFormat(
|
|
||||||
MediaFormat.MIMETYPE_VIDEO_VP9,
|
|
||||||
SCREEN_INFO.width,
|
|
||||||
SCREEN_INFO.width
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return res != null
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.M)
|
|
||||||
fun requestPermission(context: Context, type: String) {
|
fun requestPermission(context: Context, type: String) {
|
||||||
val permission = when (type) {
|
|
||||||
"ignore_battery_optimizations" -> {
|
|
||||||
try {
|
|
||||||
context.startActivity(Intent(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
|
|
||||||
data = Uri.parse("package:" + context.packageName)
|
|
||||||
})
|
|
||||||
} catch (e:Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
"application_details_settings" -> {
|
|
||||||
try {
|
|
||||||
context.startActivity(Intent().apply {
|
|
||||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
action = "android.settings.APPLICATION_DETAILS_SETTINGS"
|
|
||||||
data = Uri.parse("package:" + context.packageName)
|
|
||||||
})
|
|
||||||
} catch (e:Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
"audio" -> {
|
|
||||||
Permission.RECORD_AUDIO
|
|
||||||
}
|
|
||||||
"file" -> {
|
|
||||||
Permission.MANAGE_EXTERNAL_STORAGE
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XXPermissions.with(context)
|
XXPermissions.with(context)
|
||||||
.permission(permission)
|
.permission(type)
|
||||||
.request { _, all ->
|
.request { _, all ->
|
||||||
if (all) {
|
if (all) {
|
||||||
Handler(Looper.getMainLooper()).post {
|
Handler(Looper.getMainLooper()).post {
|
||||||
MainActivity.flutterMethodChannel.invokeMethod(
|
MainActivity.flutterMethodChannel?.invokeMethod(
|
||||||
"on_android_permission_result",
|
"on_android_permission_result",
|
||||||
mapOf("type" to type, "result" to all)
|
mapOf("type" to type, "result" to all)
|
||||||
)
|
)
|
||||||
@ -93,24 +71,18 @@ fun requestPermission(context: Context, type: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.M)
|
fun startAction(context: Context, action: String) {
|
||||||
fun checkPermission(context: Context, type: String): Boolean {
|
try {
|
||||||
val permission = when (type) {
|
context.startActivity(Intent(action).apply {
|
||||||
"ignore_battery_optimizations" -> {
|
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
val pw = context.getSystemService(Context.POWER_SERVICE) as PowerManager
|
// don't pass package name when launch ACTION_ACCESSIBILITY_SETTINGS
|
||||||
return pw.isIgnoringBatteryOptimizations(context.packageName)
|
if (ACTION_ACCESSIBILITY_SETTINGS != action) {
|
||||||
}
|
data = Uri.parse("package:" + context.packageName)
|
||||||
"audio" -> {
|
}
|
||||||
Permission.RECORD_AUDIO
|
})
|
||||||
}
|
} catch (e: Exception) {
|
||||||
"file" -> {
|
e.printStackTrace()
|
||||||
Permission.MANAGE_EXTERNAL_STORAGE
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return XXPermissions.isGranted(context, permission)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AudioReader(val bufSize: Int, private val maxFrames: Int) {
|
class AudioReader(val bufSize: Int, private val maxFrames: Int) {
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -910,21 +910,14 @@ class AccessibilityListener extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionManager {
|
class AndroidPermissionManager {
|
||||||
static Completer<bool>? _completer;
|
static Completer<bool>? _completer;
|
||||||
static Timer? _timer;
|
static Timer? _timer;
|
||||||
static var _current = "";
|
static var _current = "";
|
||||||
|
|
||||||
static final permissions = [
|
|
||||||
"audio",
|
|
||||||
"file",
|
|
||||||
"ignore_battery_optimizations",
|
|
||||||
"application_details_settings"
|
|
||||||
];
|
|
||||||
|
|
||||||
static bool isWaitingFile() {
|
static bool isWaitingFile() {
|
||||||
if (_completer != null) {
|
if (_completer != null) {
|
||||||
return !_completer!.isCompleted && _current == "file";
|
return !_completer!.isCompleted && _current == kManageExternalStorage;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -933,31 +926,33 @@ class PermissionManager {
|
|||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
}
|
}
|
||||||
if (!permissions.contains(type)) {
|
|
||||||
return Future.error("Wrong permission!$type");
|
|
||||||
}
|
|
||||||
return gFFI.invokeMethod("check_permission", type);
|
return gFFI.invokeMethod("check_permission", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// startActivity goto Android Setting's page to request permission manually by user
|
||||||
|
static void startAction(String action) {
|
||||||
|
gFFI.invokeMethod(AndroidChannel.kStartAction, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// We use XXPermissions to request permissions,
|
||||||
|
/// for supported types, see https://github.com/getActivity/XXPermissions/blob/e46caea32a64ad7819df62d448fb1c825481cd28/library/src/main/java/com/hjq/permissions/Permission.java
|
||||||
static Future<bool> request(String type) {
|
static Future<bool> request(String type) {
|
||||||
if (isDesktop) {
|
if (isDesktop) {
|
||||||
return Future.value(true);
|
return Future.value(true);
|
||||||
}
|
}
|
||||||
if (!permissions.contains(type)) {
|
|
||||||
return Future.error("Wrong permission!$type");
|
|
||||||
}
|
|
||||||
|
|
||||||
gFFI.invokeMethod("request_permission", type);
|
gFFI.invokeMethod("request_permission", type);
|
||||||
if (type == "ignore_battery_optimizations") {
|
|
||||||
return Future.value(false);
|
// clear last task
|
||||||
|
if (_completer?.isCompleted == false) {
|
||||||
|
_completer?.complete(false);
|
||||||
}
|
}
|
||||||
|
_timer?.cancel();
|
||||||
|
|
||||||
_current = type;
|
_current = type;
|
||||||
_completer = Completer<bool>();
|
_completer = Completer<bool>();
|
||||||
gFFI.invokeMethod("request_permission", type);
|
|
||||||
|
|
||||||
// timeout
|
_timer = Timer(Duration(seconds: 120), () {
|
||||||
_timer?.cancel();
|
|
||||||
_timer = Timer(Duration(seconds: 60), () {
|
|
||||||
if (_completer == null) return;
|
if (_completer == null) return;
|
||||||
if (!_completer!.isCompleted) {
|
if (!_completer!.isCompleted) {
|
||||||
_completer!.complete(false);
|
_completer!.complete(false);
|
||||||
@ -1487,8 +1482,8 @@ connect(BuildContext context, String id,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isFileTransfer) {
|
if (isFileTransfer) {
|
||||||
if (!await PermissionManager.check("file")) {
|
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
|
||||||
if (!await PermissionManager.request("file")) {
|
if (!await AndroidPermissionManager.request(kManageExternalStorage)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,11 +59,12 @@ const double kDesktopFileTransferMaximumWidth = 300;
|
|||||||
const double kDesktopFileTransferRowHeight = 30.0;
|
const double kDesktopFileTransferRowHeight = 30.0;
|
||||||
const double kDesktopFileTransferHeaderHeight = 25.0;
|
const double kDesktopFileTransferHeaderHeight = 25.0;
|
||||||
|
|
||||||
EdgeInsets get kDragToResizeAreaPadding => !kUseCompatibleUiMode && Platform.isLinux
|
EdgeInsets get kDragToResizeAreaPadding =>
|
||||||
? stateGlobal.fullscreen || stateGlobal.maximize
|
!kUseCompatibleUiMode && Platform.isLinux
|
||||||
? EdgeInsets.zero
|
? stateGlobal.fullscreen || stateGlobal.maximize
|
||||||
: EdgeInsets.all(5.0)
|
? EdgeInsets.zero
|
||||||
: EdgeInsets.zero;
|
: EdgeInsets.all(5.0)
|
||||||
|
: EdgeInsets.zero;
|
||||||
// https://en.wikipedia.org/wiki/Non-breaking_space
|
// https://en.wikipedia.org/wiki/Non-breaking_space
|
||||||
const int $nbsp = 0x00A0;
|
const int $nbsp = 0x00A0;
|
||||||
|
|
||||||
@ -136,6 +137,25 @@ const kRemoteAudioDualWay = 'dual-way';
|
|||||||
|
|
||||||
const kIgnoreDpi = true;
|
const kIgnoreDpi = true;
|
||||||
|
|
||||||
|
/// Android constants
|
||||||
|
const kActionApplicationDetailsSettings =
|
||||||
|
"android.settings.APPLICATION_DETAILS_SETTINGS";
|
||||||
|
const kActionAccessibilitySettings = "android.settings.ACCESSIBILITY_SETTINGS";
|
||||||
|
|
||||||
|
const kRecordAudio = "android.permission.RECORD_AUDIO";
|
||||||
|
const kManageExternalStorage = "android.permission.MANAGE_EXTERNAL_STORAGE";
|
||||||
|
const kRequestIgnoreBatteryOptimizations =
|
||||||
|
"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS";
|
||||||
|
const kSystemAlertWindow = "android.permission.SYSTEM_ALERT_WINDOW";
|
||||||
|
|
||||||
|
/// Android channel invoke type key
|
||||||
|
class AndroidChannel {
|
||||||
|
static final kStartAction = "start_action";
|
||||||
|
static final kGetStartOnBootOpt = "get_start_on_boot_opt";
|
||||||
|
static final kSetStartOnBootOpt = "set_start_on_boot_opt";
|
||||||
|
static final kSyncAppDirConfigPath = "sync_app_dir";
|
||||||
|
}
|
||||||
|
|
||||||
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
|
/// flutter/packages/flutter/lib/src/services/keyboard_key.dart -> _keyLabels
|
||||||
/// see [LogicalKeyboardKey.keyLabel]
|
/// see [LogicalKeyboardKey.keyLabel]
|
||||||
const Map<int, String> logicalKeyMap = <int, String>{
|
const Map<int, String> logicalKeyMap = <int, String>{
|
||||||
|
|||||||
@ -153,6 +153,7 @@ void runMainApp(bool startService) async {
|
|||||||
void runMobileApp() async {
|
void runMobileApp() async {
|
||||||
await initEnv(kAppTypeMain);
|
await initEnv(kAppTypeMain);
|
||||||
if (isAndroid) androidChannelInit();
|
if (isAndroid) androidChannelInit();
|
||||||
|
platformFFI.syncAndroidServiceAppDirConfigPath();
|
||||||
runApp(App());
|
runApp(App());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/widgets/dialog.dart';
|
import '../../common/widgets/dialog.dart';
|
||||||
|
import '../../consts.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../../models/server_model.dart';
|
import '../../models/server_model.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
@ -40,14 +41,14 @@ class ServerPage extends StatefulWidget implements PageShape {
|
|||||||
value: "setTemporaryPasswordLength",
|
value: "setTemporaryPasswordLength",
|
||||||
enabled:
|
enabled:
|
||||||
gFFI.serverModel.verificationMethod != kUsePermanentPassword,
|
gFFI.serverModel.verificationMethod != kUsePermanentPassword,
|
||||||
child: Text(translate("Set temporary password length")),
|
child: Text(translate("One-time password length")),
|
||||||
),
|
),
|
||||||
const PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 0.0),
|
padding: const EdgeInsets.symmetric(horizontal: 0.0),
|
||||||
value: kUseTemporaryPassword,
|
value: kUseTemporaryPassword,
|
||||||
child: ListTile(
|
child: ListTile(
|
||||||
title: Text(translate("Use temporary password")),
|
title: Text(translate("Use one-time password")),
|
||||||
trailing: Icon(
|
trailing: Icon(
|
||||||
Icons.check,
|
Icons.check,
|
||||||
color: gFFI.serverModel.verificationMethod ==
|
color: gFFI.serverModel.verificationMethod ==
|
||||||
@ -150,10 +151,11 @@ class _ServerPageState extends State<ServerPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void checkService() async {
|
void checkService() async {
|
||||||
gFFI.invokeMethod("check_service"); // jvm
|
gFFI.invokeMethod("check_service");
|
||||||
// for Android 10/11,MANAGE_EXTERNAL_STORAGE permission from a system setting page
|
// for Android 10/11, request MANAGE_EXTERNAL_STORAGE permission from system setting page
|
||||||
if (PermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
|
if (AndroidPermissionManager.isWaitingFile() && !gFFI.serverModel.fileOk) {
|
||||||
PermissionManager.complete("file", await PermissionManager.check("file"));
|
AndroidPermissionManager.complete(kManageExternalStorage,
|
||||||
|
await AndroidPermissionManager.check(kManageExternalStorage));
|
||||||
debugPrint("file permission finished");
|
debugPrint("file permission finished");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -567,7 +569,7 @@ void androidChannelInit() {
|
|||||||
{
|
{
|
||||||
var type = arguments["type"] as String;
|
var type = arguments["type"] as String;
|
||||||
var result = arguments["result"] as bool;
|
var result = arguments["result"] as bool;
|
||||||
PermissionManager.complete(type, result);
|
AndroidPermissionManager.complete(type, result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "on_media_projection_canceled":
|
case "on_media_projection_canceled":
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import 'package:url_launcher/url_launcher.dart';
|
|||||||
import '../../common.dart';
|
import '../../common.dart';
|
||||||
import '../../common/widgets/dialog.dart';
|
import '../../common/widgets/dialog.dart';
|
||||||
import '../../common/widgets/login.dart';
|
import '../../common/widgets/login.dart';
|
||||||
|
import '../../consts.dart';
|
||||||
import '../../models/model.dart';
|
import '../../models/model.dart';
|
||||||
import '../../models/platform_model.dart';
|
import '../../models/platform_model.dart';
|
||||||
import '../widgets/dialog.dart';
|
import '../widgets/dialog.dart';
|
||||||
@ -31,18 +32,20 @@ class SettingsPage extends StatefulWidget implements PageShape {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const url = 'https://rustdesk.com/';
|
const url = 'https://rustdesk.com/';
|
||||||
final _hasIgnoreBattery = androidVersion >= 26;
|
|
||||||
var _ignoreBatteryOpt = false;
|
|
||||||
var _enableAbr = false;
|
|
||||||
var _denyLANDiscovery = false;
|
|
||||||
var _onlyWhiteList = false;
|
|
||||||
var _enableDirectIPAccess = false;
|
|
||||||
var _enableRecordSession = false;
|
|
||||||
var _autoRecordIncomingSession = false;
|
|
||||||
var _localIP = "";
|
|
||||||
var _directAccessPort = "";
|
|
||||||
|
|
||||||
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
||||||
|
final _hasIgnoreBattery = androidVersion >= 26;
|
||||||
|
var _ignoreBatteryOpt = false;
|
||||||
|
var _enableStartOnBoot = false;
|
||||||
|
var _enableAbr = false;
|
||||||
|
var _denyLANDiscovery = false;
|
||||||
|
var _onlyWhiteList = false;
|
||||||
|
var _enableDirectIPAccess = false;
|
||||||
|
var _enableRecordSession = false;
|
||||||
|
var _autoRecordIncomingSession = false;
|
||||||
|
var _localIP = "";
|
||||||
|
var _directAccessPort = "";
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -50,11 +53,34 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
|
|
||||||
() async {
|
() async {
|
||||||
var update = false;
|
var update = false;
|
||||||
|
|
||||||
if (_hasIgnoreBattery) {
|
if (_hasIgnoreBattery) {
|
||||||
update = await updateIgnoreBatteryStatus();
|
if (await checkAndUpdateIgnoreBatteryStatus()) {
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final enableAbrRes = await bind.mainGetOption(key: "enable-abr") != "N";
|
if (await checkAndUpdateStartOnBoot()) {
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW
|
||||||
|
var enableStartOnBoot =
|
||||||
|
await gFFI.invokeMethod(AndroidChannel.kGetStartOnBootOpt);
|
||||||
|
if (enableStartOnBoot) {
|
||||||
|
if (!await canStartOnBoot()) {
|
||||||
|
enableStartOnBoot = false;
|
||||||
|
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enableStartOnBoot != _enableStartOnBoot) {
|
||||||
|
update = true;
|
||||||
|
_enableStartOnBoot = enableStartOnBoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
final enableAbrRes = option2bool(
|
||||||
|
"enable-abr", await bind.mainGetOption(key: "enable-abr"));
|
||||||
if (enableAbrRes != _enableAbr) {
|
if (enableAbrRes != _enableAbr) {
|
||||||
update = true;
|
update = true;
|
||||||
_enableAbr = enableAbrRes;
|
_enableAbr = enableAbrRes;
|
||||||
@ -125,15 +151,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
if (state == AppLifecycleState.resumed) {
|
if (state == AppLifecycleState.resumed) {
|
||||||
() async {
|
() async {
|
||||||
if (await updateIgnoreBatteryStatus()) {
|
final ibs = await checkAndUpdateIgnoreBatteryStatus();
|
||||||
|
final sob = await checkAndUpdateStartOnBoot();
|
||||||
|
if (ibs || sob) {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateIgnoreBatteryStatus() async {
|
Future<bool> checkAndUpdateIgnoreBatteryStatus() async {
|
||||||
final res = await PermissionManager.check("ignore_battery_optimizations");
|
final res = await AndroidPermissionManager.check(
|
||||||
|
kRequestIgnoreBatteryOptimizations);
|
||||||
if (_ignoreBatteryOpt != res) {
|
if (_ignoreBatteryOpt != res) {
|
||||||
_ignoreBatteryOpt = res;
|
_ignoreBatteryOpt = res;
|
||||||
return true;
|
return true;
|
||||||
@ -142,6 +171,18 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> checkAndUpdateStartOnBoot() async {
|
||||||
|
if (!await canStartOnBoot() && _enableStartOnBoot) {
|
||||||
|
_enableStartOnBoot = false;
|
||||||
|
debugPrint(
|
||||||
|
"checkAndUpdateStartOnBoot and set _enableStartOnBoot -> false");
|
||||||
|
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, false);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Provider.of<FfiModel>(context);
|
Provider.of<FfiModel>(context);
|
||||||
@ -265,7 +306,8 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
]),
|
]),
|
||||||
onToggle: (v) async {
|
onToggle: (v) async {
|
||||||
if (v) {
|
if (v) {
|
||||||
PermissionManager.request("ignore_battery_optimizations");
|
await AndroidPermissionManager.request(
|
||||||
|
kRequestIgnoreBatteryOptimizations);
|
||||||
} else {
|
} else {
|
||||||
final res = await gFFI.dialogManager
|
final res = await gFFI.dialogManager
|
||||||
.show<bool>((setState, close) => CustomAlertDialog(
|
.show<bool>((setState, close) => CustomAlertDialog(
|
||||||
@ -282,11 +324,44 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
],
|
],
|
||||||
));
|
));
|
||||||
if (res == true) {
|
if (res == true) {
|
||||||
PermissionManager.request("application_details_settings");
|
AndroidPermissionManager.startAction(
|
||||||
|
kActionApplicationDetailsSettings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
enhancementsTiles.add(SettingsTile.switchTile(
|
||||||
|
initialValue: _enableStartOnBoot,
|
||||||
|
title: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||||
|
Text("${translate('Start on Boot')} (beta)"),
|
||||||
|
Text(
|
||||||
|
'* ${translate('Start the screen sharing service on boot, requires special permissions')}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall),
|
||||||
|
]),
|
||||||
|
onToggle: (toValue) async {
|
||||||
|
if (toValue) {
|
||||||
|
// 1. request kIgnoreBatteryOptimizations
|
||||||
|
if (!await AndroidPermissionManager.check(
|
||||||
|
kRequestIgnoreBatteryOptimizations)) {
|
||||||
|
if (!await AndroidPermissionManager.request(
|
||||||
|
kRequestIgnoreBatteryOptimizations)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. request kSystemAlertWindow
|
||||||
|
if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
|
||||||
|
if (!await AndroidPermissionManager.request(kSystemAlertWindow)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Optional) 3. request input permission
|
||||||
|
}
|
||||||
|
setState(() => _enableStartOnBoot = toValue);
|
||||||
|
|
||||||
|
gFFI.invokeMethod(AndroidChannel.kSetStartOnBootOpt, toValue);
|
||||||
|
}));
|
||||||
|
|
||||||
return SettingsList(
|
return SettingsList(
|
||||||
sections: [
|
sections: [
|
||||||
@ -387,6 +462,17 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> canStartOnBoot() async {
|
||||||
|
// start on boot depends on ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS and SYSTEM_ALERT_WINDOW
|
||||||
|
if (_hasIgnoreBattery && !_ignoreBatteryOpt) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!await AndroidPermissionManager.check(kSystemAlertWindow)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showServerSettings(OverlayDialogManager dialogManager) async {
|
void showServerSettings(OverlayDialogManager dialogManager) async {
|
||||||
|
|||||||
@ -30,7 +30,7 @@ typedef F4Dart = int Function(Pointer<Utf8>);
|
|||||||
typedef F5 = Void Function(Pointer<Utf8>);
|
typedef F5 = Void Function(Pointer<Utf8>);
|
||||||
typedef F5Dart = void Function(Pointer<Utf8>);
|
typedef F5Dart = void Function(Pointer<Utf8>);
|
||||||
typedef HandleEvent = Future<void> Function(Map<String, dynamic> evt);
|
typedef HandleEvent = Future<void> Function(Map<String, dynamic> evt);
|
||||||
// pub fn session_register_texture(id: *const char, ptr: usize)
|
// pub fn session_register_texture(id: *const char, ptr: usize)
|
||||||
typedef F6 = Void Function(Pointer<Utf8>, Uint64);
|
typedef F6 = Void Function(Pointer<Utf8>, Uint64);
|
||||||
typedef F6Dart = void Function(Pointer<Utf8>, int);
|
typedef F6Dart = void Function(Pointer<Utf8>, int);
|
||||||
|
|
||||||
@ -56,7 +56,6 @@ class PlatformFFI {
|
|||||||
F4Dart? _session_get_rgba_size;
|
F4Dart? _session_get_rgba_size;
|
||||||
F5Dart? _session_next_rgba;
|
F5Dart? _session_next_rgba;
|
||||||
F6Dart? _session_register_texture;
|
F6Dart? _session_register_texture;
|
||||||
|
|
||||||
|
|
||||||
static get localeName => Platform.localeName;
|
static get localeName => Platform.localeName;
|
||||||
|
|
||||||
@ -162,7 +161,8 @@ class PlatformFFI {
|
|||||||
dylib.lookupFunction<F4, F4Dart>("session_get_rgba_size");
|
dylib.lookupFunction<F4, F4Dart>("session_get_rgba_size");
|
||||||
_session_next_rgba =
|
_session_next_rgba =
|
||||||
dylib.lookupFunction<F5, F5Dart>("session_next_rgba");
|
dylib.lookupFunction<F5, F5Dart>("session_next_rgba");
|
||||||
_session_register_texture = dylib.lookupFunction<F6, F6Dart>("session_register_texture");
|
_session_register_texture =
|
||||||
|
dylib.lookupFunction<F6, F6Dart>("session_register_texture");
|
||||||
try {
|
try {
|
||||||
// SYSTEM user failed
|
// SYSTEM user failed
|
||||||
_dir = (await getApplicationDocumentsDirectory()).path;
|
_dir = (await getApplicationDocumentsDirectory()).path;
|
||||||
@ -301,4 +301,8 @@ class PlatformFFI {
|
|||||||
if (!isAndroid) return Future<bool>(() => false);
|
if (!isAndroid) return Future<bool>(() => false);
|
||||||
return await _toAndroidChannel.invokeMethod(method, arguments);
|
return await _toAndroidChannel.invokeMethod(method, arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void syncAndroidServiceAppDirConfigPath() {
|
||||||
|
invokeMethod(AndroidChannel.kSyncAppDirConfigPath, _dir);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import 'dart:convert';
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hbb/consts.dart';
|
||||||
import 'package:flutter_hbb/main.dart';
|
import 'package:flutter_hbb/main.dart';
|
||||||
import 'package:flutter_hbb/models/platform_model.dart';
|
import 'package:flutter_hbb/models/platform_model.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@ -154,7 +155,8 @@ class ServerModel with ChangeNotifier {
|
|||||||
/// file true by default (if permission on)
|
/// file true by default (if permission on)
|
||||||
checkAndroidPermission() async {
|
checkAndroidPermission() async {
|
||||||
// audio
|
// audio
|
||||||
if (androidVersion < 30 || !await PermissionManager.check("audio")) {
|
if (androidVersion < 30 ||
|
||||||
|
!await AndroidPermissionManager.check(kRecordAudio)) {
|
||||||
_audioOk = false;
|
_audioOk = false;
|
||||||
bind.mainSetOption(key: "enable-audio", value: "N");
|
bind.mainSetOption(key: "enable-audio", value: "N");
|
||||||
} else {
|
} else {
|
||||||
@ -163,7 +165,7 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// file
|
// file
|
||||||
if (!await PermissionManager.check("file")) {
|
if (!await AndroidPermissionManager.check(kManageExternalStorage)) {
|
||||||
_fileOk = false;
|
_fileOk = false;
|
||||||
bind.mainSetOption(key: "enable-file-transfer", value: "N");
|
bind.mainSetOption(key: "enable-file-transfer", value: "N");
|
||||||
} else {
|
} else {
|
||||||
@ -229,10 +231,10 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleAudio() async {
|
toggleAudio() async {
|
||||||
if (!_audioOk && !await PermissionManager.check("audio")) {
|
if (!_audioOk && !await AndroidPermissionManager.check(kRecordAudio)) {
|
||||||
final res = await PermissionManager.request("audio");
|
final res = await AndroidPermissionManager.request(kRecordAudio);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
// TODO handle fail
|
showToast(translate('Failed'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -243,10 +245,12 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toggleFile() async {
|
toggleFile() async {
|
||||||
if (!_fileOk && !await PermissionManager.check("file")) {
|
if (!_fileOk &&
|
||||||
final res = await PermissionManager.request("file");
|
!await AndroidPermissionManager.check(kManageExternalStorage)) {
|
||||||
|
final res =
|
||||||
|
await AndroidPermissionManager.request(kManageExternalStorage);
|
||||||
if (!res) {
|
if (!res) {
|
||||||
// TODO handle fail
|
showToast(translate('Failed'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -344,10 +348,6 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initInput() async {
|
|
||||||
await parent.target?.invokeMethod("init_input");
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> setPermanentPassword(String newPW) async {
|
Future<bool> setPermanentPassword(String newPW) async {
|
||||||
await bind.mainSetPermanentPassword(password: newPW);
|
await bind.mainSetPermanentPassword(password: newPW);
|
||||||
await Future.delayed(Duration(milliseconds: 500));
|
await Future.delayed(Duration(milliseconds: 500));
|
||||||
@ -561,7 +561,8 @@ class ServerModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> closeAll() async {
|
Future<void> closeAll() async {
|
||||||
await Future.wait(_clients.map((client) => bind.cmCloseConnection(connId: client.id)));
|
await Future.wait(
|
||||||
|
_clients.map((client) => bind.cmCloseConnection(connId: client.id)));
|
||||||
_clients.clear();
|
_clients.clear();
|
||||||
tabController.state.value.tabs.clear();
|
tabController.state.value.tabs.clear();
|
||||||
}
|
}
|
||||||
@ -684,7 +685,7 @@ String getLoginDialogTag(int id) {
|
|||||||
showInputWarnAlert(FFI ffi) {
|
showInputWarnAlert(FFI ffi) {
|
||||||
ffi.dialogManager.show((setState, close) {
|
ffi.dialogManager.show((setState, close) {
|
||||||
submit() {
|
submit() {
|
||||||
ffi.serverModel.initInput();
|
AndroidPermissionManager.startAction(kActionAccessibilitySettings);
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,11 +36,11 @@ def main():
|
|||||||
def expand():
|
def expand():
|
||||||
for fn in glob.glob('./src/lang/*'):
|
for fn in glob.glob('./src/lang/*'):
|
||||||
lang = os.path.basename(fn)[:-3]
|
lang = os.path.basename(fn)[:-3]
|
||||||
if lang in ['en','cn']: continue
|
if lang in ['en','template']: continue
|
||||||
print(lang)
|
print(lang)
|
||||||
dict = get_lang(lang)
|
dict = get_lang(lang)
|
||||||
fw = open("./src/lang/%s.rs"%lang, "wt", encoding='utf8')
|
fw = open("./src/lang/%s.rs"%lang, "wt", encoding='utf8')
|
||||||
for line in open('./src/lang/cn.rs', encoding='utf8'):
|
for line in open('./src/lang/template.rs', encoding='utf8'):
|
||||||
line_strip = line.strip()
|
line_strip = line.strip()
|
||||||
if line_strip.startswith('("'):
|
if line_strip.startswith('("'):
|
||||||
k, v = line_split(line_strip)
|
k, v = line_split(line_strip)
|
||||||
|
|||||||
@ -1361,7 +1361,7 @@ pub fn send_url_scheme(_url: String) {
|
|||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
pub mod server_side {
|
pub mod server_side {
|
||||||
use hbb_common::log;
|
use hbb_common::{log, config};
|
||||||
use jni::{
|
use jni::{
|
||||||
objects::{JClass, JString},
|
objects::{JClass, JString},
|
||||||
sys::jstring,
|
sys::jstring,
|
||||||
@ -1374,11 +1374,25 @@ pub mod server_side {
|
|||||||
pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_startServer(
|
pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_startServer(
|
||||||
env: JNIEnv,
|
env: JNIEnv,
|
||||||
_class: JClass,
|
_class: JClass,
|
||||||
|
app_dir: JString,
|
||||||
) {
|
) {
|
||||||
log::debug!("startServer from java");
|
log::debug!("startServer from jvm");
|
||||||
|
if let Ok(app_dir) = env.get_string(app_dir) {
|
||||||
|
*config::APP_DIR.write().unwrap() = app_dir.into();
|
||||||
|
}
|
||||||
std::thread::spawn(move || start_server(true));
|
std::thread::spawn(move || start_server(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_startService(
|
||||||
|
env: JNIEnv,
|
||||||
|
_class: JClass,
|
||||||
|
) {
|
||||||
|
log::debug!("startService from jvm");
|
||||||
|
config::Config::set_option("stop-service".into(), "".into());
|
||||||
|
crate::rendezvous_mediator::RendezvousMediator::restart();
|
||||||
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_translateLocale(
|
pub unsafe extern "system" fn Java_com_carriez_flutter_1hbb_MainService_translateLocale(
|
||||||
env: JNIEnv,
|
env: JNIEnv,
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Mantenir RustDesk com a servei en segon pla"),
|
("Keep RustDesk background service", "Mantenir RustDesk com a servei en segon pla"),
|
||||||
("Ignore Battery Optimizations", "Ignorar optimizacions de la bateria"),
|
("Ignore Battery Optimizations", "Ignorar optimizacions de la bateria"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Connexió no disponible"),
|
("Connection not allowed", "Connexió no disponible"),
|
||||||
("Legacy mode", "Mode heretat"),
|
("Legacy mode", "Mode heretat"),
|
||||||
("Map mode", "Mode mapa"),
|
("Map mode", "Mode mapa"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "保持 RustDesk 后台服务"),
|
("Keep RustDesk background service", "保持 RustDesk 后台服务"),
|
||||||
("Ignore Battery Optimizations", "忽略电池优化"),
|
("Ignore Battery Optimizations", "忽略电池优化"),
|
||||||
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的 RustDesk 应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
("android_open_battery_optimizations_tip", "如需关闭此功能,请在接下来的 RustDesk 应用设置页面中,找到并进入 [电源] 页面,取消勾选 [不受限制]"),
|
||||||
|
("Start on Boot", "开机自启动"),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", "开机自动启动屏幕共享服务,此功能需要一些特殊权限。"),
|
||||||
("Connection not allowed", "对方不允许连接"),
|
("Connection not allowed", "对方不允许连接"),
|
||||||
("Legacy mode", "传统模式"),
|
("Legacy mode", "传统模式"),
|
||||||
("Map mode", "1:1 传输"),
|
("Map mode", "1:1 传输"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
@ -454,8 +456,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Stop voice call", ""),
|
("Stop voice call", ""),
|
||||||
("relay_hint_tip", ""),
|
("relay_hint_tip", ""),
|
||||||
("Reconnect", ""),
|
("Reconnect", ""),
|
||||||
("No transfers in progress", ""),
|
|
||||||
("Codec", ""),
|
("Codec", ""),
|
||||||
("Resolution", ""),
|
("Resolution", ""),
|
||||||
|
("No transfers in progress", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Behold RustDesk baggrundstjeneste"),
|
("Keep RustDesk background service", "Behold RustDesk baggrundstjeneste"),
|
||||||
("Ignore Battery Optimizations", "Ignorer betteri optimeringer"),
|
("Ignore Battery Optimizations", "Ignorer betteri optimeringer"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Forbindelse ikke tilladt"),
|
("Connection not allowed", "Forbindelse ikke tilladt"),
|
||||||
("Legacy mode", "Bagudkompatibilitetstilstand"),
|
("Legacy mode", "Bagudkompatibilitetstilstand"),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk im Hintergrund ausführen"),
|
("Keep RustDesk background service", "RustDesk im Hintergrund ausführen"),
|
||||||
("Ignore Battery Optimizations", "Akkuoptimierung ignorieren"),
|
("Ignore Battery Optimizations", "Akkuoptimierung ignorieren"),
|
||||||
("android_open_battery_optimizations_tip", "Möchten Sie die Einstellungen zur Akkuoptimierung öffnen?"),
|
("android_open_battery_optimizations_tip", "Möchten Sie die Einstellungen zur Akkuoptimierung öffnen?"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Verbindung abgelehnt"),
|
("Connection not allowed", "Verbindung abgelehnt"),
|
||||||
("Legacy mode", "Kompatibilitätsmodus"),
|
("Legacy mode", "Kompatibilitätsmodus"),
|
||||||
("Map mode", "Kartenmodus"),
|
("Map mode", "Kartenmodus"),
|
||||||
@ -457,5 +459,5 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Codec", "Codec"),
|
("Codec", "Codec"),
|
||||||
("Resolution", "Auflösung"),
|
("Resolution", "Auflösung"),
|
||||||
("No transfers in progress", "Keine Übertragungen im Gange"),
|
("No transfers in progress", "Keine Übertragungen im Gange"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
("Keep RustDesk background service", "Dejar RustDesk como Servicio en 2do plano"),
|
||||||
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
("Ignore Battery Optimizations", "Ignorar optimizacioens de bateria"),
|
||||||
("android_open_battery_optimizations_tip", "Si deseas deshabilitar esta característica, por favor, ve a la página siguiente de ajustes, busca y entra en [Batería] y desmarca [Sin restricción]"),
|
("android_open_battery_optimizations_tip", "Si deseas deshabilitar esta característica, por favor, ve a la página siguiente de ajustes, busca y entra en [Batería] y desmarca [Sin restricción]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Conexión no disponible"),
|
("Connection not allowed", "Conexión no disponible"),
|
||||||
("Legacy mode", "Modo heredado"),
|
("Legacy mode", "Modo heredado"),
|
||||||
("Map mode", "Modo mapa"),
|
("Map mode", "Modo mapa"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "را در پس زمینه نگه دارید RustDesk سرویس"),
|
("Keep RustDesk background service", "را در پس زمینه نگه دارید RustDesk سرویس"),
|
||||||
("Ignore Battery Optimizations", "بهینه سازی باتری نادیده گرفته شود"),
|
("Ignore Battery Optimizations", "بهینه سازی باتری نادیده گرفته شود"),
|
||||||
("android_open_battery_optimizations_tip", "به صفحه تنظیمات بعدی بروید"),
|
("android_open_battery_optimizations_tip", "به صفحه تنظیمات بعدی بروید"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "اتصال مجاز نیست"),
|
("Connection not allowed", "اتصال مجاز نیست"),
|
||||||
("Legacy mode", "legacy حالت"),
|
("Legacy mode", "legacy حالت"),
|
||||||
("Map mode", "map حالت"),
|
("Map mode", "map حالت"),
|
||||||
@ -454,8 +456,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Stop voice call", "توقف تماس صوتی"),
|
("Stop voice call", "توقف تماس صوتی"),
|
||||||
("relay_hint_tip", " را به شناسه اضافه کنید یا گزینه \"همیشه از طریق رله متصل شوید\" را در کارت همتا انتخاب کنید. همچنین، اگر میخواهید فوراً از سرور رله استفاده کنید، میتوانید پسوند \"/r\".\n اتصال مستقیم ممکن است امکان پذیر نباشد. در این صورت می توانید سعی کنید از طریق سرور رله متصل شوید"),
|
("relay_hint_tip", " را به شناسه اضافه کنید یا گزینه \"همیشه از طریق رله متصل شوید\" را در کارت همتا انتخاب کنید. همچنین، اگر میخواهید فوراً از سرور رله استفاده کنید، میتوانید پسوند \"/r\".\n اتصال مستقیم ممکن است امکان پذیر نباشد. در این صورت می توانید سعی کنید از طریق سرور رله متصل شوید"),
|
||||||
("Reconnect", "اتصال مجدد"),
|
("Reconnect", "اتصال مجدد"),
|
||||||
("No transfers in progress", "هیچ انتقالی در حال انجام نیست"),
|
|
||||||
("Codec", "کدک"),
|
("Codec", "کدک"),
|
||||||
("Resolution", "وضوح"),
|
("Resolution", "وضوح"),
|
||||||
|
("No transfers in progress", "هیچ انتقالی در حال انجام نیست"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Gardez le service RustDesk en arrière plan"),
|
("Keep RustDesk background service", "Gardez le service RustDesk en arrière plan"),
|
||||||
("Ignore Battery Optimizations", "Ignorer les optimisations batterie"),
|
("Ignore Battery Optimizations", "Ignorer les optimisations batterie"),
|
||||||
("android_open_battery_optimizations_tip", "Conseil android d'optimisation de batterie"),
|
("android_open_battery_optimizations_tip", "Conseil android d'optimisation de batterie"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Connexion non autorisée"),
|
("Connection not allowed", "Connexion non autorisée"),
|
||||||
("Legacy mode", "Mode hérité"),
|
("Legacy mode", "Mode hérité"),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Εκτέλεση του RustDesk στο παρασκήνιο"),
|
("Keep RustDesk background service", "Εκτέλεση του RustDesk στο παρασκήνιο"),
|
||||||
("Ignore Battery Optimizations", "Παράβλεψη βελτιστοποιήσεων μπαταρίας"),
|
("Ignore Battery Optimizations", "Παράβλεψη βελτιστοποιήσεων μπαταρίας"),
|
||||||
("android_open_battery_optimizations_tip", "Θέλετε να ανοίξετε τις ρυθμίσεις βελτιστοποίησης μπαταρίας;"),
|
("android_open_battery_optimizations_tip", "Θέλετε να ανοίξετε τις ρυθμίσεις βελτιστοποίησης μπαταρίας;"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Η σύνδεση απορρίφθηκε"),
|
("Connection not allowed", "Η σύνδεση απορρίφθηκε"),
|
||||||
("Legacy mode", "Λειτουργία συμβατότητας"),
|
("Legacy mode", "Λειτουργία συμβατότητας"),
|
||||||
("Map mode", "Map mode"),
|
("Map mode", "Map mode"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk futtatása a háttérben"),
|
("Keep RustDesk background service", "RustDesk futtatása a háttérben"),
|
||||||
("Ignore Battery Optimizations", "Akkumulátorkímélő figyelmen kívűl hagyása"),
|
("Ignore Battery Optimizations", "Akkumulátorkímélő figyelmen kívűl hagyása"),
|
||||||
("android_open_battery_optimizations_tip", "Ha le szeretné tiltani ezt a funkciót, lépjen a RustDesk alkalmazás beállítási oldalára, keresse meg az [Akkumulátorkímélő] lehetőséget és válassza a nincs korlátozás lehetőséget."),
|
("android_open_battery_optimizations_tip", "Ha le szeretné tiltani ezt a funkciót, lépjen a RustDesk alkalmazás beállítási oldalára, keresse meg az [Akkumulátorkímélő] lehetőséget és válassza a nincs korlátozás lehetőséget."),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "A csatlakozás nem engedélyezett"),
|
("Connection not allowed", "A csatlakozás nem engedélyezett"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
|
("Keep RustDesk background service", "Pertahankan RustDesk berjalan pada background service"),
|
||||||
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
|
("Ignore Battery Optimizations", "Abaikan Pengoptimalan Baterai"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Koneksi tidak dijinkan"),
|
("Connection not allowed", "Koneksi tidak dijinkan"),
|
||||||
("Legacy mode", "Mode lama"),
|
("Legacy mode", "Mode lama"),
|
||||||
("Map mode", "Mode peta"),
|
("Map mode", "Mode peta"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Mantieni il servizio di RustDesk in background"),
|
("Keep RustDesk background service", "Mantieni il servizio di RustDesk in background"),
|
||||||
("Ignore Battery Optimizations", "Ignora le ottimizzazioni della batteria"),
|
("Ignore Battery Optimizations", "Ignora le ottimizzazioni della batteria"),
|
||||||
("android_open_battery_optimizations_tip", "Se si desidera disabilitare questa funzione, andare nelle impostazioni dell'applicazione RustDesk, aprire la sezione [Batteria] e deselezionare [Senza restrizioni]."),
|
("android_open_battery_optimizations_tip", "Se si desidera disabilitare questa funzione, andare nelle impostazioni dell'applicazione RustDesk, aprire la sezione [Batteria] e deselezionare [Senza restrizioni]."),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Connessione non consentita"),
|
("Connection not allowed", "Connessione non consentita"),
|
||||||
("Legacy mode", "Modalità legacy"),
|
("Legacy mode", "Modalità legacy"),
|
||||||
("Map mode", "Modalità mappa"),
|
("Map mode", "Modalità mappa"),
|
||||||
@ -454,8 +456,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Stop voice call", "Interrompi la chiamata vocale"),
|
("Stop voice call", "Interrompi la chiamata vocale"),
|
||||||
("relay_hint_tip", "Se non è possibile connettersi direttamente, si può provare a farlo tramite relay.\nInoltre, se si desidera utilizzare il relay al primo tentativo, è possibile aggiungere il suffisso \"/r\" all'ID o selezionare l'opzione \"Collegati sempre tramite relay\" nella scheda peer."),
|
("relay_hint_tip", "Se non è possibile connettersi direttamente, si può provare a farlo tramite relay.\nInoltre, se si desidera utilizzare il relay al primo tentativo, è possibile aggiungere il suffisso \"/r\" all'ID o selezionare l'opzione \"Collegati sempre tramite relay\" nella scheda peer."),
|
||||||
("Reconnect", "Riconnetti"),
|
("Reconnect", "Riconnetti"),
|
||||||
("No transfers in progress", "Nessun trasferimento in corso"),
|
|
||||||
("Codec", "Codec"),
|
("Codec", "Codec"),
|
||||||
("Resolution", "Risoluzione"),
|
("Resolution", "Risoluzione"),
|
||||||
|
("No transfers in progress", "Nessun trasferimento in corso"),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"),
|
("Keep RustDesk background service", "RustDesk バックグラウンドサービスを維持"),
|
||||||
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
|
("Ignore Battery Optimizations", "バッテリーの最適化を無効にする"),
|
||||||
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
|
("android_open_battery_optimizations_tip", "この機能を使わない場合は、次のRestDeskアプリ設定ページから「バッテリー」に進み、「制限なし」の選択を外してください"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "接続が許可されていません"),
|
("Connection not allowed", "接続が許可されていません"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk 백그라운드 서비스로 유지하기"),
|
("Keep RustDesk background service", "RustDesk 백그라운드 서비스로 유지하기"),
|
||||||
("Ignore Battery Optimizations", "배터리 최적화 무시하기"),
|
("Ignore Battery Optimizations", "배터리 최적화 무시하기"),
|
||||||
("android_open_battery_optimizations_tip", "해당 기능을 비활성화하려면 RustDesk 응용 프로그램 설정 페이지로 이동하여 [배터리]에서 [제한 없음] 선택을 해제하십시오."),
|
("android_open_battery_optimizations_tip", "해당 기능을 비활성화하려면 RustDesk 응용 프로그램 설정 페이지로 이동하여 [배터리]에서 [제한 없음] 선택을 해제하십시오."),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "연결이 허용되지 않음"),
|
("Connection not allowed", "연결이 허용되지 않음"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Артжақтағы RustDesk сербесін сақтап тұру"),
|
("Keep RustDesk background service", "Артжақтағы RustDesk сербесін сақтап тұру"),
|
||||||
("Ignore Battery Optimizations", "Бәтері Оңтайландыруларын Елемеу"),
|
("Ignore Battery Optimizations", "Бәтері Оңтайландыруларын Елемеу"),
|
||||||
("android_open_battery_optimizations_tip", "Егер де бұл ерекшелікті өшіруді қаласаңыз, келесі RustDesk апылқат орнатпалары бетіне барып, [Бәтері]'ні тауып кіріңіз де [Шектеусіз]'ден құсбелгіні алып тастауды өтінеміз"),
|
("android_open_battery_optimizations_tip", "Егер де бұл ерекшелікті өшіруді қаласаңыз, келесі RustDesk апылқат орнатпалары бетіне барып, [Бәтері]'ні тауып кіріңіз де [Шектеусіз]'ден құсбелгіні алып тастауды өтінеміз"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Қосылу рұқсат етілмеген"),
|
("Connection not allowed", "Қосылу рұқсат етілмеген"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk achtergronddienst behouden"),
|
("Keep RustDesk background service", "RustDesk achtergronddienst behouden"),
|
||||||
("Ignore Battery Optimizations", "Negeer Batterij Optimalisaties"),
|
("Ignore Battery Optimizations", "Negeer Batterij Optimalisaties"),
|
||||||
("android_open_battery_optimizations_tip", "Ga naar de volgende pagina met instellingen"),
|
("android_open_battery_optimizations_tip", "Ga naar de volgende pagina met instellingen"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Verbinding niet toegestaan"),
|
("Connection not allowed", "Verbinding niet toegestaan"),
|
||||||
("Legacy mode", "Verouderde modus"),
|
("Legacy mode", "Verouderde modus"),
|
||||||
("Map mode", "Map mode"),
|
("Map mode", "Map mode"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Zachowaj usługę RustDesk w tle"),
|
("Keep RustDesk background service", "Zachowaj usługę RustDesk w tle"),
|
||||||
("Ignore Battery Optimizations", "Ignoruj optymalizację baterii"),
|
("Ignore Battery Optimizations", "Ignoruj optymalizację baterii"),
|
||||||
("android_open_battery_optimizations_tip", "Jeśli chcesz wyłączyć tę funkcję, przejdź do następnej strony ustawień aplikacji RustDesk, znajdź i wprowadź [Bateria], odznacz [Bez ograniczeń]"),
|
("android_open_battery_optimizations_tip", "Jeśli chcesz wyłączyć tę funkcję, przejdź do następnej strony ustawień aplikacji RustDesk, znajdź i wprowadź [Bateria], odznacz [Bez ograniczeń]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Połączenie niedozwolone"),
|
("Connection not allowed", "Połączenie niedozwolone"),
|
||||||
("Legacy mode", "Tryb kompatybilności wstecznej (legacy)"),
|
("Legacy mode", "Tryb kompatybilności wstecznej (legacy)"),
|
||||||
("Map mode", "Tryb mapowania"),
|
("Map mode", "Tryb mapowania"),
|
||||||
@ -456,9 +458,6 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Reconnect", "Połącz ponownie"),
|
("Reconnect", "Połącz ponownie"),
|
||||||
("Codec", "Kodek"),
|
("Codec", "Kodek"),
|
||||||
("Resolution", "Rozdzielczość"),
|
("Resolution", "Rozdzielczość"),
|
||||||
("Use temporary password", "Użyj hasła tymczasowego"),
|
|
||||||
("Set temporary password length", "Ustaw długość hasła tymczasowego"),
|
|
||||||
("Key", "Klucz"),
|
|
||||||
("No transfers in progress", ""),
|
("No transfers in progress", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Manter o serviço RustDesk em funcionamento"),
|
("Keep RustDesk background service", "Manter o serviço RustDesk em funcionamento"),
|
||||||
("Ignore Battery Optimizations", "Ignorar optimizações de Bateria"),
|
("Ignore Battery Optimizations", "Ignorar optimizações de Bateria"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Ligação não autorizada"),
|
("Connection not allowed", "Ligação não autorizada"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
@ -454,8 +456,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Stop voice call", ""),
|
("Stop voice call", ""),
|
||||||
("relay_hint_tip", ""),
|
("relay_hint_tip", ""),
|
||||||
("Reconnect", ""),
|
("Reconnect", ""),
|
||||||
("No transfers in progress", ""),
|
|
||||||
("Codec", ""),
|
("Codec", ""),
|
||||||
("Resolution", ""),
|
("Resolution", ""),
|
||||||
|
("No transfers in progress", ""),
|
||||||
].iter().cloned().collect();
|
].iter().cloned().collect();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Manter o serviço do RustDesk executando em segundo plano"),
|
("Keep RustDesk background service", "Manter o serviço do RustDesk executando em segundo plano"),
|
||||||
("Ignore Battery Optimizations", "Ignorar otimizações de bateria"),
|
("Ignore Battery Optimizations", "Ignorar otimizações de bateria"),
|
||||||
("android_open_battery_optimizations_tip", "Abrir otimizações de bateria"),
|
("android_open_battery_optimizations_tip", "Abrir otimizações de bateria"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Conexão não permitida"),
|
("Connection not allowed", "Conexão não permitida"),
|
||||||
("Legacy mode", "Modo legado"),
|
("Legacy mode", "Modo legado"),
|
||||||
("Map mode", "Modo mapa"),
|
("Map mode", "Modo mapa"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Rulează serviciul RustDesk în fundal"),
|
("Keep RustDesk background service", "Rulează serviciul RustDesk în fundal"),
|
||||||
("Ignore Battery Optimizations", "Ignoră optimizările de baterie"),
|
("Ignore Battery Optimizations", "Ignoră optimizările de baterie"),
|
||||||
("android_open_battery_optimizations_tip", "Pentru dezactivarea acestei funcții, accesează setările aplicației RustDesk, deschide secțiunea [Baterie] și deselectează [Fără restricții]."),
|
("android_open_battery_optimizations_tip", "Pentru dezactivarea acestei funcții, accesează setările aplicației RustDesk, deschide secțiunea [Baterie] și deselectează [Fără restricții]."),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Conexiune neautoriztă"),
|
("Connection not allowed", "Conexiune neautoriztă"),
|
||||||
("Legacy mode", "Mod legacy"),
|
("Legacy mode", "Mod legacy"),
|
||||||
("Map mode", "Mod hartă"),
|
("Map mode", "Mod hartă"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Держать в фоне службу RustDesk"),
|
("Keep RustDesk background service", "Держать в фоне службу RustDesk"),
|
||||||
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
|
("Ignore Battery Optimizations", "Игнорировать оптимизацию батареи"),
|
||||||
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек"),
|
("android_open_battery_optimizations_tip", "Перейдите на следующую страницу настроек"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Подключение не разрешено"),
|
("Connection not allowed", "Подключение не разрешено"),
|
||||||
("Legacy mode", "Устаревший режим"),
|
("Legacy mode", "Устаревший режим"),
|
||||||
("Map mode", "Режим сопоставления"),
|
("Map mode", "Режим сопоставления"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Ohrani RustDeskovo storitev v ozadju"),
|
("Keep RustDesk background service", "Ohrani RustDeskovo storitev v ozadju"),
|
||||||
("Ignore Battery Optimizations", "Prezri optimizacije baterije"),
|
("Ignore Battery Optimizations", "Prezri optimizacije baterije"),
|
||||||
("android_open_battery_optimizations_tip", "Če želite izklopiti to možnost, pojdite v nastavitve aplikacije RustDesk, poiščite »Baterija« in izklopite »Neomejeno«"),
|
("android_open_battery_optimizations_tip", "Če želite izklopiti to možnost, pojdite v nastavitve aplikacije RustDesk, poiščite »Baterija« in izklopite »Neomejeno«"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Povezava ni dovoljena"),
|
("Connection not allowed", "Povezava ni dovoljena"),
|
||||||
("Legacy mode", "Stari način"),
|
("Legacy mode", "Stari način"),
|
||||||
("Map mode", "Način preslikave"),
|
("Map mode", "Način preslikave"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Mbaje shërbimin e sfondit të RustDesk"),
|
("Keep RustDesk background service", "Mbaje shërbimin e sfondit të RustDesk"),
|
||||||
("Ignore Battery Optimizations", "Injoro optimizimet e baterisë"),
|
("Ignore Battery Optimizations", "Injoro optimizimet e baterisë"),
|
||||||
("android_open_battery_optimizations_tip", "Nëse dëshironi ta çaktivizoni këtë veçori, ju lutemi shkoni te faqja tjetër e cilësimeve të aplikacionit RustDesk, gjeni dhe shtypni [Batteri], hiqni zgjedhjen [Te pakufizuara]"),
|
("android_open_battery_optimizations_tip", "Nëse dëshironi ta çaktivizoni këtë veçori, ju lutemi shkoni te faqja tjetër e cilësimeve të aplikacionit RustDesk, gjeni dhe shtypni [Batteri], hiqni zgjedhjen [Te pakufizuara]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Lidhja nuk lejohet"),
|
("Connection not allowed", "Lidhja nuk lejohet"),
|
||||||
("Legacy mode", "Modaliteti i trashëgimisë"),
|
("Legacy mode", "Modaliteti i trashëgimisë"),
|
||||||
("Map mode", "Modaliteti i hartës"),
|
("Map mode", "Modaliteti i hartës"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Zadrži RustDesk kao pozadinski servis"),
|
("Keep RustDesk background service", "Zadrži RustDesk kao pozadinski servis"),
|
||||||
("Ignore Battery Optimizations", "Zanemari optimizacije baterije"),
|
("Ignore Battery Optimizations", "Zanemari optimizacije baterije"),
|
||||||
("android_open_battery_optimizations_tip", "Ako želite da onemogućite ovu funkciju, molimo idite na sledeću stranicu za podešavanje RustDesk aplikacije, pronađite i uđite u [Battery], isključite [Unrestricted]"),
|
("android_open_battery_optimizations_tip", "Ako želite da onemogućite ovu funkciju, molimo idite na sledeću stranicu za podešavanje RustDesk aplikacije, pronađite i uđite u [Battery], isključite [Unrestricted]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Konekcija nije dozvoljena"),
|
("Connection not allowed", "Konekcija nije dozvoljena"),
|
||||||
("Legacy mode", "Zastareli mod"),
|
("Legacy mode", "Zastareli mod"),
|
||||||
("Map mode", "Mod mapiranja"),
|
("Map mode", "Mod mapiranja"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Behåll RustDesk i bakgrunden"),
|
("Keep RustDesk background service", "Behåll RustDesk i bakgrunden"),
|
||||||
("Ignore Battery Optimizations", "Ignorera batterioptimering"),
|
("Ignore Battery Optimizations", "Ignorera batterioptimering"),
|
||||||
("android_open_battery_optimizations_tip", "Om du vill stänga av denna funktion, gå till nästa RustDesk programs inställningar, hitta [Batteri], Checka ur [Obegränsad]"),
|
("android_open_battery_optimizations_tip", "Om du vill stänga av denna funktion, gå till nästa RustDesk programs inställningar, hitta [Batteri], Checka ur [Obegränsad]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Anslutning ej tillåten"),
|
("Connection not allowed", "Anslutning ej tillåten"),
|
||||||
("Legacy mode", "Legacy mode"),
|
("Legacy mode", "Legacy mode"),
|
||||||
("Map mode", "Kartläge"),
|
("Map mode", "Kartläge"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", ""),
|
("Keep RustDesk background service", ""),
|
||||||
("Ignore Battery Optimizations", ""),
|
("Ignore Battery Optimizations", ""),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", ""),
|
("Connection not allowed", ""),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"),
|
("Keep RustDesk background service", "คงสถานะการทำงานเบื้องหลังของเซอร์วิส RustDesk"),
|
||||||
("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"),
|
("Ignore Battery Optimizations", "เพิกเฉยการตั้งค่าการใช้งาน Battery Optimization"),
|
||||||
("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"),
|
("android_open_battery_optimizations_tip", "หากคุณต้องการปิดการใช้งานฟีเจอร์นี้ กรุณาไปยังหน้าตั้งค่าในแอปพลิเคชัน RustDesk ค้นหาหัวข้อ [Battery] และยกเลิกการเลือกรายการ [Unrestricted]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"),
|
("Connection not allowed", "การเชื่อมต่อไม่อนุญาต"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "RustDesk arka plan hizmetini sürdürün"),
|
("Keep RustDesk background service", "RustDesk arka plan hizmetini sürdürün"),
|
||||||
("Ignore Battery Optimizations", "Pil Optimizasyonlarını Yoksay"),
|
("Ignore Battery Optimizations", "Pil Optimizasyonlarını Yoksay"),
|
||||||
("android_open_battery_optimizations_tip", ""),
|
("android_open_battery_optimizations_tip", ""),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "bağlantıya izin verilmedi"),
|
("Connection not allowed", "bağlantıya izin verilmedi"),
|
||||||
("Legacy mode", "Eski mod"),
|
("Legacy mode", "Eski mod"),
|
||||||
("Map mode", "Haritalama modu"),
|
("Map mode", "Haritalama modu"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "保持RustDesk後台服務"),
|
("Keep RustDesk background service", "保持RustDesk後台服務"),
|
||||||
("Ignore Battery Optimizations", "忽略電池優化"),
|
("Ignore Battery Optimizations", "忽略電池優化"),
|
||||||
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
("android_open_battery_optimizations_tip", "如需關閉此功能,請在接下來的RustDesk應用設置頁面中,找到並進入 [電源] 頁面,取消勾選 [不受限制]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "對方不允許連接"),
|
("Connection not allowed", "對方不允許連接"),
|
||||||
("Legacy mode", "傳統模式"),
|
("Legacy mode", "傳統模式"),
|
||||||
("Map mode", "1:1傳輸"),
|
("Map mode", "1:1傳輸"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Зберегти фонову службу RustDesk"),
|
("Keep RustDesk background service", "Зберегти фонову службу RustDesk"),
|
||||||
("Ignore Battery Optimizations", "Ігнорувати оптимізацію батареї"),
|
("Ignore Battery Optimizations", "Ігнорувати оптимізацію батареї"),
|
||||||
("android_open_battery_optimizations_tip", "Перейдіть на наступну сторінку налаштувань"),
|
("android_open_battery_optimizations_tip", "Перейдіть на наступну сторінку налаштувань"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Підключення не дозволено"),
|
("Connection not allowed", "Підключення не дозволено"),
|
||||||
("Legacy mode", "Застарілий режим"),
|
("Legacy mode", "Застарілий режим"),
|
||||||
("Map mode", "Режим карти"),
|
("Map mode", "Режим карти"),
|
||||||
|
|||||||
@ -312,6 +312,8 @@ pub static ref T: std::collections::HashMap<&'static str, &'static str> =
|
|||||||
("Keep RustDesk background service", "Giữ dịch vụ nền RustDesk"),
|
("Keep RustDesk background service", "Giữ dịch vụ nền RustDesk"),
|
||||||
("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"),
|
("Ignore Battery Optimizations", "Bỏ qua các tối ưu pin"),
|
||||||
("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"),
|
("android_open_battery_optimizations_tip", "Nếu bạn muốn tắt tính năng này, vui lòng chuyển đến trang cài đặt ứng dụng RustDesk tiếp theo, tìm và nhập [Pin], Bỏ chọn [Không hạn chế]"),
|
||||||
|
("Start on Boot", ""),
|
||||||
|
("Start the screen sharing service on boot, requires special permissions", ""),
|
||||||
("Connection not allowed", "Kết nối không đuợc phép"),
|
("Connection not allowed", "Kết nối không đuợc phép"),
|
||||||
("Legacy mode", ""),
|
("Legacy mode", ""),
|
||||||
("Map mode", ""),
|
("Map mode", ""),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user