@capgo/capacitor-video-player 8.1.9 → 8.1.11

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.
@@ -58,7 +58,7 @@ dependencies {
58
58
  implementation 'androidx.gridlayout:gridlayout:1.1.0'
59
59
  implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
60
60
  implementation 'com.google.android.exoplayer:exoplayer-core:2.19.1'
61
- implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.0'
61
+ implementation 'com.google.android.exoplayer:exoplayer-ui:2.19.1'
62
62
  implementation 'com.google.android.exoplayer:exoplayer-hls:2.19.1'
63
63
  implementation 'com.google.android.exoplayer:exoplayer-dash:2.19.1'
64
64
  implementation 'com.google.android.exoplayer:exoplayer-smoothstreaming:2.19.1'
@@ -38,7 +38,7 @@ import java.util.Map;
38
38
  )
39
39
  public class VideoPlayerPlugin extends Plugin {
40
40
 
41
- private final String pluginVersion = "8.1.9";
41
+ private final String pluginVersion = "8.1.11";
42
42
 
43
43
  // Permission alias constants
44
44
  private static final String PERMISSION_DENIED_ERROR = "Unable to access media videos, user denied permission request";
@@ -24,6 +24,7 @@ final class VideoPlayerCastController: NSObject {
24
24
  private weak var player: AVPlayer?
25
25
  private weak var playerViewController: AVPlayerViewController?
26
26
  private weak var castButton: GCKUICastButton?
27
+ private weak var castIndicatorLabel: UILabel?
27
28
  private weak var observedRemoteMediaClient: GCKRemoteMediaClient?
28
29
  private var mediaLoadRequest: GCKRequest?
29
30
  private var pendingCastCommands: [PendingCastCommand] = []
@@ -36,6 +37,9 @@ final class VideoPlayerCastController: NSObject {
36
37
  private var onPlay: (() -> Void)?
37
38
  private var onPause: (() -> Void)?
38
39
  private var onEnd: (() -> Void)?
40
+ private var controlsHideTimer: Timer?
41
+ private weak var tapGestureRecognizer: UITapGestureRecognizer?
42
+ private static let overlayAutoHideDuration: TimeInterval = 3.0
39
43
 
40
44
  var isCasting: Bool {
41
45
  return remoteMediaClient != nil && isLoadedOnCast
@@ -68,6 +72,8 @@ final class VideoPlayerCastController: NSObject {
68
72
 
69
73
  GCKCastContext.sharedInstance().sessionManager.add(self)
70
74
  self.addCastButton(to: playerViewController)
75
+ self.addCastIndicator(to: playerViewController)
76
+ self.beginObservingPlayerTaps(playerViewController)
71
77
  self.loadMediaIfCastSessionAvailable()
72
78
  }
73
79
 
@@ -98,8 +104,17 @@ final class VideoPlayerCastController: NSObject {
98
104
 
99
105
  self.stopRemoteMediaObservation()
100
106
  GCKCastContext.sharedInstance().sessionManager.remove(self)
107
+ self.controlsHideTimer?.invalidate()
108
+ self.controlsHideTimer = nil
109
+ if let tapRecognizer = self.tapGestureRecognizer,
110
+ let view = self.playerViewController?.view {
111
+ view.removeGestureRecognizer(tapRecognizer)
112
+ }
113
+ self.tapGestureRecognizer = nil
101
114
  self.castButton?.removeFromSuperview()
102
115
  self.castButton = nil
116
+ self.castIndicatorLabel?.removeFromSuperview()
117
+ self.castIndicatorLabel = nil
103
118
  self.player = nil
104
119
  self.playerViewController = nil
105
120
  self.isLoadedOnCast = false
@@ -277,13 +292,93 @@ private extension VideoPlayerCastController {
277
292
  NSLayoutConstraint.activate([
278
293
  button.widthAnchor.constraint(equalToConstant: 44),
279
294
  button.heightAnchor.constraint(equalToConstant: 44),
280
- button.topAnchor.constraint(equalTo: overlayView.safeAreaLayoutGuide.topAnchor, constant: 16),
295
+ // Position below AVPlayerViewController's top row of controls (Done/X and route picker)
296
+ // using trailing to stay away from the leading-side dismiss button.
297
+ button.topAnchor.constraint(equalTo: overlayView.safeAreaLayoutGuide.topAnchor, constant: 60),
281
298
  button.trailingAnchor.constraint(equalTo: overlayView.safeAreaLayoutGuide.trailingAnchor, constant: -16)
282
299
  ])
283
300
 
284
301
  castButton = button
285
302
  }
286
303
 
304
+ func addCastIndicator(to playerViewController: AVPlayerViewController) {
305
+ guard castIndicatorLabel == nil else {
306
+ return
307
+ }
308
+
309
+ guard let overlayView = playerViewController.view else {
310
+ return
311
+ }
312
+
313
+ let label = UILabel(frame: .zero)
314
+ label.translatesAutoresizingMaskIntoConstraints = false
315
+ label.text = "Casting"
316
+ label.textColor = .white
317
+ label.font = UIFont.preferredFont(forTextStyle: .caption1)
318
+ label.backgroundColor = UIColor.black.withAlphaComponent(0.55)
319
+ label.layer.cornerRadius = 10
320
+ label.clipsToBounds = true
321
+ label.isHidden = true
322
+
323
+ overlayView.addSubview(label)
324
+
325
+ if let castButton = castButton {
326
+ NSLayoutConstraint.activate([
327
+ label.centerYAnchor.constraint(equalTo: castButton.centerYAnchor),
328
+ label.trailingAnchor.constraint(equalTo: castButton.leadingAnchor, constant: -8)
329
+ ])
330
+ } else {
331
+ NSLayoutConstraint.activate([
332
+ label.topAnchor.constraint(equalTo: overlayView.safeAreaLayoutGuide.topAnchor, constant: 60),
333
+ label.trailingAnchor.constraint(equalTo: overlayView.safeAreaLayoutGuide.trailingAnchor, constant: -68)
334
+ ])
335
+ }
336
+
337
+ castIndicatorLabel = label
338
+ }
339
+
340
+ func beginObservingPlayerTaps(_ playerViewController: AVPlayerViewController) {
341
+ guard let overlayView = playerViewController.view else { return }
342
+ // Remove any existing recognizer before adding a new one to prevent duplicates.
343
+ if let existing = tapGestureRecognizer {
344
+ overlayView.removeGestureRecognizer(existing)
345
+ }
346
+ let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(handlePlayerTap))
347
+ tapRecognizer.cancelsTouchesInView = false
348
+ overlayView.addGestureRecognizer(tapRecognizer)
349
+ self.tapGestureRecognizer = tapRecognizer
350
+ // Show the overlay immediately; it will auto-hide after the standard controls delay.
351
+ showOverlayControls()
352
+ }
353
+
354
+ @objc func handlePlayerTap() {
355
+ showOverlayControls()
356
+ }
357
+
358
+ func showOverlayControls() {
359
+ castButton?.isHidden = false
360
+ castIndicatorLabel?.isHidden = !isCasting
361
+ controlsHideTimer?.invalidate()
362
+ controlsHideTimer = Timer.scheduledTimer(withTimeInterval: Self.overlayAutoHideDuration, repeats: false) { [weak self] _ in
363
+ self?.hideOverlayControls()
364
+ }
365
+ }
366
+
367
+ func hideOverlayControls() {
368
+ controlsHideTimer = nil
369
+ let button = castButton
370
+ let label = castIndicatorLabel
371
+ UIView.animate(withDuration: 0.3, animations: {
372
+ button?.alpha = 0
373
+ label?.alpha = 0
374
+ }, completion: { _ in
375
+ button?.isHidden = true
376
+ label?.isHidden = true
377
+ button?.alpha = 1
378
+ label?.alpha = 1
379
+ })
380
+ }
381
+
287
382
  func loadMediaIfCastSessionAvailable() {
288
383
  guard let remoteMediaClient = remoteMediaClient,
289
384
  let mediaInfo = makeMediaInformation() else {
@@ -511,6 +606,10 @@ private extension VideoPlayerCastController {
511
606
  isLoadingOnCast = false
512
607
  isLoadedOnCast = true
513
608
  didNotifyRemoteEnd = false
609
+ DispatchQueue.main.async { [weak self] in
610
+ // Reveal the cast indicator and extend the controls visibility window.
611
+ self?.showOverlayControls()
612
+ }
514
613
  flushPendingCastCommands()
515
614
  }
516
615
 
@@ -8,7 +8,7 @@ import AVKit
8
8
  */
9
9
  @objc(VideoPlayerPlugin)
10
10
  public class VideoPlayerPlugin: CAPPlugin, CAPBridgedPlugin {
11
- private let pluginVersion: String = "8.1.9"
11
+ private let pluginVersion: String = "8.1.11"
12
12
  public let identifier = "VideoPlayerPlugin"
13
13
  public let jsName = "VideoPlayer"
14
14
  public let pluginMethods: [CAPPluginMethod] = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capgo/capacitor-video-player",
3
- "version": "8.1.9",
3
+ "version": "8.1.11",
4
4
  "description": "Capacitor plugin to play video in native player",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",