@castlabs/react-native-prestoplay-ios-avplayerviewcontroller 1.0.0

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.
Files changed (48) hide show
  1. package/ios/AVPlayerViewControllerExtension.swift +72 -0
  2. package/ios/ApplePlayerControlsPlugin.swift +23 -0
  3. package/ios/RNPrestoplayApplePlayerControlsModule.swift +192 -0
  4. package/ios/RNPrestoplayApplePlayerControlsPluginFactory.swift +19 -0
  5. package/ios/RNPrestroplay-Bridging-Header.h +9 -0
  6. package/ios/RNPrestroplayBridge.m +26 -0
  7. package/lib/AVPlayerViewControllerExtension.d.ts +28 -0
  8. package/lib/AVPlayerViewControllerExtension.js +148 -0
  9. package/lib/AVPlayerViewControllerPlugin.d.ts +15 -0
  10. package/lib/AVPlayerViewControllerPlugin.js +20 -0
  11. package/lib/components/AVPlayerViewControllerProvider.ios.d.ts +38 -0
  12. package/lib/components/AVPlayerViewControllerProvider.ios.js +41 -0
  13. package/lib/components/AppleTvContextualAction.ios.d.ts +32 -0
  14. package/lib/components/AppleTvContextualAction.ios.js +43 -0
  15. package/lib/components/AppleTvInfoBarAction.ios.d.ts +34 -0
  16. package/lib/components/AppleTvInfoBarAction.ios.js +46 -0
  17. package/lib/components/AppleTvPlayerControls.ios.d.ts +52 -0
  18. package/lib/components/AppleTvPlayerControls.ios.js +60 -0
  19. package/lib/components/AppleTvTransportBarAction.ios.d.ts +34 -0
  20. package/lib/components/AppleTvTransportBarAction.ios.js +47 -0
  21. package/lib/components/AppleTvTransportBarPopupMenu.ios.d.ts +41 -0
  22. package/lib/components/AppleTvTransportBarPopupMenu.ios.js +81 -0
  23. package/lib/components/AppleTvTransportBarSubMenu.ios.d.ts +41 -0
  24. package/lib/components/AppleTvTransportBarSubMenu.ios.js +70 -0
  25. package/lib/components/AppleTvTransportBarSubMenuItem.ios.d.ts +24 -0
  26. package/lib/components/AppleTvTransportBarSubMenuItem.ios.js +23 -0
  27. package/lib/events/AppleTvActionDetails.d.ts +7 -0
  28. package/lib/events/AppleTvActionDetails.js +1 -0
  29. package/lib/events/EventType.d.ts +7 -0
  30. package/lib/events/EventType.js +8 -0
  31. package/lib/index.d.ts +17 -0
  32. package/lib/index.js +11 -0
  33. package/lib/types/Action.d.ts +13 -0
  34. package/lib/types/Action.js +1 -0
  35. package/lib/types/ActionType.d.ts +6 -0
  36. package/lib/types/ActionType.js +7 -0
  37. package/lib/types/ActionUiProperty.d.ts +5 -0
  38. package/lib/types/ActionUiProperty.js +1 -0
  39. package/lib/types/PopupMenu.d.ts +8 -0
  40. package/lib/types/PopupMenu.js +1 -0
  41. package/lib/types/PopupMenuInternal.d.ts +8 -0
  42. package/lib/types/PopupMenuInternal.js +1 -0
  43. package/lib/types/SubMenu.d.ts +7 -0
  44. package/lib/types/SubMenu.js +1 -0
  45. package/lib/types/SubMenuItem.d.ts +4 -0
  46. package/lib/types/SubMenuItem.js +1 -0
  47. package/package.json +89 -0
  48. package/react-native-prestoplay-ios-avplayerviewcontroller.podspec +21 -0
@@ -0,0 +1,72 @@
1
+ import AVKit
2
+ import PRESTOplay
3
+ import react_native_prestoplay
4
+
5
+ class AVPlayerViewControllerExtension: BasePlayerExtension {
6
+ override init(player: PlayerProtocol, playerEventEmitter: PlayerEventEmitter) {
7
+ super.init(player: player, playerEventEmitter: playerEventEmitter)
8
+ }
9
+
10
+ override open func onContentLoaded() {
11
+ // Check if AVPlayerViewController is being used
12
+ guard let playerViewController = player?.getViewController() else {
13
+ return
14
+ }
15
+ playerViewController.playerDelegate = self;
16
+ }
17
+ }
18
+
19
+ @available(tvOS 15.0, *)
20
+ extension AVPlayerViewControllerExtension: PlayerViewControllerProtocol {
21
+
22
+ func didPlayToEnd(_ playerViewController: any PRESTOplay.PlayerViewControllerAPI) {
23
+ //print ("didPlayToEnd: ", playerViewController.avPlayerInstance?.currentItem)
24
+ }
25
+
26
+
27
+ func playerIsClosed(_ playerViewController: any PRESTOplay.PlayerViewControllerAPI) {
28
+ self.player?.destroy()
29
+
30
+ DispatchQueue.main.async { [weak self] in
31
+ self?.playerEventEmitter?.emit(
32
+ consumerId: self?.player?.id ?? "",
33
+ eventType: "playerClosed",
34
+ eventDetails: [:],
35
+ preventDuplicates: false
36
+ )
37
+ }
38
+ }
39
+
40
+ func action(_: PlayerViewControllerAPI, ofId id: String, eventName: String, data: String) {
41
+
42
+ // print ("action: id: ", id,"eventName: ", eventName, " data: ", data)
43
+
44
+ emitAction(
45
+ playerId: player?.id ?? "",
46
+ actionId: id,
47
+ eventName: eventName,
48
+ data: data
49
+ )
50
+ }
51
+
52
+ func emitAction(playerId: String,
53
+ actionId: String,
54
+ eventName: String,
55
+ data: String) {
56
+
57
+ DispatchQueue.main.async { [weak self] in
58
+ self?.playerEventEmitter?.emit(
59
+ consumerId: playerId,
60
+ eventType: "appleTvAction",
61
+ eventDetails: [
62
+ "actionId": actionId,
63
+ "eventName": eventName,
64
+ "data": data
65
+ ],
66
+ preventDuplicates: false
67
+ )
68
+ }
69
+ }
70
+ }
71
+
72
+
@@ -0,0 +1,23 @@
1
+ import PRESTOplay
2
+ import react_native_prestoplay
3
+
4
+
5
+ class ApplePlayerControlsPlugin: BasePlugin {
6
+ override func onSdkWillInitialize() -> [CLPluginProtocol] {
7
+ // print ("ApplePlayerControlsPlugin - onSdkWillInitialize")
8
+ #if os(tvOS)
9
+ return []
10
+ #else
11
+ return []
12
+ #endif
13
+ }
14
+
15
+ override func onPlayerCreated(player: PlayerProtocol, playerEventEmitter: PlayerEventEmitter) {
16
+ player.addExtension(
17
+ playerExtension: AVPlayerViewControllerExtension(
18
+ player: player,
19
+ playerEventEmitter: playerEventEmitter
20
+ )
21
+ )
22
+ }
23
+ }
@@ -0,0 +1,192 @@
1
+ import React
2
+ import AVKit
3
+ import PRESTOplay
4
+ import react_native_prestoplay
5
+
6
+ extension ActionSubMenuItemInfo {
7
+ public convenience init?(from dic: NSDictionary) {
8
+ guard let title = dic["title"] as? String else { return nil }
9
+ guard let value = dic["value"] as? String else { return nil }
10
+
11
+ self.init(title: title, value: value)
12
+ }
13
+ }
14
+
15
+ extension ActionSubMenuInfo {
16
+ public convenience init?(from dic: NSDictionary) {
17
+ guard let menuItems = dic["menuItems"] as? NSArray else { return nil }
18
+
19
+ let defaultValue = dic["defaultValue"] as? String
20
+ let subMenuItemInfos: [ActionSubMenuItemInfo] = menuItems.compactMap {
21
+ guard let menuItemDic = $0 as? NSDictionary else { return nil }
22
+ return ActionSubMenuItemInfo(from: menuItemDic)
23
+ }
24
+
25
+ self.init(defaultValue: defaultValue, menuItems: subMenuItemInfos)
26
+ }
27
+ }
28
+
29
+ extension ActionButtonInfo {
30
+ public convenience init?(from dic: NSDictionary) {
31
+ guard let id = dic["id"] as? String else { return nil }
32
+ guard let title = dic["title"] as? String else { return nil }
33
+
34
+ var subMenuInfo: ActionSubMenuInfo?
35
+ if let subMenu = dic["subMenu"] as? NSDictionary {
36
+ subMenuInfo = ActionSubMenuInfo(from: subMenu)
37
+ }
38
+
39
+ self.init(
40
+ id: id,
41
+ title: title,
42
+ imageName: dic["imageName"] as? String,
43
+ subMenu: subMenuInfo
44
+ )
45
+ }
46
+ }
47
+
48
+ extension ActionPopupMenuInfo {
49
+ public convenience init?(from dic: NSDictionary) {
50
+ guard let title = dic["title"] as? String else { return nil }
51
+ guard let imageName = dic["imageName"] as? String else { return nil }
52
+ guard let actions = dic["actions"] as? NSArray else { return nil }
53
+
54
+ let actionInfos: [ActionButtonInfo] = actions.compactMap {
55
+ guard let actionDic = $0 as? NSDictionary else { return nil }
56
+ return ActionButtonInfo(from: actionDic)
57
+ }
58
+
59
+ self.init(title: title, imageName: imageName, actionInfos: actionInfos)
60
+ }
61
+ }
62
+
63
+ extension ContextualActionButtonInfo {
64
+ public convenience init?(from dic: NSDictionary) {
65
+ guard let id = dic["id"] as? String else { return nil }
66
+ guard let title = dic["title"] as? String else { return nil }
67
+ guard let startSecond = dic["startSecond"] as? CMTimeValue else { return nil }
68
+ guard let endSecond = dic["endSecond"] as? CMTimeValue else { return nil }
69
+
70
+ self.init(
71
+ id: id,
72
+ title: title,
73
+ imageName: dic["imageName"] as? String,
74
+ startSecond: startSecond,
75
+ endSecond: endSecond
76
+ )
77
+ }
78
+ }
79
+
80
+ @objc(RNPrestoplayApplePlayerControlsModule)
81
+ class RNPrestoplayApplePlayerControlsModule: NSObject {
82
+
83
+ var playerId: String?;
84
+
85
+ override init() {
86
+ super.init()
87
+ }
88
+
89
+ private let PLAYER_EVENT = "onPlayerEvent"
90
+ private let TRANSPORT_BAR_ACTION_EVENT = "onTransportBarActionEvent"
91
+ private let INFO_BAR_ACTION_EVENT = "onInfoBarActionEvent"
92
+ private let CONTEXTUAL_ACTION_EVENT = "onContextualActionEvent"
93
+
94
+ @objc(setAppleTvActions:transportBarActions:infoBarActions:contextualActions:resolver:rejecter:)
95
+ public func setAppleTvActions(
96
+ _ playerId: String!,
97
+ _ actionsInfo: [NSDictionary]?,
98
+ _ infoBarActionsInfo: [NSDictionary]?,
99
+ _ contextualActionsInfo: [NSDictionary]?,
100
+ resolver resolve: @escaping RCTPromiseResolveBlock,
101
+ rejecter reject: @escaping RCTPromiseRejectBlock
102
+ ) {
103
+
104
+ self.playerId = playerId
105
+
106
+ guard let player = Repository.shared.players.get(key: playerId) else {
107
+ Rejecter(reject).reject(PrestoPlayError(.fatal, .playerNotFound))
108
+ return
109
+ }
110
+
111
+ guard let playerViewController = player.getViewController() else {
112
+ Rejecter(reject).reject(PrestoPlayError(.fatal, .playerNotFound, message: "invalid player type"))
113
+ return
114
+ }
115
+
116
+ #if os(tvOS)
117
+ DispatchQueue.main.async { [weak self] in
118
+
119
+ let transportActions: [AnyObject]? = actionsInfo?.compactMap {
120
+ if let popupMenu = ActionPopupMenuInfo(from: $0) {
121
+ return popupMenu
122
+ } else if let action = ActionButtonInfo(from: $0) {
123
+ return action
124
+ } else {
125
+ return nil
126
+ }
127
+ }
128
+
129
+ if let transportActions {
130
+ playerViewController.setTransportBarActions(transportActions, eventName: self!.TRANSPORT_BAR_ACTION_EVENT)
131
+ }
132
+
133
+ let infobarActions: [ActionButtonInfo]? = infoBarActionsInfo?.compactMap {
134
+ if let action = ActionButtonInfo(from: $0) {
135
+ return action
136
+ } else {
137
+ return nil
138
+ }
139
+ }
140
+
141
+ if let infobarActions {
142
+ playerViewController.setInfoTabActions(infobarActions, eventName: self!.INFO_BAR_ACTION_EVENT)
143
+ }
144
+
145
+ let contextualActions: [ContextualActionButtonInfo]? = contextualActionsInfo?.compactMap {
146
+ if let action = ContextualActionButtonInfo(from: $0) {
147
+ return action
148
+ } else {
149
+ return nil
150
+ }
151
+ }
152
+ if let contextualActions {
153
+ playerViewController.setContextualActions(contextualActions, eventName: self!.CONTEXTUAL_ACTION_EVENT)
154
+ }
155
+ resolve(nil)
156
+
157
+ }
158
+ #endif
159
+ }
160
+
161
+ @objc(setActionStateAndUi:actionId:state:uiInfo:resolver:rejecter:)
162
+ public func setActionStateAndUi(
163
+ _ playerId: String!,
164
+ _ actionId: String,
165
+ state: Bool,
166
+ uiInfo: NSDictionary,
167
+ resolver resolve: @escaping RCTPromiseResolveBlock,
168
+ rejecter reject: @escaping RCTPromiseRejectBlock
169
+ ) {
170
+ #if os(tvOS)
171
+
172
+ guard let player = Repository.shared.players.get(key: playerId) else {
173
+ Rejecter(reject).reject(PrestoPlayError(.fatal, .playerNotFound))
174
+ return
175
+ }
176
+ guard let playerViewController = player.getViewController() else {
177
+ Rejecter(reject).reject(PrestoPlayError(.fatal, .playerNotFound, message: "invalid player type"))
178
+ return
179
+ }
180
+
181
+ DispatchQueue.main.async { [weak self] in
182
+ playerViewController.setActionState(
183
+ ofId: actionId,
184
+ state: state,
185
+ title: uiInfo["title"] as? String,
186
+ imageName: uiInfo["imageName"] as? String
187
+ )
188
+ resolve(nil)
189
+ }
190
+ #endif
191
+ }
192
+ }
@@ -0,0 +1,19 @@
1
+ import React
2
+ import react_native_prestoplay
3
+
4
+ @objc(RNPrestoplayAVPlayerViewControllerPluginFactory)
5
+ class RNPrestoplayAVPlayerViewControllerPluginFactory: NSObject, RCTBridgeModule, PluginFactoryProtocol {
6
+
7
+ static func moduleName() -> String! {
8
+ return "RNPrestoplayAVPlayerViewControllerPluginFactory"
9
+ }
10
+
11
+ @objc(requiresMainQueueSetup)
12
+ static func requiresMainQueueSetup() -> Bool {
13
+ return false
14
+ }
15
+
16
+ public func getPlugin() -> PluginProtocol {
17
+ return ApplePlayerControlsPlugin()
18
+ }
19
+ }
@@ -0,0 +1,9 @@
1
+ //
2
+ // Use this file to import your target's public headers that you would like to expose to Swift.
3
+ //
4
+
5
+ #import <React/RCTViewManager.h>
6
+ #import <React/RCTUIManager.h>
7
+ #import <React/RCTBridgeModule.h>
8
+ #import <React/RCTEventEmitter.h>
9
+ #import <React/RCTRootContentView.h>
@@ -0,0 +1,26 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <React/RCTViewManager.h>
3
+ #import <React/RCTBridgeModule.h>
4
+ #import <React/RCTEventEmitter.h>
5
+
6
+
7
+ @interface RCT_EXTERN_MODULE(RNPrestoplayApplePlayerControlsModule, NSObject)
8
+
9
+ RCT_EXTERN_METHOD(
10
+ setAppleTvActions:(nonnull NSString*) playerId
11
+ transportBarActions:(NSArray *)transportBarActions
12
+ infoBarActions:(NSArray *)infoBarActions
13
+ contextualActions:(NSArray *)contextualActions
14
+ resolver:(RCTPromiseResolveBlock)resolve
15
+ rejecter:(RCTPromiseRejectBlock)reject)
16
+
17
+ RCT_EXTERN_METHOD(
18
+ setActionStateAndUi:(nonnull NSString*) playerId
19
+ actionId:(NSString *)id
20
+ state:(BOOL)state
21
+ uiInfo:(NSDictionary *)uiInfo
22
+ resolver:(RCTPromiseResolveBlock)resolve
23
+ rejecter:(RCTPromiseRejectBlock)reject)
24
+ @end
25
+
26
+
@@ -0,0 +1,28 @@
1
+ import { AppleTvTransportBarActionProps } from './components/AppleTvTransportBarAction.ios';
2
+ import { AppleTvInfoBarActionProps } from './components/AppleTvInfoBarAction.ios';
3
+ import { Player, PlayerExtension, BaseEventEmitter } from '@castlabs/react-native-prestoplay';
4
+ import { AppleTvContextualActionProps } from './components/AppleTvContextualAction.ios';
5
+ import { ActionType } from './types/ActionType';
6
+ import { PopupMenuInternal } from './types/PopupMenuInternal';
7
+ export declare class AVPlayerViewControllerExtension extends PlayerExtension {
8
+ readonly id: string;
9
+ private readyToSubmit;
10
+ private readonly logger;
11
+ private transportBarActions;
12
+ private infoBarActions;
13
+ private contextualActions;
14
+ private popUpMenus;
15
+ readonly eventEmitter: BaseEventEmitter;
16
+ constructor(player: Player);
17
+ destroy(): void;
18
+ private onAppleTvAction;
19
+ private registerTransportBarActions;
20
+ private registerTransportBarSubMenu;
21
+ addAction(type: ActionType, actionId: string, props: AppleTvTransportBarActionProps | AppleTvInfoBarActionProps | AppleTvContextualActionProps): void;
22
+ removeAction(type: ActionType, actionId: string): void;
23
+ setPopupMenu(menuId: string, popUpMenu: PopupMenuInternal): void;
24
+ removePopupMenu(menuId: string): void;
25
+ updateAction(actionId: string, state: boolean, title: string, icon: string): Promise<void>;
26
+ onContentLoaded(): void;
27
+ private submitActions;
28
+ }
@@ -0,0 +1,148 @@
1
+ import { PlayerExtension, rethrowAsPrestoPlayErrorIfPossible, Logger, BaseEventEmitter, } from '@castlabs/react-native-prestoplay';
2
+ import { NativeModules } from 'react-native';
3
+ import { ActionType } from './types/ActionType';
4
+ import { EventType as LocalEventType } from './events/EventType';
5
+ export class AVPlayerViewControllerExtension extends PlayerExtension {
6
+ id;
7
+ readyToSubmit = false;
8
+ logger = new Logger('AVPlayerViewControllerExtension');
9
+ transportBarActions = [];
10
+ infoBarActions = [];
11
+ contextualActions = [];
12
+ popUpMenus = new Map();
13
+ eventEmitter;
14
+ constructor(player) {
15
+ super(player);
16
+ this.id = player.id;
17
+ this.eventEmitter = new BaseEventEmitter();
18
+ player.addEventListener(LocalEventType.AppleTvAction, this.onAppleTvAction.bind(this));
19
+ }
20
+ destroy() {
21
+ this.player.removeEventListener(LocalEventType.AppleTvAction, this.onAppleTvAction.bind(this));
22
+ }
23
+ onAppleTvAction(event) {
24
+ const details = event.details;
25
+ this.eventEmitter.emit(details.actionId, event);
26
+ }
27
+ registerTransportBarActions(actions) {
28
+ const actionsResult = [];
29
+ actions.forEach(action => {
30
+ actionsResult.push({
31
+ id: action.id,
32
+ title: action.title,
33
+ state: action.state,
34
+ imageName: action.imageName,
35
+ });
36
+ });
37
+ return actionsResult;
38
+ }
39
+ registerTransportBarSubMenu(submenus) {
40
+ const actions = [];
41
+ submenus.forEach(menu => {
42
+ actions.push({
43
+ id: menu.id,
44
+ title: menu.title,
45
+ subMenu: menu,
46
+ });
47
+ });
48
+ return actions;
49
+ }
50
+ addAction(type, actionId, props) {
51
+ if (type === ActionType.InfoBar) {
52
+ const infoBarProps = props;
53
+ this.infoBarActions.push({
54
+ id: actionId,
55
+ title: props.title,
56
+ state: infoBarProps.state,
57
+ imageName: props.icon,
58
+ });
59
+ }
60
+ else if (type === ActionType.Contextual) {
61
+ const contextualProps = props;
62
+ this.contextualActions.push({
63
+ id: actionId,
64
+ title: props.title,
65
+ imageName: props.icon,
66
+ startSecond: contextualProps.startAt,
67
+ endSecond: contextualProps.endAt,
68
+ });
69
+ }
70
+ else if (type === ActionType.TransportBar) {
71
+ const transportBarProps = props;
72
+ this.transportBarActions.push({
73
+ id: actionId,
74
+ title: props.title,
75
+ state: transportBarProps.state,
76
+ imageName: props.icon,
77
+ });
78
+ }
79
+ if (this.readyToSubmit) {
80
+ this.submitActions();
81
+ }
82
+ }
83
+ removeAction(type, actionId) {
84
+ if (type === ActionType.InfoBar) {
85
+ this.infoBarActions = this.infoBarActions.filter(item => item.id !== actionId);
86
+ }
87
+ else if (type === ActionType.Contextual) {
88
+ this.contextualActions = this.contextualActions.filter(item => item.id !== actionId);
89
+ }
90
+ else if (type === ActionType.TransportBar) {
91
+ this.transportBarActions = this.transportBarActions.filter(item => item.id !== actionId);
92
+ }
93
+ if (this.readyToSubmit) {
94
+ this.submitActions();
95
+ }
96
+ }
97
+ setPopupMenu(menuId, popUpMenu) {
98
+ const newPopupMenu = {
99
+ id: menuId,
100
+ title: popUpMenu.title,
101
+ imageName: popUpMenu.icon,
102
+ actions: [],
103
+ };
104
+ newPopupMenu.actions.push(...this.registerTransportBarActions(popUpMenu.transportBarActions));
105
+ newPopupMenu.actions.push(...this.registerTransportBarSubMenu(popUpMenu.subMenus));
106
+ this.popUpMenus.set(menuId, newPopupMenu);
107
+ if (this.readyToSubmit) {
108
+ this.submitActions();
109
+ }
110
+ }
111
+ removePopupMenu(menuId) {
112
+ this.popUpMenus.delete(menuId);
113
+ if (this.readyToSubmit) {
114
+ this.submitActions();
115
+ }
116
+ }
117
+ async updateAction(actionId, state, title, icon) {
118
+ const update = {
119
+ title: title,
120
+ imageName: icon,
121
+ };
122
+ await NativeModules.RNPrestoplayApplePlayerControlsModule.setActionStateAndUi(this.player.id, actionId, state, update);
123
+ }
124
+ onContentLoaded() {
125
+ /**
126
+ * setAppleTvActions must be called after AVPlayerViewController is available
127
+ * which is after content is loaded due API restrinction on prestroplay-apple
128
+ */
129
+ this.readyToSubmit = true;
130
+ this.submitActions();
131
+ }
132
+ async submitActions() {
133
+ if (!this.player.isApplePlayer) {
134
+ return;
135
+ }
136
+ const transportBarActions = [...this.transportBarActions];
137
+ this.popUpMenus.forEach((menu, _id) => {
138
+ transportBarActions.push(menu);
139
+ });
140
+ NativeModules.RNPrestoplayApplePlayerControlsModule.setAppleTvActions(this.player.id, transportBarActions, this.infoBarActions, this.contextualActions)
141
+ .then(() => {
142
+ this.logger.debug('AVPlayerViewControllerExtension: submitActions done');
143
+ })
144
+ .catch((error) => {
145
+ rethrowAsPrestoPlayErrorIfPossible(error);
146
+ });
147
+ }
148
+ }
@@ -0,0 +1,15 @@
1
+ import { Player, Plugin } from '@castlabs/react-native-prestoplay';
2
+ /**
3
+ * Provides {@link AVPlayerViewControllerProvider} integration for iOS and tvOS.
4
+ *
5
+ * In case of tvOS, it also enables the use of custom Apple TV player controls
6
+ * by using the {@link AppleTvPlayerControls} component.
7
+ *
8
+ * @group Classes
9
+ */
10
+ export default class AVPlayerViewControllerPlugin extends Plugin {
11
+ /** @hidden */
12
+ getNativePluginFactoryModuleName(): string | null;
13
+ /** @hidden */
14
+ onPlayerCreated(player: Player): void;
15
+ }
@@ -0,0 +1,20 @@
1
+ import { Plugin } from '@castlabs/react-native-prestoplay';
2
+ import { AVPlayerViewControllerExtension } from './AVPlayerViewControllerExtension';
3
+ /**
4
+ * Provides {@link AVPlayerViewControllerProvider} integration for iOS and tvOS.
5
+ *
6
+ * In case of tvOS, it also enables the use of custom Apple TV player controls
7
+ * by using the {@link AppleTvPlayerControls} component.
8
+ *
9
+ * @group Classes
10
+ */
11
+ export default class AVPlayerViewControllerPlugin extends Plugin {
12
+ /** @hidden */
13
+ getNativePluginFactoryModuleName() {
14
+ return 'RNPrestoplayAVPlayerViewControllerPluginFactory';
15
+ }
16
+ /** @hidden */
17
+ onPlayerCreated(player) {
18
+ player.addExtension(new AVPlayerViewControllerExtension(player));
19
+ }
20
+ }
@@ -0,0 +1,38 @@
1
+ import React, { ReactElement, ReactNode } from 'react';
2
+ import { PlayerConfiguration } from '@castlabs/react-native-prestoplay';
3
+ import AppleTvPlayerControls from './AppleTvPlayerControls.ios';
4
+ /**
5
+ * Props for the {@link AVPlayerViewControllerProvider} component.
6
+ *
7
+ * @group Type Aliases
8
+ */
9
+ export type AVPlayerViewControllerProviderProps = {
10
+ /**
11
+ * The configuration used to initialize the player instance.
12
+ *
13
+ * For detailed options, see {@link PlayerConfiguration}.
14
+ *
15
+ */
16
+ playerConfig?: PlayerConfiguration;
17
+ /**
18
+ * Callback invoked when the underlying `AVPlayerViewController` is closed.
19
+ */
20
+ onClose?: () => void;
21
+ /**
22
+ * Child component are not rendered visually, but they can
23
+ * interact with the player instance.
24
+ *
25
+ * This may include {@link AppleTvPlayerControls} for custom tvOS player controls.
26
+ */
27
+ children: ReactNode | ReactElement<typeof AppleTvPlayerControls>;
28
+ };
29
+ /**
30
+ * An alternative to the `PlayerProvider` component, which uses an
31
+ * AVPlayerViewController to present the player UI on iOS and tvOS.
32
+ *
33
+ * You can use the {@link AppleTvPlayerControls} component as a child to
34
+ * present custom tvOS player controls.
35
+ *
36
+ * @group Components
37
+ */
38
+ export declare const AVPlayerViewControllerProvider: React.NamedExoticComponent<AVPlayerViewControllerProviderProps>;
@@ -0,0 +1,41 @@
1
+ import React, { memo, useEffect, useMemo } from 'react';
2
+ import { EventType, PlayerContext, } from '@castlabs/react-native-prestoplay';
3
+ import { Player } from '@castlabs/react-native-prestoplay';
4
+ /**
5
+ * An alternative to the `PlayerProvider` component, which uses an
6
+ * AVPlayerViewController to present the player UI on iOS and tvOS.
7
+ *
8
+ * You can use the {@link AppleTvPlayerControls} component as a child to
9
+ * present custom tvOS player controls.
10
+ *
11
+ * @group Components
12
+ */
13
+ export const AVPlayerViewControllerProvider = memo(function ({ playerConfig, onClose, children, }) {
14
+ const player = useMemo(() => {
15
+ return new Player(true);
16
+ }, []);
17
+ useEffect(() => {
18
+ const onEvent = (_event) => {
19
+ onClose && onClose();
20
+ };
21
+ player.addEventListener(EventType.PlayerClosed, onEvent);
22
+ return () => {
23
+ player.removeEventListener(EventType.PlayerClosed, onEvent);
24
+ player.destroy();
25
+ };
26
+ }, [player, onClose]);
27
+ // The children might be a new value, while config remains the same.
28
+ // Avoid creating the new player instance by comparing the hash of the config.
29
+ const playerConfigHash = JSON.stringify(playerConfig);
30
+ useEffect(() => {
31
+ if (playerConfig) {
32
+ console.info('Opening player with config:', playerConfig);
33
+ player.open(playerConfig).catch(error => {
34
+ console.error('Error opening player:', error);
35
+ });
36
+ }
37
+ },
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
39
+ [playerConfigHash]);
40
+ return (<PlayerContext.Provider value={{ player }}>{children}</PlayerContext.Provider>);
41
+ });
@@ -0,0 +1,32 @@
1
+ /// <reference types="react" />
2
+ /**
3
+ * When you set a value for the {@link AppleTvContextualAction} properties,
4
+ * the player presents the controls immediately. To present them only during a
5
+ * relevant section of the content, set the {@link startAt} and {@link endAt}
6
+ * properties to define the presentation range.
7
+ *
8
+ * @group Type Aliases
9
+ */
10
+ export type AppleTvContextualActionProps = {
11
+ /** The text of the contextual action button. */
12
+ title: string;
13
+ /** The name of the image resource in the Xcode app project. */
14
+ icon: string;
15
+ /** The time in seconds when the contextual action becomes visible. */
16
+ startAt: number;
17
+ /** The time in seconds when the contextual action stops being visible. */
18
+ endAt: number;
19
+ /** Action to be executed when the button is pressed. */
20
+ onPress: () => void;
21
+ };
22
+ /**
23
+ * **⚠️ This component is only supported on tvOS.**
24
+ *
25
+ * You can use the tvOS player UI to present controls contextually, which you
26
+ * display for a specific range of time in the content and then dismiss.
27
+ *
28
+ * For more details, see Apple documentation: {@link https://developer.apple.com/documentation/avkit/customizing-the-tvos-playback-experience#Present-Actions-Contextually | Present Actions Contextually}.
29
+ *
30
+ * @group Components
31
+ */
32
+ export declare const AppleTvContextualAction: React.FC<AppleTvContextualActionProps>;