Native video: readable tile labels (red mute, name chip) + flip camera

Label legibility: the name + mute overlays were both white/invisible.
- Mute badge is now a RED (#dc2626) circle with a white mic-slash, matching
  the web tile's .meet-mute, moved to the top-left.
- Name is now white on a dark translucent pill (PaddingLabel) so it stays
  legible over any video, bottom-left.

Front/back camera switch: new NativeCall.switchCamera() flips the local
CameraCapturer front<->back (switchCameraPosition, verified in 2.15.3). New
"Flip camera" button on the meeting bar (switchCamera icon), shown only
while a native call's camera is on (updateFlipBtn). Mirroring auto-corrects
(VideoView mirrorMode .auto only mirrors the front camera).

Needs a Codemagic build (plugin change). Web deployed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 15:58:12 +05:30
parent 34bdd8ea16
commit a5e2ed8a9d
3 changed files with 49 additions and 13 deletions
+7 -1
View File
@@ -4881,6 +4881,7 @@ function renderCall(){
+ '<span class="mic-grp"><button class="meet-ic'+(meetMic?'':' off')+'" id="meetMicBtn" title="'+(meetMic?'Mute':'Unmute')+'">'+ic(meetMic?'mic':'micOff',20)+'</button>'
+ '<button class="mic-caret" id="meetAudioBtn" title="Audio devices (mic & speaker)">'+ic('chevronUp',12)+'</button></span>'
+ '<button class="meet-ic'+(meetCam?'':' off')+'" id="meetCamBtn" title="'+(meetCam?'Turn camera off':'Turn camera on')+'">'+ic(meetCam?'video':'videoOff',20)+'</button>'
+ '<button class="meet-ic" id="meetFlipBtn" title="Flip camera" style="display:none">'+ic('switchCamera',20)+'</button>' // native video calls only (see updateFlipBtn)
// Mobile keeps only Mic / Camera / Speaker / End on the bar (#8e); everything else moves behind ⋮ More.
+ '<button class="meet-ic sec-btn" id="meetScreenBtn" title="Share screen">'+ic('monitor',20)+'</button>'
+ '<button class="meet-ic host-only sec-btn" id="meetRecBtn" title="Record meeting" style="display:none">'+ic('record',20)+'</button>'
@@ -4892,6 +4893,7 @@ function renderCall(){
+ '<button class="meet-ic leave" id="meetLeaveBtn" title="Leave">'+ic('callEnd',20)+'</button></div></div>';
document.getElementById('meetMicBtn').onclick=toggleMic;
document.getElementById('meetCamBtn').onclick=toggleCam;
{ const fb=document.getElementById('meetFlipBtn'); if(fb) fb.onclick=flipCamera; }
document.getElementById('meetScreenBtn').onclick=toggleScreen;
document.getElementById('meetRecBtn').onclick=toggleRecord;
{ const tb=document.getElementById('meetTransBtn'); if(tb) tb.onclick=toggleTranscribe; }
@@ -5473,7 +5475,11 @@ async function onMeetMsg(e){
if(m.type==='error'){ const msg=m.message; leaveMeeting(true); const e2=document.getElementById('meetErr'); if(e2) e2.textContent=msg||'Meeting error'; return; }
}
function updateMicBtn(){ const b=document.getElementById('meetMicBtn'); if(b){ b.classList.toggle('off',!meetMic); b.title=meetMic?'Mute':'Unmute'; b.innerHTML=ic(meetMic?'mic':'micOff',20); } }
function updateCamBtn(){ const b=document.getElementById('meetCamBtn'); if(b){ b.classList.toggle('off',!meetCam); b.title=meetCam?'Turn camera off':'Turn camera on'; b.innerHTML=ic(meetCam?'video':'videoOff',20); } }
function updateCamBtn(){ const b=document.getElementById('meetCamBtn'); if(b){ b.classList.toggle('off',!meetCam); b.title=meetCam?'Turn camera off':'Turn camera on'; b.innerHTML=ic(meetCam?'video':'videoOff',20); } updateFlipBtn(); }
// Flip front/back camera — native calls only (the plugin owns the camera). The button shows only while a
// native call's camera is on.
function flipCamera(){ const NC=nativeCallPlugin(); if(NC&&NC.switchCamera){ try{ NC.switchCamera(); }catch(_){} } }
function updateFlipBtn(){ const b=document.getElementById('meetFlipBtn'); if(b) b.style.display=(meetNative&&meetCam)?'inline-flex':'none'; }
// Unmute acquires the mic on demand (no prompt until then) and renegotiates with peers.
async function toggleMic(){
if(!meetLocalStream) return;