diff --git a/apps/android/app/src/main/java/cn/crossdesk/mobile/MobileUi.java b/apps/android/app/src/main/java/cn/crossdesk/mobile/MobileUi.java index 671cecbe..b4a37efe 100644 --- a/apps/android/app/src/main/java/cn/crossdesk/mobile/MobileUi.java +++ b/apps/android/app/src/main/java/cn/crossdesk/mobile/MobileUi.java @@ -59,7 +59,7 @@ final class MobileUi { Icon glyph=new Icon(context,icon,INK);FrameLayout.LayoutParams p=new FrameLayout.LayoutParams(dp(22),dp(22),Gravity.CENTER);box.addView(glyph,p); box.setContentDescription(label);box.setClickable(true);box.setFocusable(true);box.setOnClickListener(v->click.run());return box; } - View icon(String name,int color) { return new Icon(context,name,color); } + Icon icon(String name,int color) { return new Icon(context,name,color); } void gap(LinearLayout parent,int height){parent.addView(new View(context),new LinearLayout.LayoutParams(1,dp(height)));} void divider(LinearLayout parent){View line=new View(context);line.setBackgroundColor(LINE);parent.addView(line,new LinearLayout.LayoutParams(-1,dp(.5f)));} LinearLayout.LayoutParams size(int width,int height){return new LinearLayout.LayoutParams(width<0?width:dp(width),height<0?height:dp(height));} @@ -93,8 +93,9 @@ final class MobileUi { } @android.annotation.SuppressLint("ViewConstructor") // Programmatic icon primitive, not inflated from XML. static final class Icon extends View { - private final Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG); private final String name; + private final Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG); private String name; Icon(Context context,String name,int color){super(context);this.name=name;paint.setColor(color);setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);} + void setName(String value){if(name.equals(value))return;name=value;invalidate();} @Override protected void onDraw(Canvas c){ c.save();c.scale(getWidth()/24f,getHeight()/24f);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(1.7f);paint.setStrokeCap(Paint.Cap.ROUND);paint.setStrokeJoin(Paint.Join.ROUND); switch(name){ @@ -136,6 +137,14 @@ final class MobileUi { case "unlock":c.drawRoundRect(5,10,19,22,2,2,paint);c.drawArc(8,2,16,15,180,150,false,paint);c.drawPoint(12,16,paint);break; case "clipboard":c.drawRoundRect(4,4,20,22,2,2,paint);c.drawRoundRect(8,2,16,7,2,2,paint);break; case "eye":path(c,2,12,6,7,12,5,18,7,22,12,18,17,12,19,6,17,2,12);c.drawCircle(12,12,3,paint);break; + case "eye_off": + Path hiddenEye=new Path(); + hiddenEye.moveTo(10,5);hiddenEye.cubicTo(15,4,19,8,22,12); + hiddenEye.quadTo(20.4f,14.7f,18,17); + hiddenEye.moveTo(6,6);hiddenEye.quadTo(3.5f,8.5f,2,12); + hiddenEye.cubicTo(7,20,13,21,18,17); + hiddenEye.moveTo(10,10);hiddenEye.cubicTo(8,13,11,16,14,14); + c.drawPath(hiddenEye,paint);path(c,3,3,21,21);break; default:c.drawCircle(12,12,9,paint);path(c,12,10,12,17);c.drawPoint(12,6,paint); }c.restore(); } diff --git a/apps/android/app/src/main/java/cn/crossdesk/mobile/SessionControls.java b/apps/android/app/src/main/java/cn/crossdesk/mobile/SessionControls.java index 1a5c667e..a54f5196 100644 --- a/apps/android/app/src/main/java/cn/crossdesk/mobile/SessionControls.java +++ b/apps/android/app/src/main/java/cn/crossdesk/mobile/SessionControls.java @@ -39,6 +39,7 @@ final class SessionControls extends FrameLayout { private TextView videoFeedback; private RemotePrivacyScreen privacy=new RemotePrivacyScreen(); private View privacyButton; + private MobileUi.Icon privacyIcon; private TextView privacyFeedback,privacyCaption; private TextView transferLabel,saveFileButton; private String transferStatus=""; @@ -116,7 +117,7 @@ final class SessionControls extends FrameLayout { if(transferLabel!=null){transferLabel.setText(transferStatus);transferLabel.setVisibility(transferStatus.isEmpty()?GONE:VISIBLE);} if(saveFileButton!=null)saveFileButton.setVisibility(hasReceivedFile?VISIBLE:GONE); } - private void closeMenu(){if(menu!=null){removeView(menu);menu=null;menuScroll=null;}if(shield!=null){removeView(shield);shield=null;}videoFeedback=transferLabel=saveFileButton=null;privacyFeedback=privacyCaption=null;privacyButton=null;trafficCells=null;trafficRows=null;networkDetails=null;encryptionLock=encryptionOpen=null;settingRows.clear();orb.setContentDescription("展开远程控制菜单");positionMouse();} + private void closeMenu(){if(menu!=null){removeView(menu);menu=null;menuScroll=null;}if(shield!=null){removeView(shield);shield=null;}videoFeedback=transferLabel=saveFileButton=null;privacyFeedback=privacyCaption=null;privacyButton=null;privacyIcon=null;trafficCells=null;trafficRows=null;networkDetails=null;encryptionLock=encryptionOpen=null;settingRows.clear();orb.setContentDescription("展开远程控制菜单");positionMouse();} private void openMenu(String page){ cancelMouseInput(); closeMenu();menuPage=page;shield=new View(getContext());shield.setOnClickListener(v->closeMenu());addView(shield,new LayoutParams(-1,-1)); @@ -133,7 +134,7 @@ final class SessionControls extends FrameLayout { } if(page.equals("controls")){ String[] labels={"键盘",mouseVisible?"收起鼠标":"虚拟鼠标",relative?"相对鼠标":"绝对鼠标","显示器","画面设置",muted?"静音":"声音","发送文件","网络状态","Ctrl+Alt+Del","剪贴板","隐私屏"}; - String[] icons={"keyboard","mouse","mouse","display","sliders","audio","folder","chart","lock","clipboard","lock"}; + String[] icons={"keyboard","mouse","mouse","display","sliders","audio","folder","chart","lock","clipboard","eye"}; 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(); java.util.List visibleControls=new java.util.ArrayList<>(); @@ -142,8 +143,8 @@ final class SessionControls extends FrameLayout { 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=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;} + for(int row=0;row<(controlCount+menuColumns-1)/menuColumns;row++){LinearLayout line=ui.row();for(int col=0;col=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));MobileUi.Icon icon=ui.icon(icons[index],INK);control.addView(icon,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;privacyIcon=icon;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 (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 icons(): string[] { return ['keyboard', 'mouse', 'mouse', 'display', 'sliders', this.state.muted ? 'muted' : 'audio', 'folder', 'chart', 'lock', 'clipboard', this.state.privacyEnabled ? 'eye_off' : 'eye']; } 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)))); } private menuX(): number { return clamp(this.orbX > this.viewWidth / 2 ? this.orbX - this.menuWidth() - 12 : this.orbX + 66, 8, this.viewWidth - this.menuWidth() - 8); } diff --git a/apps/ios/CrossDeskMobile/RemoteSessionView.swift b/apps/ios/CrossDeskMobile/RemoteSessionView.swift index 8250af3d..ee3db6f6 100644 --- a/apps/ios/CrossDeskMobile/RemoteSessionView.swift +++ b/apps/ios/CrossDeskMobile/RemoteSessionView.swift @@ -1249,7 +1249,7 @@ private struct FloatingSessionMenu: View { if session.privacyScreen.desktopAvailable { FloatingControlButton( title: session.privacyScreen.buttonTitle(at: session.privacyClock), - symbol: "eye.slash", + symbol: session.privacyScreen.enabled(at: session.privacyClock) ? "eye.slash" : "eye", selected: session.privacyScreen.enabled(at: session.privacyClock), action: session.togglePrivacyScreen )