mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge pull request #412 from Ebullioscopic/feature/camera-mirror-mode
Multiple camera choice
This commit is contained in:
commit
dbdeabdd83
5 changed files with 46 additions and 23 deletions
|
|
@ -1672,6 +1672,7 @@ struct ContentView: View {
|
|||
let horizontalPadding: CGFloat = 8
|
||||
let activationWidth = vm.closedNotchSize.width + horizontalPadding * 2
|
||||
let activationHeight = max(vm.closedNotchSize.height + zeroHeightHoverPadding, 14)
|
||||
|
||||
let activationRect = CGRect(
|
||||
x: screen.frame.midX - activationWidth / 2,
|
||||
y: screen.frame.maxY - activationHeight,
|
||||
|
|
|
|||
|
|
@ -2314,7 +2314,6 @@
|
|||
},
|
||||
"±10 Seconds" : {
|
||||
"comment" : "Display name for the \"±10 Seconds\" music skip behavior option.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -3138,7 +3137,6 @@
|
|||
},
|
||||
"Accent" : {
|
||||
"comment" : "Display name for the caps lock indicator tint mode.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -3696,7 +3694,6 @@
|
|||
},
|
||||
"Adaptive gradient" : {
|
||||
"comment" : "Display name for the timer icon color mode: adaptive gradient.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -6016,7 +6013,6 @@
|
|||
},
|
||||
"Apple Music" : {
|
||||
"comment" : "Media controller type for Apple Music",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -6920,7 +6916,6 @@
|
|||
},
|
||||
"Bar" : {
|
||||
"comment" : "Name of the timer progress style that shows a horizontal bar.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -14521,7 +14516,6 @@
|
|||
},
|
||||
"Digital" : {
|
||||
"comment" : "The display name for the \"Digital\" reminder presentation style.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -20829,7 +20823,6 @@
|
|||
},
|
||||
"Green" : {
|
||||
"comment" : "Display name for the green caps lock indicator tint mode.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -26874,6 +26867,9 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Mirror Camera" : {
|
||||
|
||||
},
|
||||
"Mirror macOS Clock timers" : {
|
||||
"comment" : "A toggle that enables or disables the app's ability to mirror macOS Clock timers.",
|
||||
|
|
@ -29941,7 +29937,6 @@
|
|||
},
|
||||
"Now Playing" : {
|
||||
"comment" : "Name of the media controller type for the \"Now Playing\" option.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -30687,7 +30682,6 @@
|
|||
},
|
||||
"Panel" : {
|
||||
"comment" : "Display name for the clipboard display mode \"Panel\".",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -34230,7 +34224,6 @@
|
|||
},
|
||||
"Ring" : {
|
||||
"comment" : "The name of the timer progress style that shows a ring shape.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -35752,7 +35745,6 @@
|
|||
},
|
||||
"Separate Tab" : {
|
||||
"comment" : "Display name for the clipboard display mode option to open the clipboard in a separate tab.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -38185,7 +38177,6 @@
|
|||
},
|
||||
"Shows screen assistant as a dropdown attached to the AI button" : {
|
||||
"comment" : "Description of the screen assistant display mode option that shows the screen assistant as a dropdown attached to the AI button.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -38210,7 +38201,6 @@
|
|||
},
|
||||
"Shows screen assistant in a floating panel near the notch" : {
|
||||
"comment" : "Description of the screen assistant display mode option that shows the screen assistant in a floating panel near the notch.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -38438,7 +38428,6 @@
|
|||
},
|
||||
"Skip forward or backward by ten seconds" : {
|
||||
"comment" : "Description of a music skip behavior option.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -39162,7 +39151,6 @@
|
|||
},
|
||||
"Spotify" : {
|
||||
"comment" : "Spotify media controller type.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -39399,7 +39387,6 @@
|
|||
},
|
||||
"Standard previous/next track controls" : {
|
||||
"comment" : "Description of the music skip behavior option to use standard previous/next track controls.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -42227,7 +42214,6 @@
|
|||
},
|
||||
"Track Skip" : {
|
||||
"comment" : "Display name for the \"Track Skip\" music skip behavior.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
@ -45585,7 +45571,6 @@
|
|||
},
|
||||
"Youtube Music" : {
|
||||
"comment" : "Spotify music service.",
|
||||
"extractionState" : "stale",
|
||||
"isCommentAutoGenerated" : true,
|
||||
"localizations" : {
|
||||
"ko" : {
|
||||
|
|
|
|||
|
|
@ -3830,7 +3830,9 @@ struct LiveActivitiesSettings: View {
|
|||
|
||||
struct Appearance: View {
|
||||
@ObservedObject var coordinator = DynamicIslandViewCoordinator.shared
|
||||
@ObservedObject var webcamManager = WebcamManager.shared
|
||||
@Default(.mirrorShape) var mirrorShape
|
||||
@Default(.selectedCameraID) var selectedCameraID
|
||||
@Default(.sliderColor) var sliderColor
|
||||
@Default(.useMusicVisualizer) var useMusicVisualizer
|
||||
@Default(.customVisualizers) var customVisualizers
|
||||
|
|
@ -4237,6 +4239,22 @@ struct Appearance: View {
|
|||
.tag(MirrorShapeEnum.rectangle)
|
||||
}
|
||||
.settingsHighlight(id: highlightID("Mirror shape"))
|
||||
|
||||
if webcamManager.cameraAvailable {
|
||||
Picker("Mirror Camera", selection: $selectedCameraID) {
|
||||
ForEach(webcamManager.availableCameras, id: \.uniqueID) { device in
|
||||
Text(device.localizedName)
|
||||
.tag(device.uniqueID)
|
||||
}
|
||||
}
|
||||
.onChange(of: selectedCameraID) { _, _ in
|
||||
if Defaults[.showMirror] {
|
||||
webcamManager.stopSession()
|
||||
webcamManager.startSession()
|
||||
}
|
||||
}
|
||||
.settingsHighlight(id: highlightID("Mirror Camera"))
|
||||
}
|
||||
Defaults.Toggle(key: .showNotHumanFace) {
|
||||
Text("Idle Animation")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
import AVFoundation
|
||||
import SwiftUI
|
||||
import Defaults
|
||||
|
||||
class WebcamManager: NSObject, ObservableObject {
|
||||
static let shared = WebcamManager()
|
||||
|
|
@ -51,6 +52,12 @@ class WebcamManager: NSObject, ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
@Published var availableCameras: [AVCaptureDevice] = [] {
|
||||
didSet {
|
||||
objectWillChange.send()
|
||||
}
|
||||
}
|
||||
|
||||
private let sessionQueue = DispatchQueue(label: "DynamicIsland.WebcamManager.SessionQueue", qos: .userInitiated)
|
||||
|
||||
private var isCleaningUp: Bool = false
|
||||
|
|
@ -132,7 +139,7 @@ class WebcamManager: NSObject, ObservableObject {
|
|||
/// Checks if any camera devices are available and sets up capture session if needed
|
||||
func checkCameraAvailability() {
|
||||
let availableDevices = AVCaptureDevice.DiscoverySession(
|
||||
deviceTypes: [.external, .builtInWideAngleCamera],
|
||||
deviceTypes: [.external, .builtInWideAngleCamera, .deskViewCamera, .externalUnknown],
|
||||
mediaType: .video,
|
||||
position: .unspecified
|
||||
).devices
|
||||
|
|
@ -140,6 +147,7 @@ class WebcamManager: NSObject, ObservableObject {
|
|||
let hasAvailableDevices = !availableDevices.isEmpty
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.availableCameras = availableDevices
|
||||
self.cameraAvailable = hasAvailableDevices
|
||||
}
|
||||
}
|
||||
|
|
@ -158,14 +166,24 @@ class WebcamManager: NSObject, ObservableObject {
|
|||
let session = AVCaptureSession()
|
||||
|
||||
do {
|
||||
// Get available devices and prefer external camera if available
|
||||
// Get available devices
|
||||
let discoverySession = AVCaptureDevice.DiscoverySession(
|
||||
deviceTypes: [.external, .builtInWideAngleCamera],
|
||||
deviceTypes: [.external, .builtInWideAngleCamera, .deskViewCamera, .externalUnknown],
|
||||
mediaType: .video,
|
||||
position: .unspecified
|
||||
)
|
||||
|
||||
guard let videoDevice = discoverySession.devices.first else {
|
||||
let devices = discoverySession.devices
|
||||
let selectedID = Defaults[.selectedCameraID]
|
||||
|
||||
let videoDevice: AVCaptureDevice?
|
||||
if !selectedID.isEmpty {
|
||||
videoDevice = devices.first(where: { $0.uniqueID == selectedID }) ?? devices.first
|
||||
} else {
|
||||
videoDevice = devices.first
|
||||
}
|
||||
|
||||
guard let videoDevice = videoDevice else {
|
||||
NSLog("No video devices available")
|
||||
DispatchQueue.main.async {
|
||||
self.isSessionRunning = false
|
||||
|
|
@ -175,7 +193,7 @@ class WebcamManager: NSObject, ObservableObject {
|
|||
return
|
||||
}
|
||||
|
||||
NSLog("Using camera: \(videoDevice.localizedName)")
|
||||
NSLog("Using camera: \(videoDevice.localizedName) (ID: \(videoDevice.uniqueID))")
|
||||
|
||||
// Lock device for configuration
|
||||
try videoDevice.lockForConfiguration()
|
||||
|
|
|
|||
|
|
@ -794,6 +794,7 @@ extension Defaults.Keys {
|
|||
//static let alwaysShowTabs = Key<Bool>("alwaysShowTabs", default: true)
|
||||
static let showMirror = Key<Bool>("showMirror", default: false)
|
||||
static let mirrorShape = Key<MirrorShapeEnum>("mirrorShape", default: MirrorShapeEnum.rectangle)
|
||||
static let selectedCameraID = Key<String>("selectedCameraID", default: "")
|
||||
static let settingsIconInNotch = Key<Bool>("settingsIconInNotch", default: true)
|
||||
static let lightingEffect = Key<Bool>("lightingEffect", default: true)
|
||||
static let accentColor = Key<Color>("accentColor", default: Color.blue)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue