forked from videolan/vlc-ios
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathVLCRendererDiscovererManager.swift
264 lines (231 loc) · 10.5 KB
/
VLCRendererDiscovererManager.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*****************************************************************************
* VLCRendererDiscovererManager.swift
*
* Copyright © 2018 VLC authors and VideoLAN
* Copyright © 2018 Videolabs
*
* Authors: Soomin Lee <[email protected]>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
@objc protocol VLCRendererDiscovererManagerDelegate {
@objc optional func removedCurrentRendererItem(_ item: VLCRendererItem)
}
class VLCRendererDiscovererManager: NSObject {
// Array of RendererDiscoverers(Chromecast, UPnP, ...)
@objc var discoverers: [VLCRendererDiscoverer] = [VLCRendererDiscoverer]()
@objc weak var delegate: VLCRendererDiscovererManagerDelegate?
@objc lazy var actionSheet: ActionSheet = {
let actionSheet = ActionSheet()
actionSheet.delegate = self
actionSheet.dataSource = self
actionSheet.modalPresentationStyle = .custom
actionSheet.setAction { [weak self] (item) in
if let rendererItem = item as? VLCRendererItem {
self?.setRendererItem(rendererItem: rendererItem)
}
}
return actionSheet
}()
@objc var presentingViewController: UIViewController?
@objc var rendererButtons: [UIButton] = [UIButton]()
@objc init(presentingViewController: UIViewController?) {
self.presentingViewController = presentingViewController
super.init()
}
// Returns renderers of *all* discoverers
@objc func getAllRenderers() -> [VLCRendererItem] {
return discoverers.flatMap { $0.renderers }
}
fileprivate func isDuplicateDiscoverer(with description: VLCRendererDiscovererDescription) -> Bool {
for discoverer in discoverers where discoverer.name == description.name {
return true
}
return false
}
@objc func start() {
// Gather potential renderer discoverers
guard let tmpDiscoverersDescription: [VLCRendererDiscovererDescription] = VLCRendererDiscoverer.list() else {
print("VLCRendererDiscovererManager: Unable to retrieve list of VLCRendererDiscovererDescription")
return
}
for discovererDescription in tmpDiscoverersDescription where !isDuplicateDiscoverer(with: discovererDescription) {
guard let rendererDiscoverer = VLCRendererDiscoverer(name: discovererDescription.name) else {
print("VLCRendererDiscovererManager: Unable to instanciate renderer discoverer with name: \(discovererDescription.name)")
continue
}
guard rendererDiscoverer.start() else {
print("VLCRendererDiscovererManager: Unable to start renderer discoverer with name: \(rendererDiscoverer.name)")
continue
}
rendererDiscoverer.delegate = self
discoverers.append(rendererDiscoverer)
}
}
@objc func stop() {
for discoverer in discoverers {
discoverer.stop()
}
discoverers.removeAll()
}
// MARK: VLCActionSheet
@objc fileprivate func displayActionSheet() {
guard let presentingViewController = presentingViewController else {
assertionFailure("VLCRendererDiscovererManager: Cannot display actionSheet, no viewController setted")
return
}
// If only one renderer, choose it automatically
if getAllRenderers().count == 1, let rendererItem = getAllRenderers().first {
let indexPath = IndexPath(row: 0, section: 0)
actionSheet.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
actionSheet(collectionView: actionSheet.collectionView, cellForItemAt: indexPath)
actionSheet.action?(rendererItem)
} else {
presentingViewController.present(actionSheet, animated: false, completion: nil)
}
}
fileprivate func setRendererItem(rendererItem: VLCRendererItem) {
let vpcRenderer = VLCPlaybackController.sharedInstance().renderer
var finalRendererItem: VLCRendererItem?
var isSelected: Bool = false
if vpcRenderer != rendererItem {
finalRendererItem = rendererItem
isSelected = true
}
VLCPlaybackController.sharedInstance().renderer = finalRendererItem
for button in rendererButtons {
button.isSelected = isSelected
}
}
@objc func addSelectionHandler(_ selectionHandler: ((_ rendererItem: VLCRendererItem?) -> Void)?) {
actionSheet.setAction { [weak self] (item) in
if let rendererItem = item as? VLCRendererItem {
//if we select the same renderer we want to disconnect
let oldRenderer = VLCPlaybackController.sharedInstance().renderer
self?.setRendererItem(rendererItem: rendererItem)
if let handler = selectionHandler {
handler(oldRenderer == rendererItem ? nil : rendererItem)
}
}
}
}
/// Add the given button to VLCRendererDiscovererManager.
/// The button state will be handled by the manager.
///
/// - Returns: New `UIButton`
@objc func setupRendererButton() -> UIButton {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
button.isHidden = getAllRenderers().isEmpty
button.tintColor = PresentationTheme.current.colors.orangeUI
button.setImage(UIImage(named: "renderer"), for: .normal)
button.setImage(UIImage(named: "rendererFull"), for: .selected)
button.addTarget(self, action: #selector(displayActionSheet), for: .touchUpInside)
button.accessibilityLabel = NSLocalizedString("BUTTON_RENDERER", comment: "")
button.accessibilityHint = NSLocalizedString("BUTTON_RENDERER_HINT", comment: "")
rendererButtons.append(button)
return button
}
}
// MARK: VLCRendererDiscovererDelegate
extension VLCRendererDiscovererManager: VLCRendererDiscovererDelegate {
func rendererDiscovererItemAdded(_ rendererDiscoverer: VLCRendererDiscoverer, item: VLCRendererItem) {
for button in rendererButtons {
UIView.animate(withDuration: 0.1) {
button.isHidden = false
}
}
if actionSheet.viewIfLoaded?.window != nil {
actionSheet.collectionView.reloadData()
actionSheet.updateViewConstraints()
}
}
func rendererDiscovererItemDeleted(_ rendererDiscoverer: VLCRendererDiscoverer, item: VLCRendererItem) {
let playbackController = VLCPlaybackController.sharedInstance()
// Current renderer has been removed
if playbackController.renderer == item {
playbackController.renderer = nil
delegate?.removedCurrentRendererItem?(item)
// Reset buttons state
for button in rendererButtons {
button.isSelected = false
}
}
if actionSheet.viewIfLoaded?.window != nil {
actionSheet.collectionView.reloadData()
actionSheet.updateViewConstraints()
}
// No more renderers to show
if getAllRenderers().isEmpty {
for button in rendererButtons {
UIView.animate(withDuration: 0.1) {
button.isHidden = true
}
}
actionSheet.removeActionSheet()
}
}
fileprivate func updateCollectionViewCellApparence(cell: ActionSheetCell, highlighted: Bool) {
var image = UIImage(named: "renderer")
var textColor = PresentationTheme.current.colors.cellTextColor
var tintColor = PresentationTheme.current.colors.cellDetailTextColor
if highlighted {
image = UIImage(named: "rendererFull")
textColor = PresentationTheme.current.colors.orangeUI
tintColor = PresentationTheme.current.colors.orangeUI
}
cell.tintColor = tintColor
cell.icon.image = image
cell.name.textColor = textColor
}
}
// MARK: VLCActionSheetDelegate
extension VLCRendererDiscovererManager: ActionSheetDelegate {
func headerViewTitle() -> String? {
return NSLocalizedString("HEADER_TITLE_RENDERER", comment: "")
}
func itemAtIndexPath(_ indexPath: IndexPath) -> Any? {
let renderers = getAllRenderers()
if indexPath.row < renderers.count {
return renderers[indexPath.row]
}
assertionFailure("VLCRendererDiscovererManager: VLCActionSheetDelegate: IndexPath out of range")
return nil
}
func actionSheet(collectionView: UICollectionView, didSelectItem item: Any, At indexPath: IndexPath) {
guard let renderer = item as? VLCRendererItem,
let cell = collectionView.cellForItem(at: indexPath) as? ActionSheetCell else {
assertionFailure("VLCRendererDiscovererManager: VLCActionSheetDelegate: Cell is not a VLCActionSheetCell")
return
}
let isCurrentlySelectedRenderer = renderer == VLCPlaybackController.sharedInstance().renderer
if !isCurrentlySelectedRenderer {
collectionView.reloadData()
} else {
delegate?.removedCurrentRendererItem?(renderer)
}
updateCollectionViewCellApparence(cell: cell, highlighted: isCurrentlySelectedRenderer)
}
}
// MARK: VLCActionSheetDataSource
extension VLCRendererDiscovererManager: ActionSheetDataSource {
func numberOfRows() -> Int {
return getAllRenderers().count
}
@discardableResult
func actionSheet(collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
guard let cell = collectionView.dequeueReusableCell(
withReuseIdentifier: ActionSheetCell.identifier, for: indexPath) as? ActionSheetCell else {
assertionFailure("VLCRendererDiscovererManager: VLCActionSheetDataSource: Unable to dequeue reusable cell")
return UICollectionViewCell()
}
let renderers = getAllRenderers()
if indexPath.row < renderers.count {
cell.name.text = renderers[indexPath.row].name
let isSelectedRenderer = renderers[indexPath.row] == VLCPlaybackController.sharedInstance().renderer ? true : false
updateCollectionViewCellApparence(cell: cell, highlighted: isSelectedRenderer)
} else {
assertionFailure("VLCRendererDiscovererManager: VLCActionSheetDataSource: IndexPath out of range")
}
return cell
}
}