Merge pull request #1803 from Heap-Hop/master
feat: Android session record
This commit is contained in:
commit
080818cf16
@ -7,7 +7,7 @@ buildscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.3.0'
|
classpath 'com.android.tools.build:gradle:7.0.0'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath 'com.google.gms:google-services:4.3.14'
|
classpath 'com.google.gms:google-services:4.3.14'
|
||||||
}
|
}
|
||||||
|
@ -582,13 +582,35 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
value: 'block-input'));
|
value: 'block-input'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gFFI.ffiModel.permissions["restart"] != false &&
|
if (perms["restart"] != false &&
|
||||||
(pi.platform == "Linux" ||
|
(pi.platform == "Linux" ||
|
||||||
pi.platform == "Windows" ||
|
pi.platform == "Windows" ||
|
||||||
pi.platform == "Mac OS")) {
|
pi.platform == "Mac OS")) {
|
||||||
more.add(PopupMenuItem<String>(
|
more.add(PopupMenuItem<String>(
|
||||||
child: Text(translate('Restart Remote Device')), value: 'restart'));
|
child: Text(translate('Restart Remote Device')), value: 'restart'));
|
||||||
}
|
}
|
||||||
|
// Currently only support VP9
|
||||||
|
if (gFFI.recordingModel.start ||
|
||||||
|
(perms["recording"] != false &&
|
||||||
|
gFFI.qualityMonitorModel.data.codecFormat == "VP9")) {
|
||||||
|
more.add(PopupMenuItem<String>(
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(translate(gFFI.recordingModel.start
|
||||||
|
? 'Stop session recording'
|
||||||
|
: 'Start session recording')),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 12),
|
||||||
|
child: Icon(
|
||||||
|
gFFI.recordingModel.start
|
||||||
|
? Icons.pause_circle_filled
|
||||||
|
: Icons.videocam_outlined,
|
||||||
|
color: MyTheme.accent),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
value: 'record'));
|
||||||
|
}
|
||||||
() async {
|
() async {
|
||||||
var value = await showMenu(
|
var value = await showMenu(
|
||||||
context: context,
|
context: context,
|
||||||
@ -630,6 +652,8 @@ class _RemotePageState extends State<RemotePage> {
|
|||||||
gFFI.cursorModel.reset();
|
gFFI.cursorModel.reset();
|
||||||
} else if (value == 'restart') {
|
} else if (value == 'restart') {
|
||||||
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
showRestartRemoteDevice(pi, widget.id, gFFI.dialogManager);
|
||||||
|
} else if (value == 'record') {
|
||||||
|
gFFI.recordingModel.toggle();
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,8 @@ var _enableAbr = false;
|
|||||||
var _denyLANDiscovery = false;
|
var _denyLANDiscovery = false;
|
||||||
var _onlyWhiteList = false;
|
var _onlyWhiteList = false;
|
||||||
var _enableDirectIPAccess = false;
|
var _enableDirectIPAccess = false;
|
||||||
|
var _enableRecordSession = false;
|
||||||
|
var _autoRecordIncomingSession = false;
|
||||||
var _localIP = "";
|
var _localIP = "";
|
||||||
var _directAccessPort = "";
|
var _directAccessPort = "";
|
||||||
|
|
||||||
@ -78,6 +80,21 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
_enableDirectIPAccess = enableDirectIPAccess;
|
_enableDirectIPAccess = enableDirectIPAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final enableRecordSession = option2bool('enable-record-session',
|
||||||
|
await bind.mainGetOption(key: 'enable-record-session'));
|
||||||
|
if (enableRecordSession != _enableRecordSession) {
|
||||||
|
update = true;
|
||||||
|
_enableRecordSession = enableRecordSession;
|
||||||
|
}
|
||||||
|
|
||||||
|
final autoRecordIncomingSession = option2bool(
|
||||||
|
'allow-auto-record-incoming',
|
||||||
|
await bind.mainGetOption(key: 'allow-auto-record-incoming'));
|
||||||
|
if (autoRecordIncomingSession != _autoRecordIncomingSession) {
|
||||||
|
update = true;
|
||||||
|
_autoRecordIncomingSession = autoRecordIncomingSession;
|
||||||
|
}
|
||||||
|
|
||||||
final localIP = await bind.mainGetOption(key: 'local-ip-addr');
|
final localIP = await bind.mainGetOption(key: 'local-ip-addr');
|
||||||
if (localIP != _localIP) {
|
if (localIP != _localIP) {
|
||||||
update = true;
|
update = true;
|
||||||
@ -178,6 +195,19 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
title: Text(translate('Enable Recording Session')),
|
||||||
|
initialValue: _enableRecordSession,
|
||||||
|
onToggle: (v) async {
|
||||||
|
await bind.mainSetOption(
|
||||||
|
key: "enable-record-session", value: v ? "" : "N");
|
||||||
|
final newValue =
|
||||||
|
await bind.mainGetOption(key: "enable-record-session") != "N";
|
||||||
|
setState(() {
|
||||||
|
_enableRecordSession = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
SettingsTile.switchTile(
|
SettingsTile.switchTile(
|
||||||
title: Row(
|
title: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
@ -299,6 +329,33 @@ class _SettingsState extends State<SettingsPage> with WidgetsBindingObserver {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
]),
|
]),
|
||||||
|
SettingsSection(
|
||||||
|
title: Text(translate("Recording")),
|
||||||
|
tiles: [
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
title: Text(translate('Automatically record incoming sessions')),
|
||||||
|
leading: Icon(Icons.videocam),
|
||||||
|
description: FutureBuilder(
|
||||||
|
builder: (ctx, data) => Offstage(
|
||||||
|
offstage: !data.hasData,
|
||||||
|
child: Text("${translate("Directory")}: ${data.data}")),
|
||||||
|
future: bind.mainDefaultVideoSaveDirectory()),
|
||||||
|
initialValue: _autoRecordIncomingSession,
|
||||||
|
onToggle: (v) async {
|
||||||
|
await bind.mainSetOption(
|
||||||
|
key: "allow-auto-record-incoming",
|
||||||
|
value: bool2option("allow-auto-record-incoming", v));
|
||||||
|
final newValue = option2bool(
|
||||||
|
'allow-auto-record-incoming',
|
||||||
|
await bind.mainGetOption(
|
||||||
|
key: 'allow-auto-record-incoming'));
|
||||||
|
setState(() {
|
||||||
|
_autoRecordIncomingSession = newValue;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
title: Text(translate("Share Screen")),
|
title: Text(translate("Share Screen")),
|
||||||
tiles: shareScreenTiles,
|
tiles: shareScreenTiles,
|
||||||
|
@ -515,7 +515,7 @@ class FileModel extends ChangeNotifier {
|
|||||||
items.items.forEach((from) async {
|
items.items.forEach((from) async {
|
||||||
_jobId++;
|
_jobId++;
|
||||||
await bind.sessionSendFiles(
|
await bind.sessionSendFiles(
|
||||||
id: await bind.mainGetLastRemoteId(),
|
id: '${parent.target?.id}',
|
||||||
actId: _jobId,
|
actId: _jobId,
|
||||||
path: from.path,
|
path: from.path,
|
||||||
to: PathUtil.join(toPath, from.name, isWindows),
|
to: PathUtil.join(toPath, from.name, isWindows),
|
||||||
|
@ -231,7 +231,8 @@ class FfiModel with ChangeNotifier {
|
|||||||
} else if (type == 'input-password') {
|
} else if (type == 'input-password') {
|
||||||
enterPasswordDialog(id, dialogManager);
|
enterPasswordDialog(id, dialogManager);
|
||||||
} else if (type == 'restarting') {
|
} else if (type == 'restarting') {
|
||||||
showMsgBox(id, type, title, text, link, false, dialogManager, hasCancel: false);
|
showMsgBox(id, type, title, text, link, false, dialogManager,
|
||||||
|
hasCancel: false);
|
||||||
} else {
|
} else {
|
||||||
var hasRetry = evt['hasRetry'] == 'true';
|
var hasRetry = evt['hasRetry'] == 'true';
|
||||||
showMsgBox(id, type, title, text, link, hasRetry, dialogManager);
|
showMsgBox(id, type, title, text, link, hasRetry, dialogManager);
|
||||||
@ -1003,16 +1004,16 @@ class RecordingModel with ChangeNotifier {
|
|||||||
get start => _start;
|
get start => _start;
|
||||||
|
|
||||||
onSwitchDisplay() {
|
onSwitchDisplay() {
|
||||||
if (!isDesktop || !_start) return;
|
if (isIOS || !_start) return;
|
||||||
var id = parent.target?.id;
|
var id = parent.target?.id;
|
||||||
int? width = parent.target?.canvasModel.getDisplayWidth();
|
int? width = parent.target?.canvasModel.getDisplayWidth();
|
||||||
int? height = parent.target?.canvasModel.getDisplayWidth();
|
int? height = parent.target?.canvasModel.getDisplayHeight();
|
||||||
if (id == null || width == null || height == null) return;
|
if (id == null || width == null || height == null) return;
|
||||||
bind.sessionRecordScreen(id: id, start: true, width: width, height: height);
|
bind.sessionRecordScreen(id: id, start: true, width: width, height: height);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggle() {
|
toggle() {
|
||||||
if (!isDesktop) return;
|
if (isIOS) return;
|
||||||
var id = parent.target?.id;
|
var id = parent.target?.id;
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
_start = !_start;
|
_start = !_start;
|
||||||
@ -1025,7 +1026,7 @@ class RecordingModel with ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onClose() {
|
onClose() {
|
||||||
if (!isDesktop) return;
|
if (isIOS) return;
|
||||||
var id = parent.target?.id;
|
var id = parent.target?.id;
|
||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
_start = false;
|
_start = false;
|
||||||
|
@ -449,7 +449,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
log::info!("gdi: {}", c.is_gdi());
|
log::info!("gdi: {}", c.is_gdi());
|
||||||
let codec_name = Encoder::current_hw_encoder_name();
|
let codec_name = Encoder::current_hw_encoder_name();
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(target_os = "ios"))]
|
||||||
let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() {
|
let recorder = if !Config::get_option("allow-auto-record-incoming").is_empty() {
|
||||||
Recorder::new(RecorderContext {
|
Recorder::new(RecorderContext {
|
||||||
id: "local".to_owned(),
|
id: "local".to_owned(),
|
||||||
@ -463,7 +463,7 @@ fn run(sp: GenericService) -> ResultType<()> {
|
|||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
};
|
};
|
||||||
#[cfg(any(target_os = "android", target_os = "ios"))]
|
#[cfg(target_os = "ios")]
|
||||||
let recorder: Arc<Mutex<Option<Recorder>>> = Default::default();
|
let recorder: Arc<Mutex<Option<Recorder>>> = Default::default();
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
start_uac_elevation_check();
|
start_uac_elevation_check();
|
||||||
@ -674,7 +674,7 @@ fn handle_one_frame(
|
|||||||
|
|
||||||
let mut send_conn_ids: HashSet<i32> = Default::default();
|
let mut send_conn_ids: HashSet<i32> = Default::default();
|
||||||
if let Ok(msg) = encoder.encode_to_message(frame, ms) {
|
if let Ok(msg) = encoder.encode_to_message(frame, ms) {
|
||||||
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
#[cfg(not(target_os = "ios"))]
|
||||||
recorder
|
recorder
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -413,7 +413,7 @@ pub async fn start_listen<T: InvokeUiCM>(
|
|||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cm.remove_connection(current_id);
|
cm.remove_connection(current_id, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_fs(fs: ipc::FS, write_jobs: &mut Vec<fs::TransferJob>, tx: &UnboundedSender<Data>) {
|
async fn handle_fs(fs: ipc::FS, write_jobs: &mut Vec<fs::TransferJob>, tx: &UnboundedSender<Data>) {
|
||||||
|
@ -733,11 +733,21 @@ pub fn get_langs() -> String {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn default_video_save_directory() -> String {
|
pub fn default_video_save_directory() -> String {
|
||||||
let appname = crate::get_app_name();
|
let appname = crate::get_app_name();
|
||||||
|
|
||||||
|
#[cfg(any(target_os = "android", target_os = "ios"))]
|
||||||
|
if let Ok(home) = config::APP_HOME_DIR.read() {
|
||||||
|
let mut path = home.to_owned();
|
||||||
|
path.push_str("/RustDesk/ScreenRecord");
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(user) = directories_next::UserDirs::new() {
|
if let Some(user) = directories_next::UserDirs::new() {
|
||||||
if let Some(video_dir) = user.video_dir() {
|
if let Some(video_dir) = user.video_dir() {
|
||||||
return video_dir.join(appname).to_string_lossy().to_string();
|
return video_dir.join(appname).to_string_lossy().to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(any(target_os = "android", target_os = "ios")))]
|
||||||
if let Some(home) = platform::get_active_user_home() {
|
if let Some(home) = platform::get_active_user_home() {
|
||||||
let name = if cfg!(target_os = "macos") {
|
let name = if cfg!(target_os = "macos") {
|
||||||
"Movies"
|
"Movies"
|
||||||
@ -746,6 +756,7 @@ pub fn default_video_save_directory() -> String {
|
|||||||
};
|
};
|
||||||
return home.join(name).join(appname).to_string_lossy().to_string();
|
return home.join(name).join(appname).to_string_lossy().to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(exe) = std::env::current_exe() {
|
if let Ok(exe) = std::env::current_exe() {
|
||||||
if let Some(dir) = exe.parent() {
|
if let Some(dir) = exe.parent() {
|
||||||
return dir.join("videos").to_string_lossy().to_string();
|
return dir.join("videos").to_string_lossy().to_string();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user