Skip to content

Commit

Permalink
Merge pull request #437 from THEOplayer/feature/ios-hide-home-indicat…
Browse files Browse the repository at this point in the history
…or-in-fullscreen

Feature/ios hide home indicator in fullscreen
  • Loading branch information
tvanlaerhoven authored Nov 19, 2024
2 parents 7209442 + 50e8e34 commit 95b42ec
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 28 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,15 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## [Unreleased]
## Unreleased

### Added

- Added HomeIndicatorViewController to iOS, which can be used as an alternative rootViewController for the native App. It will automatically show/hide the home indicator when transitioning from/to fullscreen presentationMode.

### Changed

- Simplified the viewController reparenting mechanism on iOS that is applied when changing the presentationMode to/from fullscreen.
- The `MediaPlaybackService` on Android is never restarted if a MediaButton event is received after the app was closed.
- Added a consumer R8 config file on Android, telling R8 not to throw errors or warnings because of classes that are expected to be missing.
- Simplified the viewController reparenting mechanism on iOS that is applied when changing the `presentationMode` to/from fullscreen.
Expand Down
25 changes: 25 additions & 0 deletions doc/fullscreen.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,31 @@ player.presentationMode = PresentationMode.fullscreen;

When the player transitions back to inline mode, the view hierarchy will be restored.

### iOS home indicator

On iOS, a visual bar at the bottom of the screen called the home indicator, allows the user to return to the device's home screen when dragging it up. This is often preceived as a disturbing visual element when viewing a stream in fullscreen mode. To hide the home indicator, The rootViewController of the application needs to be setup accordingly, via inheritance. As part of react-native-theoplayer we've prepared a basic ViewController setup (HomeIndicatorViewController) that takes care of this. To hide the home indicator you change the default rootViewcontroller from a basic UIViewController to our HomeIndicatorViewController:

Import the react-native-theoplayer swift code:
```swift
@import react_native_theoplayer;
```

And, when using RCTAppDelegate in the native app:
```swift
- (UIViewController *)createRootViewController {
return [HomeIndicatorViewController new];
}
```

or otherwise:
```swift
HomeIndicatorViewController *rootViewController = [HomeIndicatorViewController new];
...
self.window.rootViewController = rootViewController;
```

Our iOS presentationMode changing code checks if the rootViewController is of type HomeIndicatorViewController and will, in that case, automatically take care of showing/hiding the home indicator.

## Portals

A [Portal](https://react.dev/reference/react-dom/createPortal#usage) is a well-known concept in React that
Expand Down
54 changes: 50 additions & 4 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
PODS:
- boost (1.84.0)
- DoubleConversion (1.1.6)
- DSFRegex (3.3.1)
- FBLazyVector (0.75.4-0)
- fmt (9.1.0)
- glog (0.3.5)
- google-cast-sdk-dynamic-xcframework (4.8.0)
- GoogleAds-IMA-iOS-SDK (3.23.0)
- GoogleAds-IMA-tvOS-SDK (4.9.1)
- hermes-engine (0.75.4-0):
- hermes-engine/Pre-built (= 0.75.4-0)
- hermes-engine/Pre-built (0.75.4-0)
Expand Down Expand Up @@ -1269,9 +1272,12 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-theoplayer (8.5.0):
- react-native-theoplayer (8.7.0):
- React-Core
- THEOplayerSDK-core (~> 8.0)
- THEOplayer-Connector-SideloadedSubtitle (~> 8.3)
- THEOplayer-Integration-GoogleCast (~> 8.3)
- THEOplayer-Integration-GoogleIMA (~> 8.3)
- THEOplayerSDK-core (~> 8.3)
- React-nativeconfig (0.75.4-0)
- React-NativeModulesApple (0.75.4-0):
- glog
Expand Down Expand Up @@ -1535,7 +1541,29 @@ PODS:
- RNSVG (13.14.1):
- React-Core
- SocketRocket (0.7.0)
- Swifter (1.5.0)
- SwiftSubtitles (0.9.1):
- DSFRegex (~> 3.3.1)
- TinyCSV (~> 0.6.1)
- THEOplayer-Connector-SideloadedSubtitle (8.3.0):
- Swifter (= 1.5.0)
- SwiftSubtitles (= 0.9.1)
- THEOplayerSDK-core (~> 8)
- THEOplayer-Integration-GoogleCast (8.3.0):
- THEOplayer-Integration-GoogleCast/Base (= 8.3.0)
- THEOplayer-Integration-GoogleCast/Dependencies (= 8.3.0)
- THEOplayer-Integration-GoogleCast/Base (8.3.0)
- THEOplayer-Integration-GoogleCast/Dependencies (8.3.0):
- google-cast-sdk-dynamic-xcframework (~> 4.8)
- THEOplayer-Integration-GoogleIMA (8.3.0):
- THEOplayer-Integration-GoogleIMA/Base (= 8.3.0)
- THEOplayer-Integration-GoogleIMA/Dependencies (= 8.3.0)
- THEOplayer-Integration-GoogleIMA/Base (8.3.0)
- THEOplayer-Integration-GoogleIMA/Dependencies (8.3.0):
- GoogleAds-IMA-iOS-SDK (~> 3.18)
- GoogleAds-IMA-tvOS-SDK (~> 4.8)
- THEOplayerSDK-core (8.3.0)
- TinyCSV (0.6.1)
- Yoga (0.0.0)

DEPENDENCIES:
Expand Down Expand Up @@ -1610,10 +1638,19 @@ DEPENDENCIES:

SPEC REPOS:
trunk:
- DSFRegex
- google-cast-sdk-dynamic-xcframework
- GoogleAds-IMA-iOS-SDK
- GoogleAds-IMA-tvOS-SDK
- PromisesObjC
- SocketRocket
- Swifter
- SwiftSubtitles
- THEOplayer-Connector-SideloadedSubtitle
- THEOplayer-Integration-GoogleCast
- THEOplayer-Integration-GoogleIMA
- THEOplayerSDK-core
- TinyCSV

EXTERNAL SOURCES:
boost:
Expand Down Expand Up @@ -1759,10 +1796,13 @@ CHECKOUT OPTIONS:
SPEC CHECKSUMS:
boost: d70f09e8edc61001a5cd2131f47cca76f9b3f031
DoubleConversion: 00143ab27d470b28035933623e1a3ea37e68889c
DSFRegex: 8493187c71ac199695245eb9ec98bad4f87a2f0b
FBLazyVector: e06894178a2469b6da988d1d4de56aca5a3f90d1
fmt: 1568fa7b2f242362c45c42d4a15e9dd4b2e621b3
glog: 4d211b5b727f9d4542418484bf9945f28b8cb4a5
google-cast-sdk-dynamic-xcframework: d1323732742c979b2d7e5b061cbe665915981f3d
GoogleAds-IMA-iOS-SDK: ee2a68ed7a1a17c7bb81bdb1b81590b35a3fc8f3
GoogleAds-IMA-tvOS-SDK: 85e799c35051454693492480ef7e4ae2e701a05f
hermes-engine: ac68d6c3169772a7a7f9eeb25dbb5ff87930034f
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
RCT-Folly: 4728c42e04357ad445c7048e7c542b59f3ee6b4f
Expand Down Expand Up @@ -1796,7 +1836,7 @@ SPEC CHECKSUMS:
React-microtasksnativemodule: a3489ca37b515f6f685ec1a86c8df364343ac578
react-native-google-cast: d7bdfd1a0eeba84afde03b9722351ec29543e74c
react-native-slider: caf709802c97955d1dc369fc2ca6250f18bd58fb
react-native-theoplayer: 17d8a46df5e3e6e75f9afb96f5d409e5e0384d3d
react-native-theoplayer: 9ace968b751d79999f9c6ae6c21e80b6d143975c
React-nativeconfig: ea22f0ab525feb865d2e0ed5d7aad156c36abe6b
React-NativeModulesApple: 5efee2e69aaa7ff47f40a2918f2b48534a2e431b
React-perflogger: f31660a8693c3444e1832c237ba25a13f613436e
Expand Down Expand Up @@ -1825,8 +1865,14 @@ SPEC CHECKSUMS:
ReactCommon: a1cd388360d4e9545ab8e571ff293606c9ea6687
RNSVG: af3907ac5d4fa26a862b75a16d8f15bc74f2ceda
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
Swifter: e71dd674404923d7f03ebb03f3f222d1c570bc8e
SwiftSubtitles: c659af19d710a2946779015464c0577d07fe4666
THEOplayer-Connector-SideloadedSubtitle: cc36449bedaaa44a21cff6156bdddc71f767d1e6
THEOplayer-Integration-GoogleCast: e4fb3f3ae2022b079906f42ec708ce96579cb629
THEOplayer-Integration-GoogleIMA: 7310ca6eb20628f2b774905bbead37e1d9105d24
THEOplayerSDK-core: 03e55ca6dfe3f16d52fdc9e4bdc6bff150d63531
Yoga: 07ebe50bd234e51e5e3e07befa14a3078a0fcbbd
TinyCSV: fd6228edbcf1c07466ac34b76dac5e052143eaba
Yoga: 1eb8c4882b3018c344a2ff61c5f2e5c6b1711d82

PODFILE CHECKSUM: cde44975d89d0413eb0e568fb525618efb0ece50

Expand Down
22 changes: 21 additions & 1 deletion example/ios/ReactNativeTHEOplayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -502,13 +502,17 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS/Pods-ReactNativeTHEOplayer-tvOS-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -544,12 +548,18 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests/Pods-ReactNativeTHEOplayer-ReactNativeTHEOplayerTests-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleCast/Base/THEOplayerGoogleCastIntegration.framework/THEOplayerGoogleCastIntegration",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/google-cast-sdk-dynamic-xcframework/GoogleCast.framework/GoogleCast",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleCastIntegration.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleCast.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
Expand All @@ -566,13 +576,17 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests/Pods-ReactNativeTHEOplayer-tvOS-ReactNativeTHEOplayer-tvOSTests-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-tvOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
Expand Down Expand Up @@ -720,12 +734,18 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-ReactNativeTHEOplayer/Pods-ReactNativeTHEOplayer-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/GoogleAds-IMA-iOS-SDK/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleCast/Base/THEOplayerGoogleCastIntegration.framework/THEOplayerGoogleCastIntegration",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayer-Integration-GoogleIMA/Base/THEOplayerGoogleIMAIntegration.framework/THEOplayerGoogleIMAIntegration",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/THEOplayerSDK-core/THEOplayerSDK.framework/THEOplayerSDK",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/google-cast-sdk-dynamic-xcframework/GoogleCast.framework/GoogleCast",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleInteractiveMediaAds.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleCastIntegration.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerGoogleIMAIntegration.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/THEOplayerSDK.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleCast.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
Expand Down Expand Up @@ -838,7 +858,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 8YAB8ZY55Y;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
Expand Down
6 changes: 2 additions & 4 deletions example/ios/ReactNativeTHEOplayer/AppDelegate.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#import <React/RCTBridgeDelegate.h>
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>

@property (nonatomic, strong) UIWindow *window;
@interface AppDelegate : RCTAppDelegate

@end
27 changes: 10 additions & 17 deletions example/ios/ReactNativeTHEOplayer/AppDelegate.m
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
#import "AppDelegate.h"

#import <React/RCTBridge.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
@import react_native_theoplayer;

#if !TARGET_OS_TV
#import <GoogleCast/GoogleCast.h>
Expand All @@ -12,20 +11,10 @@ @implementation AppDelegate

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
moduleName:@"ReactNativeTHEOplayer"
initialProperties:nil];

rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];

rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
self.moduleName = @"ReactNativeTHEOplayer";
// You can add your custom initial props in the dictionary below.
// They will be passed down to the ViewController used by React Native.
self.initialProps = @{};

#if !TARGET_OS_TV
NSString *receiverAppID = @"CC1AD845"; // default receiver
Expand All @@ -36,7 +25,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
[GCKCastContext setSharedInstanceWithOptions:options];
#endif

return YES;
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (UIViewController *)createRootViewController {
return [HomeIndicatorViewController new];
}

- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
Expand Down
15 changes: 15 additions & 0 deletions ios/presentationMode/THEOplayerRCTPresentationModeManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,29 @@ public class THEOplayerRCTPresentationModeManager {
self.containerView = self.view?.findParentViewOfType(RCTView.self)
self.inlineParentView = self.containerView?.findParentViewOfType(RCTView.self)

// move the player
if let containerView = self.containerView,
let fullscreenParentView = self.view?.findParentViewOfType(RCTRootContentView.self) {
self.moveView(containerView, to: fullscreenParentView)

// start hiding home indicator
if let customRootViewController = fullscreenParentView.findViewController() as? HomeIndicatorViewController {
customRootViewController.prefersAutoHidden = true
customRootViewController.setNeedsUpdateOfHomeIndicatorAutoHidden()
}
}
self.rnInlineMode = .fullscreen
}

private func exitFullscreen() {
// stop hiding home indicator
if let fullscreenParentView = self.view?.findParentViewOfType(RCTRootContentView.self),
let customRootViewController = fullscreenParentView.findViewController() as? HomeIndicatorViewController {
customRootViewController.prefersAutoHidden = false
customRootViewController.setNeedsUpdateOfHomeIndicatorAutoHidden()
}

// move the player
if let containerView = self.containerView,
let inlineParentView = self.inlineParentView {
self.moveView(containerView, to: inlineParentView)
Expand Down
14 changes: 14 additions & 0 deletions ios/viewController/HomeIndicatorViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// HomeIndicatorViewController.swift

import UIKit

@objc
public class HomeIndicatorViewController: UIViewController {
public var prefersAutoHidden = false

public override var prefersHomeIndicatorAutoHidden: Bool {
get {
return self.prefersAutoHidden
}
}
}
2 changes: 1 addition & 1 deletion react-native-theoplayer.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Pod::Spec.new do |s|
s.platforms = { :ios => "13.4", :tvos => "13.4" }
s.source = { :git => "https://www.theoplayer.com/.git", :tag => "#{s.version}" }

s.source_files = 'ios/*.{h,m,swift}', 'ios/ads/*.swift', 'ios/casting/*.swift', 'ios/contentprotection/*.swift', 'ios/pip/*.swift', 'ios/backgroundAudio/*.swift', 'ios/cache/*.swift', 'ios/sideloadedMetadata/*.swift', 'ios/eventBroadcasting/*.swift' , 'ios/ui/*.swift', 'ios/presentationMode/*.swift'
s.source_files = 'ios/*.{h,m,swift}', 'ios/ads/*.swift', 'ios/casting/*.swift', 'ios/contentprotection/*.swift', 'ios/pip/*.swift', 'ios/backgroundAudio/*.swift', 'ios/cache/*.swift', 'ios/sideloadedMetadata/*.swift', 'ios/eventBroadcasting/*.swift' , 'ios/ui/*.swift', 'ios/presentationMode/*.swift', 'ios/viewController/*.swift'
s.resources = ['ios/*.css']

# ReactNative Dependency
Expand Down

0 comments on commit 95b42ec

Please sign in to comment.