-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #24 from nhiroyasu/develop
1.6.0
- Loading branch information
Showing
31 changed files
with
954 additions
and
62 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
class AppState { | ||
static let shared = AppState() | ||
|
||
private init() {} | ||
|
||
var wallpaperKind: WallpaperKind? = nil | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import Foundation | ||
import RealmSwift | ||
|
||
// NOTE: Added at realm schema version 7. | ||
class CameraWallpaper: Object, DateSortable { | ||
@Persisted var date: Date | ||
@Persisted var deviceId: String | ||
@Persisted var size: Int | ||
|
||
convenience init( | ||
date: Date, | ||
deviceId: String, | ||
size: Int | ||
) { | ||
self.init() | ||
self.date = date | ||
self.deviceId = deviceId | ||
self.size = size | ||
} | ||
} |
186 changes: 186 additions & 0 deletions
186
wallpaper-play/Page/Setting/Contents/Camera/Base.lproj/CameraSelectionViewController.xib
Large diffs are not rendered by default.
Oops, something went wrong.
32 changes: 32 additions & 0 deletions
32
wallpaper-play/Page/Setting/Contents/Camera/CamaraSelectionCoordinator.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import Foundation | ||
import AppKit | ||
import Swinject | ||
import Injectable | ||
|
||
class CameraSelectionCoordinator: Coordinator { | ||
private var viewController: CameraSelectionViewController! | ||
private let injector: any Injectable | ||
|
||
init(injector: any Injectable) { | ||
self.injector = injector | ||
} | ||
|
||
func create() -> NSViewController { | ||
let useCase = CameraSelectionInteractor( | ||
wallpaperRequestService: injector.build() | ||
) | ||
let presenter = CameraSelectionPresenterImpl( | ||
useCase: useCase, | ||
alertManager: injector.build(), | ||
fileSelectionService: injector.build() | ||
) | ||
viewController = CameraSelectionViewController( | ||
presenter: presenter, | ||
notificationManager: injector.build(), | ||
cameraDeviceService: injector.build(), | ||
appState: injector.build() | ||
) | ||
presenter.output = viewController | ||
return viewController | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
wallpaper-play/Page/Setting/Contents/Camera/CameraSelectionPresenter.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import Foundation | ||
import AppKit | ||
import AVFoundation | ||
import Injectable | ||
|
||
protocol CameraSelectionPresenter { | ||
func didTapSetWallpaperButton(selectedCamera: AVCaptureDevice, videoSize: VideoSize) | ||
} | ||
|
||
class CameraSelectionPresenterImpl: CameraSelectionPresenter { | ||
private let useCase: any CameraSelectionUseCase | ||
private let fileSelectionService: any FileSelectionManager | ||
private let alertManager: any AlertManager | ||
weak var output: (any CameraSelectionViewOutput)! | ||
|
||
init( | ||
useCase: any CameraSelectionUseCase, | ||
alertManager: any AlertManager, | ||
fileSelectionService: any FileSelectionManager | ||
) { | ||
self.useCase = useCase | ||
self.alertManager = alertManager | ||
self.fileSelectionService = fileSelectionService | ||
} | ||
|
||
func didTapSetWallpaperButton(selectedCamera: AVCaptureDevice, videoSize: VideoSize) { | ||
useCase.requestSettingWallpaper(selectedCamera.uniqueID, videoSize: videoSize) | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
wallpaper-play/Page/Setting/Contents/Camera/CameraSelectionUseCase.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import Injectable | ||
import AVFoundation | ||
|
||
protocol CameraSelectionUseCase { | ||
func requestSettingWallpaper(_ deviceId: String, videoSize: VideoSize) | ||
} | ||
|
||
class CameraSelectionInteractor: CameraSelectionUseCase { | ||
|
||
private let wallpaperRequestService: any WallpaperRequestService | ||
|
||
init(wallpaperRequestService: any WallpaperRequestService) { | ||
self.wallpaperRequestService = wallpaperRequestService | ||
} | ||
|
||
func requestSettingWallpaper(_ deviceId: String, videoSize: VideoSize) { | ||
wallpaperRequestService.requestCameraWallpaper( | ||
camera: CameraPlayValue(deviceId: deviceId, videoSize: videoSize) | ||
) | ||
} | ||
} |
208 changes: 208 additions & 0 deletions
208
wallpaper-play/Page/Setting/Contents/Camera/CameraSelectionViewController.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,208 @@ | ||
import Cocoa | ||
import AVFoundation | ||
import Combine | ||
|
||
protocol CameraSelectionViewOutput: AnyObject { | ||
func setCamerasPopUpButton(titles: [String]) | ||
func selectCamera(selectedCamera: String) | ||
} | ||
|
||
|
||
class CameraSelectionViewController: NSViewController { | ||
@IBOutlet weak var camerasPopUpButton: NSPopUpButton! | ||
@IBOutlet weak var cameraDisplayView: NonInteractionView! | ||
@IBOutlet weak var previewAnnotationLabel: NSTextField! | ||
@IBOutlet weak var videoSizePopUpButton: NSPopUpButton! { | ||
didSet { | ||
videoSizePopUpButton.menu?.items = VideoSize.allCases.map { | ||
.init(title: $0.text, action: nil, keyEquivalent: "") | ||
} | ||
videoSizePopUpButton.selectItem(at: 0) | ||
} | ||
} | ||
private var previewLayer: AVCaptureVideoPreviewLayer! | ||
|
||
let presenter: any CameraSelectionPresenter | ||
let notificationManager: any NotificationManager | ||
let cameraDeviceService: any CameraDeviceService | ||
let appState: AppState | ||
private var captureSession: AVCaptureSession! | ||
private var cameraDevices: [AVCaptureDevice] = [] | ||
private var cancellable: Set<AnyCancellable> = [] | ||
|
||
private let captureSessionQueue = DispatchQueue(label: "com.nhiro1109.wallpaper-play.captureSessionQueue") | ||
|
||
init( | ||
presenter: any CameraSelectionPresenter, | ||
notificationManager: any NotificationManager, | ||
cameraDeviceService: any CameraDeviceService, | ||
appState: AppState | ||
) { | ||
self.presenter = presenter | ||
self.notificationManager = notificationManager | ||
self.cameraDeviceService = cameraDeviceService | ||
self.appState = appState | ||
super.init(nibName: nil, bundle: nil) | ||
} | ||
|
||
required init?(coder: NSCoder) { | ||
fatalError("init(coder:) has not been implemented") | ||
} | ||
|
||
deinit { | ||
captureSession.stopRunning() | ||
} | ||
|
||
override func viewDidLoad() { | ||
super.viewDidLoad() | ||
|
||
cameraDevices = cameraDeviceService.fetchDevices() | ||
|
||
setUpPopUpButton() | ||
setUpCameraPreview() | ||
setPreviewAnnotationLabel() | ||
observeWallpaperRequest() | ||
} | ||
|
||
override func viewDidAppear() { | ||
super.viewDidAppear() | ||
if case .camera = appState.wallpaperKind { | ||
captureSessionQueue.async { [weak self] in | ||
self?.captureSession.stopRunning() | ||
} | ||
previewAnnotationLabel.isHidden = false | ||
} else { | ||
captureSessionQueue.async { [weak self] in | ||
self?.captureSession.startRunning() | ||
} | ||
previewAnnotationLabel.isHidden = true | ||
} | ||
} | ||
|
||
override func viewDidDisappear() { | ||
super.viewDidDisappear() | ||
captureSessionQueue.async { [weak self] in | ||
self?.captureSession.stopRunning() | ||
} | ||
previewAnnotationLabel.isHidden = false | ||
} | ||
|
||
@IBAction func didSelectCamera(_ sender: Any) { | ||
if let selectedCamera = cameraDevices.first(where: { $0.localizedName == camerasPopUpButton.titleOfSelectedItem }) { | ||
changeCameraPreview(device: selectedCamera) | ||
} | ||
} | ||
|
||
@IBAction func didTapSetWallpaperButton(_ sender: Any) { | ||
if let selectedCamera = cameraDevices.first(where: { $0.localizedName == camerasPopUpButton.titleOfSelectedItem }), | ||
let videoSize = VideoSize(rawValue: videoSizePopUpButton.indexOfSelectedItem) { | ||
presenter.didTapSetWallpaperButton(selectedCamera: selectedCamera, videoSize: videoSize) | ||
} | ||
} | ||
|
||
// MARK: - Internal | ||
|
||
func setUpPopUpButton() { | ||
let titles = cameraDevices.map { $0.localizedName } | ||
camerasPopUpButton.removeAllItems() | ||
camerasPopUpButton.addItems(withTitles: titles) | ||
} | ||
|
||
func setUpCameraPreview() { | ||
guard let camera = cameraDevices.first else { return } | ||
captureSession = AVCaptureSession() | ||
|
||
do { | ||
let input = try AVCaptureDeviceInput(device: camera) | ||
if captureSession.canAddInput(input) { | ||
captureSessionQueue.async { [weak self] in | ||
self?.captureSession.addInput(input) | ||
} | ||
} | ||
|
||
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) | ||
previewLayer.videoGravity = .resizeAspectFill | ||
previewLayer.frame = cameraDisplayView.bounds | ||
previewLayer.autoresizingMask = [.layerWidthSizable, .layerHeightSizable] | ||
|
||
cameraDisplayView.layer = CALayer() | ||
cameraDisplayView.layer?.addSublayer(previewLayer) | ||
|
||
if case .camera = appState.wallpaperKind { | ||
// do nothing | ||
} else { | ||
captureSessionQueue.async { [weak self] in | ||
self?.captureSession.startRunning() | ||
} | ||
} | ||
} catch { | ||
print(error) | ||
} | ||
} | ||
|
||
func setPreviewAnnotationLabel() { | ||
if case .camera = appState.wallpaperKind { | ||
previewAnnotationLabel.isHidden = false | ||
} else { | ||
previewAnnotationLabel.isHidden = true | ||
} | ||
} | ||
|
||
func changeCameraPreview(device: AVCaptureDevice) { | ||
captureSession.inputs.forEach { captureSession.removeInput($0) } | ||
do { | ||
let input = try AVCaptureDeviceInput(device: device) | ||
if captureSession.canAddInput(input) { | ||
captureSession.addInput(input) | ||
} | ||
} catch { | ||
print(error) | ||
} | ||
} | ||
|
||
func observeWallpaperRequest() { | ||
// NOTE: When setting the camera as the wallpaper, the video stops when displaying the preview at the same time, so stop the preview when setting the camera as the wallpaper | ||
|
||
notificationManager | ||
.publisher(for: .requestCamera) | ||
.sink { [weak self] _ in | ||
self?.captureSessionQueue.async { [weak self] in | ||
self?.captureSession.stopRunning() | ||
} | ||
self?.previewAnnotationLabel.isHidden = false | ||
} | ||
.store(in: &cancellable) | ||
|
||
notificationManager | ||
.publisher(for: .requestVideo) | ||
.sink { [weak self] _ in | ||
self?.previewAnnotationLabel.isHidden = true | ||
} | ||
.store(in: &cancellable) | ||
|
||
notificationManager | ||
.publisher(for: .requestYouTube) | ||
.sink { [weak self] _ in | ||
self?.previewAnnotationLabel.isHidden = true | ||
} | ||
.store(in: &cancellable) | ||
|
||
notificationManager | ||
.publisher(for: .requestWebPage) | ||
.sink { [weak self] _ in | ||
self?.previewAnnotationLabel.isHidden = true | ||
} | ||
.store(in: &cancellable) | ||
} | ||
} | ||
|
||
extension CameraSelectionViewController: CameraSelectionViewOutput { | ||
func setCamerasPopUpButton(titles: [String]) { | ||
camerasPopUpButton.removeAllItems() | ||
camerasPopUpButton.addItems(withTitles: titles) | ||
} | ||
|
||
func selectCamera(selectedCamera: String) { | ||
camerasPopUpButton.selectItem(withTitle: selectedCamera) | ||
} | ||
} |
Oops, something went wrong.