@neoskola/auto-play 0.3.13 → 0.3.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -188,13 +188,6 @@ class HybridAutoPlay: HybridAutoPlaySpec {
188
188
  )
189
189
  }
190
190
 
191
- // NowPlayingTemplate: inject custom UIKit view into CPWindow
192
- if let nowPlaying = template as? NowPlayingTemplate {
193
- try await MainActor.run {
194
- try nowPlaying.injectCustomView()
195
- }
196
- }
197
-
198
191
  if let autoDismissMs = TemplateStore.getTemplate(
199
192
  templateId: templateId
200
193
  )?.autoDismissMs {
@@ -2,18 +2,13 @@ import CarPlay
2
2
  import MediaPlayer
3
3
  import AVFoundation
4
4
 
5
- class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
6
- var template: CPMapTemplate
5
+ class NowPlayingTemplate: NSObject, AutoPlayTemplate {
7
6
  var config: NowPlayingTemplateConfig
8
7
  private var loadedImage: UIImage?
9
8
  private var isSetupComplete = false
10
9
  private var currentElapsedTime: Double = 0
11
10
  private var currentDuration: Double = 0
12
11
 
13
- // Custom UIKit view
14
- private var customViewController: NeoSkolaNowPlayingViewController?
15
- private var previousRootVC: UIViewController?
16
-
17
12
  // Native audio player
18
13
  private var player: AVPlayer?
19
14
  private var playerItem: AVPlayerItem?
@@ -32,17 +27,16 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
32
27
  }
33
28
 
34
29
  func getTemplate() -> CPTemplate {
35
- return template
30
+ return CPNowPlayingTemplate.shared
36
31
  }
37
32
 
38
33
  init(config: NowPlayingTemplateConfig) {
39
34
  self.config = config
40
- template = CPMapTemplate(id: config.id)
41
35
 
42
36
  super.init()
43
37
 
44
- template.mapDelegate = self
45
- template.mapButtons = Self.buildMapButtons(isPlaying: config.isPlaying, owner: self)
38
+ // Set the config ID on the shared singleton so TemplateStore can find it
39
+ initTemplate(template: CPNowPlayingTemplate.shared, id: config.id)
46
40
 
47
41
  DispatchQueue.main.async { [weak self] in
48
42
  guard let self = self else { return }
@@ -57,97 +51,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
57
51
  }
58
52
  }
59
53
 
60
- // MARK: - CPMapButton Controls (transparent touch handlers)
61
-
62
- private static func buildMapButtons(isPlaying: Bool, owner: NowPlayingTemplate) -> [CPMapButton] {
63
- // 1x1 transparent image for invisible buttons
64
- let renderer = UIGraphicsImageRenderer(size: CGSize(width: 1, height: 1))
65
- let clearImage = renderer.image { ctx in
66
- UIColor.clear.setFill()
67
- ctx.fill(CGRect(x: 0, y: 0, width: 1, height: 1))
68
- }
69
-
70
- // Previous track button
71
- let prevButton = CPMapButton(image: clearImage) { [weak owner] _ in
72
- owner?.config.onPreviousTrack?()
73
- }
74
-
75
- // Play/Pause button
76
- let playPauseButton = CPMapButton(image: clearImage) { [weak owner] _ in
77
- guard let owner = owner else { return }
78
- DispatchQueue.main.async {
79
- if owner.config.isPlaying {
80
- owner.pauseAudio()
81
- owner.config.onPause?()
82
- } else {
83
- owner.resumeAudio()
84
- owner.config.onPlay?()
85
- }
86
- }
87
- }
88
-
89
- // Next track button
90
- let nextButton = CPMapButton(image: clearImage) { [weak owner] _ in
91
- owner?.config.onNextTrack?()
92
- }
93
-
94
- return [prevButton, playPauseButton, nextButton]
95
- }
96
-
97
- private func updateMapButtons() {
98
- template.mapButtons = Self.buildMapButtons(isPlaying: config.isPlaying, owner: self)
99
- }
100
-
101
- // MARK: - Custom View Injection
102
-
103
- @MainActor
104
- func injectCustomView() throws {
105
- guard let scene = SceneStore.getRootScene(),
106
- let window = scene.window else {
107
- throw AutoPlayError.noUiWindow("NowPlaying: window nil")
108
- }
109
-
110
- previousRootVC = window.rootViewController
111
-
112
- let customVC = NeoSkolaNowPlayingViewController()
113
-
114
- let titleText = Parser.parseText(text: config.title) ?? "Now Playing"
115
- let subtitleText = config.subtitle.flatMap { Parser.parseText(text: $0) } ?? ""
116
- customVC.updateInfo(courseName: subtitleText, lessonName: titleText)
117
- customVC.updatePlaybackState(isPlaying: config.isPlaying)
118
-
119
- window.rootViewController = customVC
120
- window.makeKeyAndVisible()
121
-
122
- self.customViewController = customVC
123
- }
124
-
125
- @MainActor
126
- private func restoreOriginalView() {
127
- guard let scene = SceneStore.getRootScene(),
128
- let window = scene.window else { return }
129
-
130
- if let previousRootVC = previousRootVC {
131
- window.rootViewController = previousRootVC
132
- window.makeKeyAndVisible()
133
- }
134
- self.previousRootVC = nil
135
- self.customViewController = nil
136
- }
137
-
138
- // MARK: - Player UI
139
-
140
- private func updatePlayerUI() {
141
- guard let customVC = customViewController else { return }
142
-
143
- let titleText = Parser.parseText(text: config.title) ?? ""
144
- let subtitleText = config.subtitle.flatMap { Parser.parseText(text: $0) } ?? ""
145
- customVC.updateInfo(courseName: subtitleText, lessonName: titleText)
146
- customVC.updatePlaybackState(isPlaying: config.isPlaying)
147
- customVC.updateTime(elapsed: currentElapsedTime, duration: currentDuration)
148
- updateMapButtons()
149
- }
150
-
151
54
  // MARK: - Native Audio Playback
152
55
 
153
56
  @MainActor
@@ -165,7 +68,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
165
68
  NowPlayingSessionManager.shared.ensureSessionActive()
166
69
  config.isPlaying = true
167
70
  updateNowPlayingInfo()
168
- updatePlayerUI()
169
71
  MPNowPlayingInfoCenter.default().playbackState = .playing
170
72
 
171
73
  print("[NowPlayingTemplate] Downloading audio: \(url)")
@@ -224,7 +126,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
224
126
  if !duration.isNaN && !duration.isInfinite && duration > 0 {
225
127
  self.currentDuration = duration
226
128
  self.updateNowPlayingInfo()
227
- self.updatePlayerUI()
228
129
  print("[NowPlayingTemplate] Duration resolved via KVO: \(duration)s")
229
130
  }
230
131
  }
@@ -294,8 +195,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
294
195
  nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
295
196
  MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
296
197
 
297
- updatePlayerUI()
298
-
299
198
  // 95% completion check
300
199
  if !completionFired && currentDuration > 0 && currentTime / currentDuration >= 0.95 {
301
200
  completionFired = true
@@ -313,7 +212,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
313
212
  private func handlePlaybackFinished() {
314
213
  config.isPlaying = false
315
214
  MPNowPlayingInfoCenter.default().playbackState = .stopped
316
- updatePlayerUI()
317
215
  if !completionFired {
318
216
  completionFired = true
319
217
  config.onComplete?()
@@ -326,7 +224,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
326
224
  player?.pause()
327
225
  config.isPlaying = false
328
226
  updatePlaybackState(isPlaying: false)
329
- updatePlayerUI()
330
227
  reportProgress()
331
228
  }
332
229
 
@@ -336,7 +233,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
336
233
  player?.play()
337
234
  config.isPlaying = true
338
235
  updatePlaybackState(isPlaying: true)
339
- updatePlayerUI()
340
236
  }
341
237
 
342
238
  @MainActor
@@ -361,7 +257,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
361
257
  cleanupPlayer()
362
258
  config.isPlaying = false
363
259
  MPNowPlayingInfoCenter.default().playbackState = .stopped
364
- updatePlayerUI()
365
260
  }
366
261
 
367
262
  private func cleanupPlayer() {
@@ -478,7 +373,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
478
373
  self.player?.seek(to: time)
479
374
  self.currentElapsedTime = positionEvent.positionTime
480
375
  self.updateNowPlayingInfo()
481
- self.updatePlayerUI()
482
376
  return .success
483
377
  }
484
378
  }
@@ -503,7 +397,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
503
397
 
504
398
  @MainActor
505
399
  func invalidate() {
506
- updatePlayerUI()
507
400
  updateNowPlayingInfo()
508
401
 
509
402
  if loadedImage == nil, let image = config.image {
@@ -530,7 +423,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
530
423
  func onPopped() {
531
424
  config.onPopped?()
532
425
  cleanupPlayer()
533
- restoreOriginalView()
534
426
 
535
427
  let commandCenter = MPRemoteCommandCenter.shared()
536
428
  commandCenter.playCommand.removeTarget(nil)
@@ -551,7 +443,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
551
443
  NowPlayingSessionManager.shared.ensureSessionActive()
552
444
 
553
445
  if !isSetupComplete {
554
- updatePlayerUI()
555
446
  updateNowPlayingInfo()
556
447
  isSetupComplete = true
557
448
  }
@@ -578,7 +469,6 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
578
469
  config.title = AutoText(text: title, distance: nil, duration: nil)
579
470
  config.subtitle = AutoText(text: subtitle, distance: nil, duration: nil)
580
471
  updateNowPlayingInfo()
581
- updatePlayerUI()
582
472
  }
583
473
 
584
474
  @MainActor
@@ -600,11 +490,5 @@ class NowPlayingTemplate: NSObject, AutoPlayTemplate, CPMapTemplateDelegate {
600
490
  nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = elapsedTime
601
491
  nowPlayingInfo[MPNowPlayingInfoPropertyPlaybackRate] = config.isPlaying ? 1.0 : 0.0
602
492
  MPNowPlayingInfoCenter.default().nowPlayingInfo = nowPlayingInfo
603
-
604
- updatePlayerUI()
605
493
  }
606
-
607
- // MARK: - CPMapTemplateDelegate
608
-
609
- func mapTemplate(_ mapTemplate: CPMapTemplate, panWith direction: CPMapTemplate.PanDirection) {}
610
494
  }
@@ -12,12 +12,6 @@ class NeoSkolaNowPlayingViewController: UIViewController {
12
12
  private let durationLabel = UILabel()
13
13
  private let timeStack = UIStackView()
14
14
 
15
- // MARK: - Button Controls (visual only, touch handled by CPMapButton)
16
- private let buttonStack = UIStackView()
17
- private let previousButton = UIButton(type: .custom)
18
- private let playPauseButton = UIButton(type: .custom)
19
- private let nextButton = UIButton(type: .custom)
20
-
21
15
  // MARK: - Lifecycle
22
16
 
23
17
  override func viewDidLoad() {
@@ -72,45 +66,6 @@ class NeoSkolaNowPlayingViewController: UIViewController {
72
66
  timeStack.addArrangedSubview(currentTimeLabel)
73
67
  timeStack.addArrangedSubview(durationLabel)
74
68
 
75
- // Previous Button
76
- previousButton.setImage(
77
- UIImage(systemName: "backward.end.fill",
78
- withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .medium))?
79
- .withTintColor(.white, renderingMode: .alwaysOriginal),
80
- for: .normal
81
- )
82
- previousButton.backgroundColor = .clear
83
- previousButton.isUserInteractionEnabled = false
84
-
85
- // Play/Pause Button
86
- playPauseButton.setImage(
87
- UIImage(systemName: "play.circle.fill",
88
- withConfiguration: UIImage.SymbolConfiguration(pointSize: 44, weight: .medium))?
89
- .withTintColor(.white, renderingMode: .alwaysOriginal),
90
- for: .normal
91
- )
92
- playPauseButton.backgroundColor = .clear
93
- playPauseButton.isUserInteractionEnabled = false
94
-
95
- // Next Button
96
- nextButton.setImage(
97
- UIImage(systemName: "forward.end.fill",
98
- withConfiguration: UIImage.SymbolConfiguration(pointSize: 28, weight: .medium))?
99
- .withTintColor(.white, renderingMode: .alwaysOriginal),
100
- for: .normal
101
- )
102
- nextButton.backgroundColor = .clear
103
- nextButton.isUserInteractionEnabled = false
104
-
105
- // Button Stack (horizontal)
106
- buttonStack.axis = .horizontal
107
- buttonStack.alignment = .center
108
- buttonStack.distribution = .equalSpacing
109
- buttonStack.spacing = 40
110
- buttonStack.addArrangedSubview(previousButton)
111
- buttonStack.addArrangedSubview(playPauseButton)
112
- buttonStack.addArrangedSubview(nextButton)
113
-
114
69
  // Main container stack (vertical)
115
70
  containerStack.axis = .vertical
116
71
  containerStack.alignment = .fill
@@ -137,14 +92,6 @@ class NeoSkolaNowPlayingViewController: UIViewController {
137
92
  containerStack.addArrangedSubview(progressView)
138
93
  containerStack.addArrangedSubview(timeStack)
139
94
 
140
- // Spacer between time and buttons
141
- let spacer3 = UIView()
142
- spacer3.translatesAutoresizingMaskIntoConstraints = false
143
- spacer3.heightAnchor.constraint(equalToConstant: 16).isActive = true
144
- containerStack.addArrangedSubview(spacer3)
145
-
146
- containerStack.addArrangedSubview(buttonStack)
147
-
148
95
  view.addSubview(containerStack)
149
96
  }
150
97
 
@@ -170,14 +117,6 @@ class NeoSkolaNowPlayingViewController: UIViewController {
170
117
  func updatePlaybackState(isPlaying: Bool) {
171
118
  statusLabel.text = isPlaying ? "OYNATILIYOR" : "DURAKLATILDI"
172
119
  statusLabel.textColor = isPlaying ? UIColor.systemGreen : UIColor.systemGray
173
-
174
- let iconName = isPlaying ? "pause.circle.fill" : "play.circle.fill"
175
- playPauseButton.setImage(
176
- UIImage(systemName: iconName,
177
- withConfiguration: UIImage.SymbolConfiguration(pointSize: 44, weight: .medium))?
178
- .withTintColor(.white, renderingMode: .alwaysOriginal),
179
- for: .normal
180
- )
181
120
  }
182
121
 
183
122
  func updateTime(elapsed: Double, duration: Double) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neoskola/auto-play",
3
- "version": "0.3.13",
3
+ "version": "0.3.15",
4
4
  "description": "Android Auto and Apple CarPlay for react-native",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",