Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ private void flashPrivacyLedForPhoto() {
}

Log.d(TAG, "📸 Flashing privacy LED synchronized with shutter sound");
hardwareManager.flashRecordingLed(2200); // 300ms flash duration
hardwareManager.flashRecordingLed(1000); // 1000ms flash duration
}

/**
Expand Down Expand Up @@ -1129,9 +1129,12 @@ public void takePhotoLocally(String size, boolean enableLed) {
// TESTING: Add fake delay for camera init
PhotoCaptureTestFramework.addFakeDelay("CAMERA_INIT");

playShutterSound();
// RGB LED always flashes for photos (user visibility indicator)
triggerPhotoFlashLed();

// enableLed (from silent param) controls sound and privacy LED only
if (enableLed) {
triggerPhotoFlashLed(); // Trigger white RGB LED flash synchronized with shutter sound
playShutterSound();
flashPrivacyLedForPhoto(); // Flash privacy LED synchronized with shutter sound
}

Expand Down Expand Up @@ -1266,9 +1269,12 @@ public void takePhotoAndUpload(String photoFilePath, String requestId, String we
PhotoCaptureTestFramework.addFakeDelay("CAMERA_CAPTURE");

try {
playShutterSound();
// RGB LED always flashes for photos (user visibility indicator)
triggerPhotoFlashLed();

// enableLed (from silent param) controls sound and privacy LED only
if (enableLed) {
triggerPhotoFlashLed(); // Trigger white RGB LED flash synchronized with shutter sound
playShutterSound();
flashPrivacyLedForPhoto(); // Flash privacy LED synchronized with shutter sound
}

Expand Down Expand Up @@ -2078,9 +2084,12 @@ public void takePhotoForBleTransfer(String photoFilePath, String requestId, Stri
// TESTING: Add fake delay for camera capture
PhotoCaptureTestFramework.addFakeDelay("CAMERA_CAPTURE");

playShutterSound();
// RGB LED always flashes for photos (user visibility indicator)
triggerPhotoFlashLed();

// enableLed (from silent param) controls sound and privacy LED only
if (enableLed) {
triggerPhotoFlashLed(); // Trigger white RGB LED flash synchronized with shutter sound
playShutterSound();
flashPrivacyLedForPhoto(); // Flash privacy LED synchronized with shutter sound
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ private boolean handleTakePhoto(JSONObject data) {
boolean save = data.optBoolean("save", false);
String size = data.optString("size", "medium");
String compress = data.optString("compress", "none"); // Default to none (no compression)
boolean enableLed = data.optBoolean("enable_led", true); // Default true for phone commands
// silent: true = no sound/LED, false (default) = normal behavior with sound/LED
boolean silent = data.optBoolean("silent", false);
boolean enableLed = !silent; // Convert to internal enableLed (inverted logic)
Comment on lines 75 to +79

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Silent flag drops support for existing enable_led requests

The take_photo handler now only reads a new silent flag and sets enableLed to !silent, dropping the previous enable_led field that callers used to disable shutter sound/LED. Any client still sending {"enable_led": false} (the documented contract before this change) will now hit the default silent=false, so the privacy indicators are forced on again. This is a backward-incompatible regression for photo capture (and the same pattern was added to the video and RTMP handlers), preventing callers from suppressing sound/LED without switching to the new field.

Useful? React with 👍 / 👎.


// Generate file path using base class functionality
String fileName = generateUniqueFilename("IMG_", ".jpg");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ private boolean handleStartRtmpStream(JSONObject data) {
}

String streamId = data.optString("streamId", "");
boolean enableLed = data.optBoolean("enable_led", true); // Default true for livestreams
// silent: true = no sound/LED, false (default) = normal behavior with sound/LED
boolean silent = data.optBoolean("silent", false);
boolean enableLed = !silent; // Convert to internal enableLed (inverted logic)
RtmpStreamingService.startStreaming(context, rtmpUrl, streamId, enableLed);

// Set StateManager for battery monitoring
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,9 @@ private boolean handleStartVideoRecording(JSONObject data) {

// Start recording with settings
boolean save = data.optBoolean("save", false);
boolean enableLed = data.optBoolean("enable_led", true); // Default true for phone commands
// silent: true = no sound/LED, false (default) = normal behavior with sound/LED
boolean silent = data.optBoolean("silent", false);
boolean enableLed = !silent; // Convert to internal enableLed (inverted logic)
String requestId = data.optString("requestId", "video_" + System.currentTimeMillis());

if (videoSettings != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1027,9 +1027,9 @@ class CoreManager {
sgc?.saveBufferVideo(requestId, durationSeconds)
}

fun startVideoRecording(requestId: String, save: Boolean) {
Bridge.log("MAN: onStartVideoRecording: requestId=$requestId, save=$save")
sgc?.startVideoRecording(requestId, save)
fun startVideoRecording(requestId: String, save: Boolean, silent: Boolean) {
Bridge.log("MAN: onStartVideoRecording: requestId=$requestId, save=$save, silent=$silent")
sgc?.startVideoRecording(requestId, save, silent)
}

fun stopVideoRecording(requestId: String) {
Expand Down Expand Up @@ -1060,10 +1060,11 @@ class CoreManager {
size: String,
webhookUrl: String,
authToken: String,
compress: String
compress: String,
silent: Boolean
) {
Bridge.log("MAN: onPhotoRequest: $requestId, $appId, $size, compress=$compress")
sgc?.requestPhoto(requestId, appId, size, webhookUrl, authToken, compress)
Bridge.log("MAN: onPhotoRequest: $requestId, $appId, $size, compress=$compress, silent=$silent")
sgc?.requestPhoto(requestId, appId, size, webhookUrl, authToken, compress, silent)
}

fun rgbLedControl(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,9 @@ class CoreModule : Module() {
size: String,
webhookUrl: String,
authToken: String,
compress: String ->
coreManager?.photoRequest(requestId, appId, size, webhookUrl, authToken, compress)
compress: String,
silent: Boolean ->
coreManager?.photoRequest(requestId, appId, size, webhookUrl, authToken, compress, silent)
}

// MARK: - Video Recording Commands
Expand All @@ -94,8 +95,8 @@ class CoreModule : Module() {
coreManager?.saveBufferVideo(requestId, durationSeconds)
}

AsyncFunction("startVideoRecording") { requestId: String, save: Boolean ->
coreManager?.startVideoRecording(requestId, save)
AsyncFunction("startVideoRecording") { requestId: String, save: Boolean, silent: Boolean ->
coreManager?.startVideoRecording(requestId, save, silent)
}

AsyncFunction("stopVideoRecording") { requestId: String ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1536,7 +1536,7 @@ public void sendJson(Map<String, Object> jsonOriginal, boolean wakeUp) {
}

@Override
public void requestPhoto(String requestId, String appId, String size, String webhookUrl, String authToken, String compress) {
public void requestPhoto(String requestId, String appId, String size, String webhookUrl, String authToken, String compress, boolean silent) {

}

Expand Down Expand Up @@ -1571,7 +1571,7 @@ public void saveBufferVideo(String requestId, int durationSeconds) {
}

@Override
public void startVideoRecording(String requestId, boolean save) {
public void startVideoRecording(String requestId, boolean save, boolean silent) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public List<String> sortMicRanking(List<String> list) {
}

@Override
public void requestPhoto(@NonNull String requestId, @NonNull String appId, @NonNull String size, @Nullable String webhookUrl, @Nullable String authToken, @Nullable String compress) {
public void requestPhoto(@NonNull String requestId, @NonNull String appId, @NonNull String size, @Nullable String webhookUrl, @Nullable String authToken, @Nullable String compress, boolean silent) {

}

Expand Down Expand Up @@ -162,7 +162,7 @@ public void saveBufferVideo(@NonNull String requestId, int durationSeconds) {
}

@Override
public void startVideoRecording(@NonNull String requestId, boolean save) {
public void startVideoRecording(@NonNull String requestId, boolean save, boolean silent) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3327,8 +3327,8 @@ public boolean isMicrophoneEnabled() {
return isMicrophoneEnabled;
}

public void requestPhoto(String requestId, String appId, String size, String webhookUrl, String authToken, String compress) {
Bridge.log("LIVE: Requesting photo: " + requestId + " for app: " + appId + " with size: " + size + ", webhookUrl: " + webhookUrl + ", authToken: " + (authToken.isEmpty() ? "none" : "***") + ", compress=" + compress);
public void requestPhoto(String requestId, String appId, String size, String webhookUrl, String authToken, String compress, boolean silent) {
Bridge.log("LIVE: Requesting photo: " + requestId + " for app: " + appId + " with size: " + size + ", webhookUrl: " + webhookUrl + ", authToken: " + (authToken.isEmpty() ? "none" : "***") + ", compress=" + compress + ", silent=" + silent);

try {
JSONObject json = new JSONObject();
Expand All @@ -3349,6 +3349,8 @@ public void requestPhoto(String requestId, String appId, String size, String web
} else {
json.put("compress", "none");
}
// silent mode: disables shutter sound and privacy LED
json.put("silent", silent);

// Always generate BLE ID for potential fallback
String bleImgId = "I" + String.format("%09d", System.currentTimeMillis() % 1000000000);
Expand Down Expand Up @@ -5248,21 +5250,22 @@ public void sendButtonMaxRecordingTime() {
}

@Override
public void startVideoRecording(String requestId, boolean save) {
startVideoRecording(requestId, save, 0, 0, 0); // Use defaults
public void startVideoRecording(String requestId, boolean save, boolean silent) {
startVideoRecording(requestId, save, silent, 0, 0, 0); // Use defaults
}

/**
* Start video recording with optional resolution settings
* @param requestId Request ID for tracking
* @param save Whether to save the video
* @param silent Whether to disable sound and privacy LED
* @param width Video width (0 for default)
* @param height Video height (0 for default)
* @param fps Video frame rate (0 for default)
*/
public void startVideoRecording(String requestId, boolean save, int width, int height, int fps) {
public void startVideoRecording(String requestId, boolean save, boolean silent, int width, int height, int fps) {
Bridge.log("LIVE: Starting video recording: requestId=" + requestId + ", save=" + save +
", resolution=" + width + "x" + height + "@" + fps + "fps");
", silent=" + silent + ", resolution=" + width + "x" + height + "@" + fps + "fps");

if (!isConnected) {
Log.w(TAG, "Cannot start video recording - not connected");
Expand All @@ -5274,6 +5277,7 @@ public void startVideoRecording(String requestId, boolean save, int width, int h
json.put("type", "start_video_recording");
json.put("requestId", requestId);
json.put("save", save);
json.put("silent", silent);

// Add video settings if provided
if (width > 0 && height > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,16 @@ abstract class SGCManager {
size: String,
webhookUrl: String?,
authToken: String?,
compress: String?
compress: String?,
silent: Boolean
)
abstract fun startRtmpStream(message: MutableMap<String, Any>)
abstract fun stopRtmpStream()
abstract fun sendRtmpKeepAlive(message: MutableMap<String, Any>)
abstract fun startBufferRecording()
abstract fun stopBufferRecording()
abstract fun saveBufferVideo(requestId: String, durationSeconds: Int)
abstract fun startVideoRecording(requestId: String, save: Boolean)
abstract fun startVideoRecording(requestId: String, save: Boolean, silent: Boolean)
abstract fun stopVideoRecording(requestId: String)

// Button Settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ class Simulated : SGCManager() {
size: String,
webhookUrl: String?,
authToken: String?,
compress: String?
compress: String?,
silent: Boolean
) {
Bridge.log("requestPhoto")
Bridge.log("requestPhoto silent=$silent")
}

override fun startRtmpStream(message: MutableMap<String, Any>) {
Expand All @@ -60,8 +61,8 @@ class Simulated : SGCManager() {
Bridge.log("saveBufferVideo")
}

override fun startVideoRecording(requestId: String, save: Boolean) {
Bridge.log("startVideoRecording")
override fun startVideoRecording(requestId: String, save: Boolean, silent: Boolean) {
Bridge.log("startVideoRecording silent=$silent")
}

override fun stopVideoRecording(requestId: String) {
Expand Down
8 changes: 4 additions & 4 deletions mobile/modules/core/ios/CoreModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -110,11 +110,11 @@ public class CoreModule: Module {
AsyncFunction("photoRequest") {
(
requestId: String, appId: String, size: String, webhookUrl: String?,
authToken: String?, compress: String?
authToken: String?, compress: String?, silent: Bool
) in
await MainActor.run {
CoreManager.shared.photoRequest(
requestId, appId, size, webhookUrl, authToken, compress
requestId, appId, size, webhookUrl, authToken, compress, silent
)
}
}
Expand All @@ -139,9 +139,9 @@ public class CoreModule: Module {
}
}

AsyncFunction("startVideoRecording") { (requestId: String, save: Bool) in
AsyncFunction("startVideoRecording") { (requestId: String, save: Bool, silent: Bool) in
await MainActor.run {
CoreManager.shared.startVideoRecording(requestId, save)
CoreManager.shared.startVideoRecording(requestId, save, silent)
}
}

Expand Down
13 changes: 7 additions & 6 deletions mobile/modules/core/ios/Source/CoreManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1019,9 +1019,9 @@ struct ViewState {
sgc?.saveBufferVideo(requestId: requestId, durationSeconds: durationSeconds)
}

func startVideoRecording(_ requestId: String, _ save: Bool) {
Bridge.log("MAN: onStartVideoRecording: requestId=\(requestId), save=\(save)")
sgc?.startVideoRecording(requestId: requestId, save: save)
func startVideoRecording(_ requestId: String, _ save: Bool, _ silent: Bool) {
Bridge.log("MAN: onStartVideoRecording: requestId=\(requestId), save=\(save), silent=\(silent)")
sgc?.startVideoRecording(requestId: requestId, save: save, silent: silent)
}

func stopVideoRecording(_ requestId: String) {
Expand Down Expand Up @@ -1070,14 +1070,15 @@ struct ViewState {
_ size: String,
_ webhookUrl: String?,
_ authToken: String?,
_ compress: String?
_ compress: String?,
_ silent: Bool
) {
Bridge.log(
"MAN: onPhotoRequest: \(requestId), \(appId), \(webhookUrl), size=\(size), compress=\(compress ?? "none")"
"MAN: onPhotoRequest: \(requestId), \(appId), \(webhookUrl), size=\(size), compress=\(compress ?? "none"), silent=\(silent)"
)
sgc?.requestPhoto(
requestId, appId: appId, size: size, webhookUrl: webhookUrl, authToken: authToken,
compress: compress
compress: compress, silent: silent
)
}

Expand Down
4 changes: 2 additions & 2 deletions mobile/modules/core/ios/Source/sgcs/G1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ class G1: NSObject, SGCManager {

func requestPhoto(
_: String, appId _: String, size _: String?, webhookUrl _: String?, authToken _: String?,
compress _: String?
compress _: String?, silent _: Bool
) {}

func startRtmpStream(_: [String: Any]) {}
Expand All @@ -330,7 +330,7 @@ class G1: NSObject, SGCManager {

func saveBufferVideo(requestId _: String, durationSeconds _: Int) {}

func startVideoRecording(requestId _: String, save _: Bool) {}
func startVideoRecording(requestId _: String, save _: Bool, silent _: Bool) {}

func stopVideoRecording(requestId _: String) {}

Expand Down
4 changes: 2 additions & 2 deletions mobile/modules/core/ios/Source/sgcs/Mach1.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import UltraliteSDK

@MainActor
class Mach1: UltraliteBaseViewController, SGCManager {
func requestPhoto(_: String, appId _: String, size _: String?, webhookUrl _: String?, authToken _: String?, compress _: String?) {}
func requestPhoto(_: String, appId _: String, size _: String?, webhookUrl _: String?, authToken _: String?, compress _: String?, silent _: Bool) {}

func sendGalleryMode() {}

Expand Down Expand Up @@ -104,7 +104,7 @@ class Mach1: UltraliteBaseViewController, SGCManager {

func saveBufferVideo(requestId _: String, durationSeconds _: Int) {}

func startVideoRecording(requestId _: String, save _: Bool) {}
func startVideoRecording(requestId _: String, save _: Bool, silent _: Bool) {}

func stopVideoRecording(requestId _: String) {}

Expand Down
Loading
Loading