Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
21 changes: 21 additions & 0 deletions android/src/main/kotlin/com/ultralytics/yolo/YOLOPlatformView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,27 @@ class YOLOPlatformView(
yoloView.setShowUIControls(show)
result.success(null)
}
"toggleTorch" -> {
yoloView.toggleTorch()
result.success(null)
}
"setTorchMode" -> {
val enabled = call.argument<Boolean>("enabled")
if (enabled != null) {
yoloView.setTorchMode(enabled)
result.success(null)
} else {
result.error("invalid_args", "enabled is required", null)
}
}
"isTorchAvailable" -> {
val isAvailable = yoloView.isTorchAvailable()
result.success(isAvailable)
}
"isTorchEnabled" -> {
val isEnabled = yoloView.isTorchEnabled()
result.success(isEnabled)
}
else -> {
result.notImplemented()
}
Expand Down
57 changes: 55 additions & 2 deletions android/src/main/kotlin/com/ultralytics/yolo/YOLOView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import android.widget.Toast
import android.view.ScaleGestureDetector
import androidx.camera.core.*
import androidx.camera.core.Camera
import androidx.camera.core.TorchState
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
Expand Down Expand Up @@ -386,15 +387,67 @@ class YOLOView @JvmOverloads constructor(
camera?.let { cam: Camera ->
// Clamp zoom level between min and max
val clampedZoomRatio = zoomLevel.coerceIn(minZoomRatio, cam.cameraInfo.zoomState.value?.maxZoomRatio ?: maxZoomRatio)

cam.cameraControl.setZoomRatio(clampedZoomRatio)
currentZoomRatio = clampedZoomRatio

// Notify zoom change
onZoomChanged?.invoke(currentZoomRatio)
}
}

// region Torch/Flashlight control

/**
* Toggles the torch (flashlight) on/off.
* Only works when using the back camera and the device has a torch.
*/
fun toggleTorch() {
camera?.let { cam: Camera ->
val currentTorchState = cam.cameraInfo.torchState.value
val newState = currentTorchState != TorchState.ON
setTorchMode(newState)
}
}

/**
* Sets the torch mode to the specified state.
* @param enabled true to turn on the torch, false to turn it off.
*/
fun setTorchMode(enabled: Boolean) {
camera?.let { cam: Camera ->
try {
// Check if torch is available before trying to enable it
if (cam.cameraInfo.hasFlashUnit()) {
cam.cameraControl.enableTorch(enabled)
Log.d(TAG, "Torch mode set to: $enabled")
} else {
Log.w(TAG, "Torch not available on this camera")
}
} catch (e: Exception) {
Log.e(TAG, "Error setting torch mode", e)
}
} ?: Log.w(TAG, "Cannot set torch mode: camera is null")
}

/**
* Checks if the torch (flashlight) is available on the current camera.
* @return true if torch is available, false otherwise.
*/
fun isTorchAvailable(): Boolean {
return camera?.cameraInfo?.hasFlashUnit() ?: false
}

/**
* Gets the current torch state.
* @return true if torch is currently on, false if off.
*/
fun isTorchEnabled(): Boolean {
return camera?.cameraInfo?.torchState?.value == TorchState.ON
}

// endregion

// endregion

// region Model / Task
Expand Down
24 changes: 23 additions & 1 deletion ios/Classes/SwiftYOLOPlatformView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,29 @@ public class SwiftYOLOPlatformView: NSObject, FlutterPlatformView, FlutterStream
}
}

// Additional methods can be added here in the future
case "toggleTorch":
self.yoloView?.toggleTorch()
result(nil)

case "setTorchMode":
if let args = call.arguments as? [String: Any],
let enabled = args["enabled"] as? Bool
{
self.yoloView?.setTorchMode(enabled)
result(nil)
} else {
result(
FlutterError(
code: "invalid_args", message: "Invalid arguments for setTorchMode", details: nil))
}

case "isTorchAvailable":
let isAvailable = self.yoloView?.isTorchAvailable() ?? false
result(isAvailable)

case "isTorchEnabled":
let isEnabled = self.yoloView?.isTorchEnabled() ?? false
result(isEnabled)

default:
result(FlutterMethodNotImplemented)
Expand Down
62 changes: 62 additions & 0 deletions ios/Classes/YOLOView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,68 @@ public class YOLOView: UIView, VideoCaptureDelegate {
}
}

// MARK: - Torch/Flashlight Control

/// Toggles the torch (flashlight) on/off.
/// Only works when using the back camera and the device has a torch.
public func toggleTorch() {
guard let device = videoCapture.captureDevice else {
print("Cannot toggle torch: capture device is nil")
return
}

guard device.hasTorch else {
print("Torch is not available on this device")
return
}

let newState = device.torchMode != .on
setTorchMode(newState)
}

/// Sets the torch mode to the specified state.
/// - Parameter enabled: true to turn on the torch, false to turn it off.
public func setTorchMode(_ enabled: Bool) {
guard let device = videoCapture.captureDevice else {
print("Cannot set torch mode: capture device is nil")
return
}

guard device.hasTorch else {
print("Torch is not available on this device")
return
}

do {
try device.lockForConfiguration()
defer {
device.unlockForConfiguration()
}

if enabled {
try device.setTorchModeOn(level: AVCaptureDevice.maxAvailableTorchLevel)
print("Torch turned on")
} else {
device.torchMode = .off
print("Torch turned off")
}
} catch {
print("Failed to set torch mode: \(error.localizedDescription)")
}
}

/// Checks if the torch (flashlight) is available on the current camera.
/// - Returns: true if torch is available, false otherwise.
public func isTorchAvailable() -> Bool {
return videoCapture.captureDevice?.hasTorch ?? false
}

/// Gets the current torch state.
/// - Returns: true if torch is currently on, false if off.
public func isTorchEnabled() -> Bool {
return videoCapture.captureDevice?.torchMode == .on
}

@objc func playTapped() {
selection.selectionChanged()
self.videoCapture.start()
Expand Down
59 changes: 59 additions & 0 deletions lib/widgets/yolo_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,65 @@ class YOLOViewController {
}
}

/// Toggles the torch (flashlight) on/off.
/// Only works when the back camera is active and the device has a torch.
Future<void> toggleTorch() async {
if (_methodChannel != null) {
try {
await _methodChannel!.invokeMethod('toggleTorch');
} catch (e) {
logInfo('Error toggling torch: $e');
}
}
}

/// Sets the torch mode to the specified state.
/// [enabled] - true to turn on the torch, false to turn it off.
/// Only works when the back camera is active and the device has a torch.
Future<void> setTorchMode(bool enabled) async {
if (_methodChannel != null) {
try {
await _methodChannel!.invokeMethod('setTorchMode', {
'enabled': enabled,
});
} catch (e) {
logInfo('Error setting torch mode: $e');
}
}
}

/// Checks if the torch (flashlight) is available on the current camera.
/// Returns true if torch is available, false otherwise.
/// Returns null if the check fails or the view is not initialized.
Future<bool?> isTorchAvailable() async {
if (_methodChannel != null) {
try {
final result = await _methodChannel!.invokeMethod('isTorchAvailable');
return result as bool?;
} catch (e) {
logInfo('Error checking torch availability: $e');
return null;
}
}
return null;
}

/// Gets the current torch state.
/// Returns true if torch is currently on, false if off.
/// Returns null if the check fails or the view is not initialized.
Future<bool?> isTorchEnabled() async {
if (_methodChannel != null) {
try {
final result = await _methodChannel!.invokeMethod('isTorchEnabled');
return result as bool?;
} catch (e) {
logInfo('Error getting torch state: $e');
return null;
}
}
return null;
}

Future<void> switchModel(String modelPath, YOLOTask task) async {
if (_methodChannel != null && _viewId != null) {
await _methodChannel!.invokeMethod('setModel', {
Expand Down
Loading