[fix] hide privacy controls on locked desktops across clients

This commit is contained in:
dijunkun
2026-10-27 03:46:19 +08:00
parent 922cd41f70
commit 0e052e6d7a
15 changed files with 92 additions and 37 deletions
@@ -25,7 +25,7 @@ interface NativeEvent {
export interface RemoteDisplay { name: string; }
interface HostInfo { host_name: string; platform: string; app_version: string; supports_video_settings: boolean; displays: RemoteDisplay[]; }
interface SettingAck { quality: number; frame_rate: number; preference: number; request_id: number; accepted: boolean; }
interface ServiceStatus { available: boolean; }
interface ServiceStatus { available: boolean; interactive_stage?: string; }
interface ControlData { type: number; host_info?: HostInfo; video_settings?: SettingAck; service_status?: ServiceStatus; privacy_status?: PrivacyStatus; }
interface PresenceRequest { type: string; devices: string[]; subscribe: boolean; }
interface SignalType { type: string; }
@@ -80,6 +80,7 @@ export class MobileState {
privacyEnabled: boolean = false;
privacyActive: boolean = false;
privacyCanToggle: boolean = false;
privacyVisible: boolean = true;
networkTime: number = 0;
unread: number = 0;
announcements: AnnouncementItem[] = [];
@@ -361,6 +362,7 @@ export class MobileController {
const now = this.now(); this.privacyScreen.tick(now);
this.state.privacyStatus = this.privacyScreen.status(now); this.state.privacyFeedback = this.privacyScreen.feedback(now);
this.state.privacyButtonTitle = this.privacyScreen.buttonTitle(now);
this.state.privacyVisible = this.privacyScreen.desktopAvailable;
this.state.privacyEnabled = this.privacyScreen.enabled(now);
this.state.privacyActive = this.privacyScreen.active(); this.state.privacyCanToggle = this.privacyScreen.canToggle(now);
}
@@ -463,7 +465,11 @@ export class MobileController {
}
private data(message: ControlData): void {
if (message.type === 10 && message.privacy_status) { this.privacyScreen.receive(message.privacy_status, this.now()); this.syncPrivacy(); return; }
if (message.type === 5) { this.serviceAvailable = message.service_status?.available === true; return; }
if (message.type === 5) {
this.serviceAvailable = message.service_status?.available === true;
this.privacyScreen.setInteractiveStage(message.service_status?.interactive_stage ?? '');
this.syncPrivacy(); return;
}
if (message.type === 12 && message.video_settings) {
const v = message.video_settings;
this.video.receive({ quality: v.quality, frameRate: v.frame_rate, preference: v.preference }, v.request_id, v.accepted === true);
@@ -135,7 +135,11 @@ export struct MobileRemoteView {
if (index === 9) { this.state.modal = 'clipboard'; this.menu = ''; }
if (index === 10) this.controller?.togglePrivacy();
}
private labels(): string[] { return ['键盘', this.state.relative ? '相对鼠标' : '绝对鼠标', this.mouseVisible ? '收起鼠标' : '虚拟鼠标', '显示器', '画面设置', this.state.muted ? '静音' : '声音', '发送文件', '网络状态', 'Ctrl+Alt+Del', '剪贴板', this.state.privacyButtonTitle]; }
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;
}
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; }
private menuWidth(): number { return Math.max(1, Math.min(this.viewWidth - 16, this.columns() === 4 ? 380 : Math.max(280, Math.min(340, this.viewWidth - 160)))); }
@@ -5,6 +5,7 @@ export interface PrivacyStatus {
}
export class RemotePrivacyScreen {
desktopAvailable: boolean = true;
snapshot?: PrivacyStatus;
pending: boolean = false;
requestId: number = 0;
@@ -13,6 +14,10 @@ export class RemotePrivacyScreen {
private requestedAt: number = 0;
private failure: string = '';
setInteractiveStage(stage: string): void {
this.desktopAvailable = stage !== 'lock-screen' && stage !== 'credential-ui' && stage !== 'secure-desktop';
}
receive(s: PrivacyStatus, now: number): void {
if (!Number.isInteger(s.state) || s.state < 0 || s.state > 5 ||
!Number.isInteger(s.revision) || s.revision < 0 || s.revision > 0xffffffff ||
@@ -40,6 +45,7 @@ export class RemotePrivacyScreen {
return '隐私屏';
}
feedback(now: number): string {
if (!this.desktopAvailable) return '';
const s = this.snapshot;
if (!this.failure && (this.pending || this.fresh(now) && (s?.state === 2 || s?.state === 4))) return '';
if (!this.failure && !this.pending && this.fresh(now) && s?.supported &&
@@ -49,7 +55,7 @@ export class RemotePrivacyScreen {
}
canToggle(now: number): boolean {
const s = this.snapshot;
return s !== undefined && !this.pending && this.fresh(now) && s.state !== 4 && (this.active() || s.supported && s.state === 1);
return this.desktopAvailable && s !== undefined && !this.pending && this.fresh(now) && s.state !== 4 && (this.active() || s.supported && s.state === 1);
}
begin(now: number): number {
this.tick(now);