mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-27 16:13:13 +08:00
[fix] show shortcut controls only for Windows hosts
This commit is contained in:
@@ -338,7 +338,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
|
||||
public void mute(){muted=!muted;if(session!=null)session.control(2,muted?0:1);controls.setMuted(muted);}
|
||||
public void mouse(){boolean relative=!preferences.getBoolean("relativeMouse",true);preferences.edit().putBoolean("relativeMouse",relative).apply();video.setRelative(relative);controls.setRelative(relative);}
|
||||
public void videoSettings(int field,int value){RemoteVideoSettings.Values old=MainActivity.this.videoSettings.selection;updateVideoSettings(new RemoteVideoSettings.Values(field==0?value:old.quality,field==1?value:old.frameRate,field==2?value:old.preference));}
|
||||
});controls.setRelative(preferences.getBoolean("relativeMouse",true));controls.setMuted(muted);controls.videoSettings(videoSettings);root.addView(controls,new LinearLayout.LayoutParams(-1,-1));getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
});controls.setHostPlatform(hostPlatform);controls.setRelative(preferences.getBoolean("relativeMouse",true));controls.setMuted(muted);controls.videoSettings(videoSettings);root.addView(controls,new LinearLayout.LayoutParams(-1,-1));getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
controls.attachMouseBar(canvas,new VirtualMouseBar(ui,video.mouseInput));
|
||||
privacyChanged();
|
||||
video.remoteFocusChanged=()->{if(controls!=null)controls.resetKeyboardContext();};
|
||||
@@ -496,8 +496,8 @@ public final class MainActivity extends Activity implements NativeSession.Listen
|
||||
if(!ready)return;int count=Math.min(displays.length(),8);if(count==0){toast("等待远端屏幕信息");return;}String[] labels=new String[count];for(int i=0;i<count;i++){JSONObject display=displays.optJSONObject(i);labels[i]=(i+1)+" · "+(display==null?"显示器":display.optString("name","显示器"));}new AlertDialog.Builder(this).setTitle("显示器").setItems(labels,(d,w)->{if(session!=null){if(w!=selectedDisplay&&video!=null)video.resetViewport();selectedDisplay=w;session.control(4,w);if(controls!=null)controls.resetVideoStatistics();}}).show();
|
||||
}
|
||||
private void secureAttention(){
|
||||
if(!ready||session==null)return;
|
||||
if(!remoteServiceAvailable){controlFeedback(hostPlatform.equals("windows")?"请在远端启用 CrossDesk 系统服务后重试":"Ctrl+Alt+Del 需要远端 Windows 系统服务支持");return;}
|
||||
if(!ready||session==null||!hostPlatform.equals("windows"))return;
|
||||
if(!remoteServiceAvailable){controlFeedback("请在远端启用 CrossDesk 系统服务后重试");return;}
|
||||
session.secureAttention();
|
||||
}
|
||||
private void chooseFile(){
|
||||
@@ -650,6 +650,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
|
||||
JSONObject info=message.optJSONObject("host_info");
|
||||
if(info!=null){
|
||||
hostPlatform=info.optString("platform","");JSONArray list=info.optJSONArray("displays");if(list!=null)displays=list;history.metadata(serverKey(),connectedRemote,info.optString("host_name",connectedRemote),hostPlatform);
|
||||
if(controls!=null)controls.setHostPlatform(hostPlatform);
|
||||
boolean wasSupported=videoSettings.supported;videoSettings.setSupported(info.optBoolean("supports_video_settings",false));
|
||||
if(!videoSettings.supported)cancelVideoSettingsTimeout();
|
||||
if(controls!=null)controls.videoSettings(videoSettings);
|
||||
@@ -739,7 +740,7 @@ public final class MainActivity extends Activity implements NativeSession.Listen
|
||||
videoSettings=retained.videoSettings;ready=true;retained.attach(this);
|
||||
if(rebuild)sessionScreen(retained.autoLandscape);
|
||||
if(retained.width>0&&retained.height>0)videoSize(retained.width,retained.height);
|
||||
if(controls!=null){controls.setMuted(muted);controls.videoSettings(videoSettings);controls.fileStatus(retained.fileStatus,receivedFile!=null);}
|
||||
if(controls!=null){controls.setHostPlatform(hostPlatform);controls.setMuted(muted);controls.videoSettings(videoSettings);controls.fileStatus(retained.fileStatus,receivedFile!=null);}
|
||||
retained.setBackground(false);signaling(signalState,localIdentity);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ final class SessionControls extends FrameLayout {
|
||||
private LinearLayout[] trafficRows;
|
||||
private TextView[] networkDetails;
|
||||
private View encryptionLock,encryptionOpen;
|
||||
private boolean muted,relative=true,orbPositioned;
|
||||
private boolean muted,relative=true,orbPositioned,secureAttentionVisible;
|
||||
SessionControls(Context context,MobileUi ui,Actions actions){
|
||||
super(context);this.ui=ui;this.actions=actions;
|
||||
keyboard=new RemoteKeyboard(context,ui,actions,()->{requestLayout();positionMouse();});
|
||||
@@ -66,6 +66,12 @@ final class SessionControls extends FrameLayout {
|
||||
drag(orb,orb,()->{if(menu==null)openMenu("controls");else closeMenu();});
|
||||
}
|
||||
void setRelative(boolean value){relative=value;if(menu!=null&&menuPage.equals("controls"))openMenu("controls");}
|
||||
void setHostPlatform(String platform){
|
||||
boolean visible="windows".equals(platform);
|
||||
if(secureAttentionVisible==visible)return;
|
||||
secureAttentionVisible=visible;
|
||||
if(menu!=null&&menuPage.equals("controls"))openMenu("controls");
|
||||
}
|
||||
void attachMouseBar(FrameLayout host, VirtualMouseBar bar) {
|
||||
mouseHost=host;mouseBar=bar;
|
||||
// A direct, tightly bounded sibling of the video is essential: otherwise
|
||||
@@ -130,8 +136,13 @@ final class SessionControls extends FrameLayout {
|
||||
String[] icons={"keyboard","mouse","mouse","display","sliders","audio","folder","chart","lock","clipboard","lock"};
|
||||
Runnable[] clicks={()->{closeMenu();toggleKeyboard();},this::toggleMouse,actions::mouse,actions::display,()->openMenu("video"),actions::mute,actions::sendFile,()->openMenu("network"),actions::secureAttention,actions::clipboard,actions::togglePrivacy};
|
||||
menuColumns=controlColumns();
|
||||
int controlCount=privacy.desktopAvailable?labels.length:labels.length-1;
|
||||
for(int row=0;row<(controlCount+menuColumns-1)/menuColumns;row++){LinearLayout line=ui.row();for(int col=0;col<menuColumns;col++){int index=row*menuColumns+col;if(index>=controlCount){View spacer=new View(getContext());LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,ui.dp(52),1);if(col>0)p.leftMargin=ui.dp(7);line.addView(spacer,p);continue;}LinearLayout control=ui.column();control.setGravity(Gravity.CENTER);control.setBackground(ui.background(0xFFF0F0F2,10));control.addView(ui.icon(icons[index],INK),ui.size(19,19));ui.gap(control,4);TextView caption=ui.text(labels[index],10,false);caption.setSingleLine();caption.setGravity(Gravity.CENTER);control.addView(caption);control.setContentDescription(labels[index]);control.setClickable(true);control.setFocusable(true);control.setOnClickListener(v->clicks[index].run());
|
||||
java.util.List<Integer> visibleControls=new java.util.ArrayList<>();
|
||||
for(int index=0;index<labels.length;index++){
|
||||
if(index==8&&!secureAttentionVisible||index==10&&!privacy.desktopAvailable)continue;
|
||||
visibleControls.add(index);
|
||||
}
|
||||
int controlCount=visibleControls.size();
|
||||
for(int row=0;row<(controlCount+menuColumns-1)/menuColumns;row++){LinearLayout line=ui.row();for(int col=0;col<menuColumns;col++){int position=row*menuColumns+col;if(position>=controlCount){View spacer=new View(getContext());LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,ui.dp(52),1);if(col>0)p.leftMargin=ui.dp(7);line.addView(spacer,p);continue;}int index=visibleControls.get(position);LinearLayout control=ui.column();control.setGravity(Gravity.CENTER);control.setBackground(ui.background(0xFFF0F0F2,10));control.addView(ui.icon(icons[index],INK),ui.size(19,19));ui.gap(control,4);TextView caption=ui.text(labels[index],10,false);caption.setSingleLine();caption.setGravity(Gravity.CENTER);control.addView(caption);control.setContentDescription(labels[index]);control.setClickable(true);control.setFocusable(true);control.setOnClickListener(v->clicks[index].run());
|
||||
if(index==10){privacyButton=control;privacyCaption=caption;}
|
||||
LinearLayout.LayoutParams p=new LinearLayout.LayoutParams(0,ui.dp(52),1);if(col>0)p.leftMargin=ui.dp(7);line.addView(control,p);
|
||||
}body.addView(line);if((row+1)*menuColumns<controlCount)ui.gap(body,7);}
|
||||
|
||||
@@ -2144,7 +2144,7 @@ void GuiApplication::BindStreamCallbacks() {
|
||||
if (name != "Ctrl+Alt+Del" && name != "Win+L") return;
|
||||
{
|
||||
std::lock_guard lock(props->remote_version_mutex_);
|
||||
if (name == "Ctrl+Alt+Del" && props->remote_platform_ == HostPlatform::HarmonyOS) return;
|
||||
if (props->remote_platform_ != HostPlatform::Windows) return;
|
||||
}
|
||||
RemoteAction action{};
|
||||
action.type = ControlType::service_command;
|
||||
@@ -3796,9 +3796,7 @@ void GuiApplication::SyncStreamWindow() {
|
||||
}
|
||||
}
|
||||
(*ui_->stream)->set_remote_update_visible(remote_update_available);
|
||||
// All desktop clients use the same menu for a Harmony host. Windows SAS is
|
||||
// not an ordinary key chord and has no public Harmony implementation.
|
||||
(*ui_->stream)->set_secure_attention_supported(remote_platform != HostPlatform::HarmonyOS);
|
||||
(*ui_->stream)->set_secure_attention_supported(remote_platform == HostPlatform::Windows);
|
||||
if (remote_update_available) {
|
||||
SetStreamKeyboardFocus(false);
|
||||
}
|
||||
|
||||
@@ -335,10 +335,8 @@ export component StreamWindow inherits Window {
|
||||
callback reorder-tab(int, length, length);
|
||||
callback close-tab(string);
|
||||
callback switch-display(int);
|
||||
in property <bool> secure-attention-supported: true;
|
||||
private property <[string]> secure-shortcut-items: ["Ctrl+Alt+Del", "Win+L"];
|
||||
private property <[string]> ordinary-shortcut-items: ["Win+L"];
|
||||
private property <[string]> shortcut-items: root.secure-attention-supported ? root.secure-shortcut-items : root.ordinary-shortcut-items;
|
||||
in property <bool> secure-attention-supported: false;
|
||||
private property <[string]> shortcut-items: ["Ctrl+Alt+Del", "Win+L"];
|
||||
private property <length> shortcut-menu-height: root.shortcut-items.length * 30px + 16px;
|
||||
callback send-shortcut(string);
|
||||
callback toggle-mouse-control;
|
||||
@@ -364,6 +362,7 @@ export component StreamWindow inherits Window {
|
||||
private property <bool> control-visual-expanded: false;
|
||||
private property <length> control-toolbar-height: 38px;
|
||||
private property <length> control-button-step: 32px;
|
||||
private property <length> control-shortcut-width: root.secure-attention-supported ? root.control-button-step : 0px;
|
||||
private property <length> control-privacy-width: root.privacy-visible ? root.control-button-step : 0px;
|
||||
private property <length> control-disconnect-gap: 8px;
|
||||
private property <length> control-buttons-x: root.control-docked-left ? 43px : 7px;
|
||||
@@ -416,6 +415,9 @@ export component StreamWindow inherits Window {
|
||||
root.close-control-menus();
|
||||
root.cancel-remote-pointer();
|
||||
}
|
||||
changed secure-attention-supported => {
|
||||
if !root.secure-attention-supported { root.shortcut-menu-open = false; }
|
||||
}
|
||||
changed control-expanded => {
|
||||
control-reveal-timer.running = false;
|
||||
control-collapse-timer.running = false;
|
||||
@@ -517,7 +519,7 @@ export component StreamWindow inherits Window {
|
||||
if logo-button.has-hover { return logo-button.tooltip-info; }
|
||||
if root.control-expanded && root.control-content-visible {
|
||||
if display-button.has-hover { return display-button.tooltip-info; }
|
||||
if shortcut-button.has-hover { return shortcut-button.tooltip-info; }
|
||||
if root.secure-attention-supported && shortcut-button.has-hover { return shortcut-button.tooltip-info; }
|
||||
if mouse-button.has-hover { return mouse-button.tooltip-info; }
|
||||
if root.privacy-visible && privacy-button.has-hover { return privacy-button.tooltip-info; }
|
||||
if video-button.has-hover { return video-button.tooltip-info; }
|
||||
@@ -715,7 +717,7 @@ export component StreamWindow inherits Window {
|
||||
&& pointer-y >= root.display-menu-y
|
||||
&& pointer-y <= root.display-menu-y + root.display-menu-height;
|
||||
let shortcut-menu-x = control.x + shortcut-button.x;
|
||||
let over-shortcut-menu = root.shortcut-menu-open
|
||||
let over-shortcut-menu = root.secure-attention-supported && root.shortcut-menu-open
|
||||
&& pointer-x >= shortcut-menu-x
|
||||
&& pointer-x <= shortcut-menu-x + 180px
|
||||
&& pointer-y >= root.shortcut-menu-y
|
||||
@@ -1085,7 +1087,7 @@ export component StreamWindow inherits Window {
|
||||
control := Rectangle {
|
||||
x: root.control-dragging ? root.control-drag-x : root.control-docked-left ? 0px : parent.width - self.width;
|
||||
y: min(max(0px, root.control-pos-y), parent.height - self.height);
|
||||
width: root.control-visual-expanded ? min(80px + 8 * root.control-button-step + root.control-privacy-width + root.control-disconnect-gap, parent.width) : root.control-toolbar-height;
|
||||
width: root.control-visual-expanded ? min(80px + 7 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width + root.control-disconnect-gap, parent.width) : root.control-toolbar-height;
|
||||
height: root.control-toolbar-height;
|
||||
z: 10;
|
||||
|
||||
@@ -1130,8 +1132,8 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
shortcut-button := ControlBarButton {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
visible: root.control-content-visible && root.secure-attention-supported;
|
||||
action-enabled: root.control-expanded && root.secure-attention-supported;
|
||||
x: root.control-buttons-x + root.control-button-step;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.keyboard; tooltip: StreamStrings.send-shortcut;
|
||||
@@ -1149,7 +1151,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 2 * root.control-button-step;
|
||||
x: root.control-buttons-x + root.control-button-step + root.control-shortcut-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.computer-mouse; slashed: !root.mouse-control-enabled;
|
||||
tooltip: root.mouse-control-enabled ? StreamStrings.release-mouse : StreamStrings.control-mouse;
|
||||
@@ -1159,7 +1161,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 3 * root.control-button-step + root.control-privacy-width;
|
||||
x: root.control-buttons-x + 2 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.volume-high; slashed: !root.audio-enabled;
|
||||
tooltip: root.audio-enabled ? StreamStrings.mute : StreamStrings.audio;
|
||||
@@ -1169,7 +1171,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 4 * root.control-button-step + root.control-privacy-width;
|
||||
x: root.control-buttons-x + 3 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.folder-open; tooltip: StreamStrings.select-file;
|
||||
clicked => { root.close-control-menus(); root.select-file(); }
|
||||
@@ -1178,7 +1180,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 5 * root.control-button-step + root.control-privacy-width;
|
||||
x: root.control-buttons-x + 4 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: "\u{f1de}"; selected: root.video-menu-open;
|
||||
tooltip: StreamStrings.video-settings;
|
||||
@@ -1193,7 +1195,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 6 * root.control-button-step + root.control-privacy-width;
|
||||
x: root.control-buttons-x + 5 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.signal; selected: root.stats-visible;
|
||||
tooltip: root.stats-visible ? StreamStrings.hide-stats : StreamStrings.show-stats;
|
||||
@@ -1203,7 +1205,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 7 * root.control-button-step + root.control-privacy-width;
|
||||
x: root.control-buttons-x + 6 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: root.fullscreen-enabled ? FontAwesomeIcons.compress : FontAwesomeIcons.expand;
|
||||
tooltip: root.fullscreen-enabled ? StreamStrings.exit-fullscreen : StreamStrings.fullscreen;
|
||||
@@ -1219,7 +1221,7 @@ export component StreamWindow inherits Window {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible;
|
||||
action-enabled: root.control-expanded;
|
||||
x: root.control-buttons-x + 8 * root.control-button-step + root.control-privacy-width + root.control-disconnect-gap;
|
||||
x: root.control-buttons-x + 7 * root.control-button-step + root.control-shortcut-width + root.control-privacy-width + root.control-disconnect-gap;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.xmark; tooltip: StreamStrings.disconnect;
|
||||
danger: true;
|
||||
@@ -1255,7 +1257,7 @@ export component StreamWindow inherits Window {
|
||||
privacy-button := ControlBarButton {
|
||||
reveal-progress: root.control-content-progress;
|
||||
visible: root.control-content-visible && root.privacy-visible;
|
||||
x: root.control-buttons-x + 3 * root.control-button-step;
|
||||
x: root.control-buttons-x + 2 * root.control-button-step + root.control-shortcut-width;
|
||||
y: (parent.height - self.height) / 2;
|
||||
icon: FontAwesomeIcons.user-secret;
|
||||
slashed: root.privacy-off && !root.privacy-unsupported;
|
||||
@@ -1698,7 +1700,7 @@ export component StreamWindow inherits Window {
|
||||
}
|
||||
}
|
||||
|
||||
if root.shortcut-menu-open: StreamControlSurface {
|
||||
if root.secure-attention-supported && root.shortcut-menu-open: StreamControlSurface {
|
||||
x: control.x + shortcut-button.x; y: root.shortcut-menu-y;
|
||||
width: 180px; height: root.shortcut-menu-height; z: 11;
|
||||
TouchArea { mouse-cursor: default; scroll-event(event) => { accept } }
|
||||
|
||||
@@ -86,7 +86,7 @@ python3 apps/harmony/scripts/build_slint_desktop.py --desktop --reuse-runtime \
|
||||
|
||||
`--desktop-permissions` 生成权限声明、用途说明和授权流程开关;`--signing-config` 生成签名配置并选择 `entry-default-signed.hap` 安装。生成的配置仅位于忽略的 `.native` 缓存,文件权限为 `0600`。无签名参数时仍输出普通 `entry-default-unsigned.hap`。可单独用 `--desktop-permissions` 做带权限声明的编译检查,不能据此认为已获得权限或可安装到真机。
|
||||
|
||||
远程 `lock_workstation` 命令已接到经过采集/输入授权检查的 Meta+L 路径,先释放残留键鼠,发送后配对释放,失败保留待清理状态。Windows `send_sas` 没有等价的鸿蒙公开接口,明确返回不支持;所有 PC 客户端共用的 Slint 快捷键菜单在目标为鸿蒙时不显示 Ctrl+Alt+Del,仍保留 Win+L。普通 Ctrl/Alt/Meta 组合键由原生钩子按原顺序转发,系统最终允许拦截哪些保留键仍需授权后的真机验证。
|
||||
远程 `lock_workstation` 命令已接到经过采集/输入授权检查的 Meta+L 路径,先释放残留键鼠,发送后配对释放,失败保留待清理状态。Windows `send_sas` 没有等价的鸿蒙公开接口,明确返回不支持;PC 和移动端仅在被控端为 Windows 时显示系统组合键按钮,目标为鸿蒙或其他系统时隐藏。普通 Ctrl/Alt/Meta 组合键由原生钩子按原顺序转发,系统最终允许拦截哪些保留键仍需授权后的真机验证。
|
||||
|
||||
本轮验证:普通 HAP 与带权限声明的 HAP 构建通过;普通 HAP 安装本机 2in1 模拟器后,全局钩子实际返回 `201`,无授权弹窗循环,仍收到共享窗口回退发送的 `keyboard` 数据流。真实 Ctrl+A/C、End、Ctrl+V 把 `246` 复制为 `246 246`。生产 C++/ArkTS 的模拟 SDK 回归覆盖 Alt+Tab、Meta+L、重复键、小键盘 Enter、取消、失焦、撤权、重复注册、卸载失败、后台通知、缓存失效、读取中变更/撤权,以及权限拒绝后不重复引导。macOS 公共 Slint 冒烟测试新增快捷键菜单的真实点击回归,验证移除 SAS 后首项为锁屏、弹层命中范围随菜单缩小、切回 Windows 菜单仍正常。成功授权的钩子和后台跨应用读取没有在无 ACL 包中冒充验证通过。
|
||||
|
||||
|
||||
@@ -311,7 +311,8 @@ export class MobileController {
|
||||
catch { this.toast('复制失败,请重试'); }
|
||||
}
|
||||
sas(): void {
|
||||
if (!this.serviceAvailable) { this.toast(this.state.platform === 'windows' ? '请在远端启用 CrossDesk 系统服务后重试' : 'Ctrl+Alt+Del 需要远端 Windows 系统服务支持'); return; }
|
||||
if (this.state.platform !== 'windows') return;
|
||||
if (!this.serviceAvailable) { this.toast('请在远端启用 CrossDesk 系统服务后重试'); return; }
|
||||
this.send({ op: 'sas' });
|
||||
}
|
||||
async chooseFile(): Promise<void> {
|
||||
|
||||
@@ -136,9 +136,11 @@ export struct MobileRemoteView {
|
||||
if (index === 10) this.controller?.togglePrivacy();
|
||||
}
|
||||
private labels(): string[] {
|
||||
const labels: string[] = ['键盘', this.state.relative ? '相对鼠标' : '绝对鼠标', this.mouseVisible ? '收起鼠标' : '虚拟鼠标', '显示器', '画面设置', this.state.muted ? '静音' : '声音', '发送文件', '网络状态', 'Ctrl+Alt+Del', '剪贴板'];
|
||||
if (this.state.privacyVisible) labels.push(this.state.privacyButtonTitle);
|
||||
return labels;
|
||||
return ['键盘', this.state.relative ? '相对鼠标' : '绝对鼠标', this.mouseVisible ? '收起鼠标' : '虚拟鼠标', '显示器', '画面设置', this.state.muted ? '静音' : '声音', '发送文件', '网络状态', 'Ctrl+Alt+Del', '剪贴板', this.state.privacyButtonTitle];
|
||||
}
|
||||
private controlIds(): number[] {
|
||||
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].filter((index: number) =>
|
||||
(index !== 8 || this.state.platform === 'windows') && (index !== 10 || this.state.privacyVisible));
|
||||
}
|
||||
private icons(): string[] { return ['keyboard', 'mouse', 'mouse', 'display', 'sliders', this.state.muted ? 'muted' : 'audio', 'folder', 'chart', 'lock', 'clipboard', 'lock']; }
|
||||
private columns(): number { return this.viewHeight < 400 && this.viewWidth >= 400 ? 4 : 3; }
|
||||
@@ -215,20 +217,20 @@ export struct MobileRemoteView {
|
||||
Column({ space: 8 }) {
|
||||
if (this.menu === 'controls') {
|
||||
GridRow({ columns: this.columns(), gutter: 7 }) {
|
||||
ForEach(this.labels(), (label: string, index: number) => {
|
||||
ForEach(this.controlIds(), (index: number) => {
|
||||
GridCol() {
|
||||
Column({ space: 4 }) {
|
||||
MobileIcon({ name: this.icons()[index], iconExtent: 19 })
|
||||
Text(label).fontSize(10).fontColor(MobileTheme.ink).maxLines(1)
|
||||
Text(this.labels()[index]).fontSize(10).fontColor(MobileTheme.ink).maxLines(1)
|
||||
}
|
||||
.height(52).width('100%').justifyContent(FlexAlign.Center)
|
||||
.backgroundColor('#F0F0F2').borderRadius(10)
|
||||
.enabled(index !== 10 || this.state.ready && this.state.privacyCanToggle)
|
||||
.opacity(index === 10 && (!this.state.ready || !this.state.privacyCanToggle) ? 0.45 : 1)
|
||||
.accessibilitySelected(index === 10 && this.state.privacyEnabled)
|
||||
.accessibilityText(index === 10 ? `${label},${this.state.privacyStatus}` : label).accessibilityLevel('yes').onClick(() => this.action(index))
|
||||
.accessibilityText(index === 10 ? `${this.labels()[index]},${this.state.privacyStatus}` : this.labels()[index]).accessibilityLevel('yes').onClick(() => this.action(index))
|
||||
}
|
||||
}, (label: string) => label)
|
||||
}, (index: number) => index.toString())
|
||||
}
|
||||
if (this.state.privacyFeedback) { Text(this.state.privacyFeedback).fontSize(11).fontColor(MobileTheme.secondary).width('100%') }
|
||||
if (this.state.transfer) { Text(this.state.transfer).fontSize(11).fontColor(MobileTheme.secondary).width('100%') }
|
||||
|
||||
@@ -1125,7 +1125,7 @@ final class RemoteSessionModel: NSObject, ObservableObject, CrossDeskRTCBridgeDe
|
||||
}
|
||||
|
||||
func sendSecureAttention() {
|
||||
guard hasNetworkConsent, isConnected else { return }
|
||||
guard hasNetworkConsent, isConnected, remotePlatform == .windows else { return }
|
||||
guard remoteServiceAvailable else {
|
||||
controlStatus = "Ctrl+Alt+Del 需要远端启用 Windows 系统服务"
|
||||
return
|
||||
|
||||
@@ -1245,9 +1245,11 @@ private struct FloatingSessionMenu: View {
|
||||
FloatingControlButton(title: "网络状态", symbol: "chart.bar.xaxis") {
|
||||
page = .networkStats
|
||||
}
|
||||
FloatingControlButton(title: "Ctrl+Alt+Del",
|
||||
symbol: "lock.trianglebadge.exclamationmark",
|
||||
action: session.sendSecureAttention)
|
||||
if session.remotePlatform == .windows {
|
||||
FloatingControlButton(title: "Ctrl+Alt+Del",
|
||||
symbol: "lock.trianglebadge.exclamationmark",
|
||||
action: session.sendSecureAttention)
|
||||
}
|
||||
FloatingControlButton(title: "剪贴板", symbol: "doc.on.clipboard",
|
||||
action: session.sendClipboard)
|
||||
if session.privacyScreen.desktopAvailable {
|
||||
|
||||
Reference in New Issue
Block a user