From 35ad67564e68dcfa92b7eba024999261297bab45 Mon Sep 17 00:00:00 2001 From: Huge_Black Date: Fri, 11 Jul 2025 11:45:56 +0800 Subject: [PATCH] Better PiP --- StikDebug.xcodeproj/project.pbxproj | 18 ++++ StikJIT/Info.plist | 4 + StikJIT/JSSupport/PiPController.swift | 55 ---------- StikJIT/JSSupport/RunJSView.swift | 5 +- StikJIT/StikJIT-Bridging-Header.h | 1 - StikJIT/Utilities/PiP/PiPController.h | 29 ------ StikJIT/Utilities/PiP/PiPController.m | 138 -------------------------- StikJIT/Views/HomeView.swift | 26 ++--- StikJIT/black.MP4 | Bin 1005 -> 0 bytes 9 files changed, 32 insertions(+), 244 deletions(-) delete mode 100644 StikJIT/JSSupport/PiPController.swift delete mode 100644 StikJIT/Utilities/PiP/PiPController.h delete mode 100644 StikJIT/Utilities/PiP/PiPController.m delete mode 100644 StikJIT/black.MP4 diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj index 479155cc..8c7afc77 100644 --- a/StikDebug.xcodeproj/project.pbxproj +++ b/StikDebug.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 17C744F02E20BED000834F17 /* Pipify in Frameworks */ = {isa = PBXBuildFile; productRef = 17C744EF2E20BED000834F17 /* Pipify */; }; 68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BD2E1B415700A5BA36 /* CodeEditorView */; }; 68D569C02E1B415700A5BA36 /* LanguageSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BF2E1B415700A5BA36 /* LanguageSupport */; }; DC139F6E2DE97EA400F63846 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC139F6D2DE97EA400F63846 /* WidgetKit.framework */; }; @@ -149,6 +150,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 17C744F02E20BED000834F17 /* Pipify in Frameworks */, 68D569C02E1B415700A5BA36 /* LanguageSupport in Frameworks */, 68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */, ); @@ -262,6 +264,7 @@ packageProductDependencies = ( 68D569BD2E1B415700A5BA36 /* CodeEditorView */, 68D569BF2E1B415700A5BA36 /* LanguageSupport */, + 17C744EF2E20BED000834F17 /* Pipify */, ); productName = StikJIT; productReference = DC6F1D372D94EADD0071B2B6 /* StikDebug.app */; @@ -371,11 +374,13 @@ knownRegions = ( en, Base, + es, ); mainGroup = DC6F1D2E2D94EADD0071B2B6; minimizedProjectReferenceProxies = 1; packageReferences = ( 68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */, + 17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */, ); preferredProjectObjectVersion = 77; productRefGroup = DC6F1D382D94EADD0071B2B6 /* Products */; @@ -970,6 +975,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/hugeBlack/swiftui-pipify"; + requirement = { + branch = main; + kind = branch; + }; + }; 68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/mchakravarty/CodeEditorView"; @@ -981,6 +994,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 17C744EF2E20BED000834F17 /* Pipify */ = { + isa = XCSwiftPackageProductDependency; + package = 17C744EE2E20BED000834F17 /* XCRemoteSwiftPackageReference "swiftui-pipify" */; + productName = Pipify; + }; 68D569BD2E1B415700A5BA36 /* CodeEditorView */ = { isa = XCSwiftPackageProductDependency; package = 68D569BC2E1B415700A5BA36 /* XCRemoteSwiftPackageReference "CodeEditorView" */; diff --git a/StikJIT/Info.plist b/StikJIT/Info.plist index dd41fa9e..ece1afd5 100644 --- a/StikJIT/Info.plist +++ b/StikJIT/Info.plist @@ -15,6 +15,10 @@ + UIBackgroundModes + + audio + UIFileSharingEnabled diff --git a/StikJIT/JSSupport/PiPController.swift b/StikJIT/JSSupport/PiPController.swift deleted file mode 100644 index ef0ba81b..00000000 --- a/StikJIT/JSSupport/PiPController.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// PiPController.swift -// StikDebug -// -// Created by Stossy11 on 10/07/2025. -// - - -import SwiftUI -import AVKit -import AVFoundation - -struct VideoPlayerView: UIViewRepresentable { - let pipController: PiPController = PiPController.shared - let view: any View - - func makeUIView(context: Context) -> UIView { - let containerView = UIView() - - // Setup player layer - let playerLayer = AVPlayerLayer() - playerLayer.frame = CGRect(x: 0, y: 0, width: 200, height: 150) - - // Load video from bundle - guard let videoURL = Bundle.main.url(forResource: "black", withExtension: "MP4") else { - print("Could not find black.MP4 in bundle") - return containerView - } - - let asset = AVAsset(url: videoURL) - let playerItem = AVPlayerItem(asset: asset) - let player = AVPlayer(playerItem: playerItem) - - playerLayer.player = player - player.isMuted = true - player.allowsExternalPlayback = true - - containerView.layer.addSublayer(playerLayer) - - // Convert SwiftUI view to UIView using UIHostingController - let hostingController = UIHostingController(rootView: AnyView(self.view)) - let swiftUIAsUIView = hostingController.view! - - // Set the converted UIView to the PiPController - pipController.customUIView = swiftUIAsUIView - - pipController.setupPiP(with: playerLayer) - - return containerView - } - - func updateUIView(_ uiView: UIView, context: Context) { - // Update UI if needed - } -} diff --git a/StikJIT/JSSupport/RunJSView.swift b/StikJIT/JSSupport/RunJSView.swift index efc1c050..4b1d11fa 100644 --- a/StikJIT/JSSupport/RunJSView.swift +++ b/StikJIT/JSSupport/RunJSView.swift @@ -69,14 +69,14 @@ class RunJSViewModel: ObservableObject { if let semaphore { semaphore.signal() } - PiPController.shared.stopPiP() DispatchQueue.main.async { if let exception = self.context?.exception { self.logs.append(exception.debugDescription) } - self.logs.append("Script Execution Completed, \nYou are safe to close the PIP Window.") + self.logs.append("Script Execution Completed") + self.logs.append("You are safe to close the PIP Window.") } } } @@ -99,6 +99,7 @@ struct RunJSViewPiP: View { .onReceive(timer) { _ in self.logs = model?.logs ?? [] } + .frame(width: 300, height: 150) } } diff --git a/StikJIT/StikJIT-Bridging-Header.h b/StikJIT/StikJIT-Bridging-Header.h index 7320cf86..c4ea98d7 100644 --- a/StikJIT/StikJIT-Bridging-Header.h +++ b/StikJIT/StikJIT-Bridging-Header.h @@ -6,4 +6,3 @@ #include "idevice/idevice.h" #include "idevice/heartbeat.h" #include "JSSupport/JSSupport.h" -#include "Utilities/PiP/PiPController.h" diff --git a/StikJIT/Utilities/PiP/PiPController.h b/StikJIT/Utilities/PiP/PiPController.h deleted file mode 100644 index 7952f4de..00000000 --- a/StikJIT/Utilities/PiP/PiPController.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// PiPController.h -// StikDebug -// -// Created by Stossy11 on 10/07/2025. -// - - -#import -#import -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface PiPController : NSObject - -@property (nonatomic, assign) BOOL isPiPActive; -@property (nonatomic, strong, nullable) UIView *customUIView; -@property (class, nonatomic, readonly) PiPController *shared; - -- (void)setupPiPWithPlayerLayer:(AVPlayerLayer *)playerLayer; -- (void)startPiP; -- (void)stopPiP; -- (void)togglePiP; - -@end - -NS_ASSUME_NONNULL_END diff --git a/StikJIT/Utilities/PiP/PiPController.m b/StikJIT/Utilities/PiP/PiPController.m deleted file mode 100644 index 83e939e8..00000000 --- a/StikJIT/Utilities/PiP/PiPController.m +++ /dev/null @@ -1,138 +0,0 @@ -// -// PiPController.m -// StikDebug -// -// Created by Stossy11 on 10/07/2025. -// - -#import "PiPController.h" - -@interface PiPController () -@property (nonatomic, strong, nullable) AVPictureInPictureController *pipController; -@property (nonatomic, strong, nullable) AVPlayerLayer *playerLayer; -@property (nonatomic, strong, nullable) UIView *customView; -@end - -@implementation PiPController - -+ (PiPController *)shared { - static PiPController *sharedInstance = nil; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - sharedInstance = [[self alloc] init]; - }); - return sharedInstance; -} - -- (instancetype)init { - self = [super init]; - if (self) { - _isPiPActive = NO; - [self setupAudioSession]; - } - return self; -} - -- (void)setupAudioSession { - NSError *error = nil; - AVAudioSession *audioSession = [AVAudioSession sharedInstance]; - - if (![audioSession setCategory:AVAudioSessionCategoryPlayback error:&error]) { - NSLog(@"Audio session setup error: %@", error.localizedDescription); - return; - } - - if (![audioSession setActive:YES withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error]) { - NSLog(@"Audio session setup error: %@", error.localizedDescription); - } -} - -- (void)setupPiPWithPlayerLayer:(AVPlayerLayer *)playerLayer { - if (![AVPictureInPictureController isPictureInPictureSupported]) { - NSLog(@"Picture in Picture not supported"); - return; - } - - self.playerLayer = playerLayer; - - self.pipController = [[AVPictureInPictureController alloc] initWithPlayerLayer:playerLayer]; - self.pipController.delegate = self; - - // Set controls style (equivalent to setValue:forKey: in Swift) - if ([self.pipController respondsToSelector:@selector(setControlsStyle:)]) { - [self.pipController setValue:@(1) forKey:@"controlsStyle"]; - } -} - -- (void)startPiP { - if (self.pipController.isPictureInPictureActive) return; - - [self.pipController startPictureInPicture]; -} - -- (void)stopPiP { - if (!self.pipController.isPictureInPictureActive) return; - - [self.pipController stopPictureInPicture]; -} - -- (void)togglePiP { - if (self.isPiPActive) { - [self stopPiP]; - } else { - [self startPiP]; - } -} - -#pragma mark - AVPictureInPictureControllerDelegate - -- (void)pictureInPictureControllerWillStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - if (!self.customUIView) { - NSLog(@"Fatal error: customUIView = nil"); - return; - } - - dispatch_async(dispatch_get_main_queue(), ^{ - self.isPiPActive = YES; - }); - - self.customView = self.customUIView; - - UIWindow *window = [UIApplication sharedApplication].windows.firstObject; - if (window && self.customView) { - self.customView.backgroundColor = [UIColor clearColor]; - [window addSubview:self.customView]; - - self.customView.translatesAutoresizingMaskIntoConstraints = NO; - [NSLayoutConstraint activateConstraints:@[ - [self.customView.topAnchor constraintEqualToAnchor:window.topAnchor], - [self.customView.leadingAnchor constraintEqualToAnchor:window.leadingAnchor], - [self.customView.trailingAnchor constraintEqualToAnchor:window.trailingAnchor], - [self.customView.bottomAnchor constraintEqualToAnchor:window.bottomAnchor] - ]]; - } -} - - -- (void)pictureInPictureControllerDidStartPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - NSLog(@"PiP started"); -} - -- (void)pictureInPictureControllerDidStopPictureInPicture:(AVPictureInPictureController *)pictureInPictureController { - dispatch_async(dispatch_get_main_queue(), ^{ - self.isPiPActive = NO; - }); - - // Remove custom view - [self.customView removeFromSuperview]; - self.customView = nil; -} - -- (void)pictureInPictureController:(AVPictureInPictureController *)pictureInPictureController failedToStartPictureInPictureWithError:(NSError *)error { - NSLog(@"Failed to start PiP: %@", error.localizedDescription); - dispatch_async(dispatch_get_main_queue(), ^{ - self.isPiPActive = NO; - }); -} - -@end diff --git a/StikJIT/Views/HomeView.swift b/StikJIT/Views/HomeView.swift index e062d0c8..89f78ffd 100644 --- a/StikJIT/Views/HomeView.swift +++ b/StikJIT/Views/HomeView.swift @@ -7,6 +7,7 @@ import SwiftUI import UniformTypeIdentifiers +import Pipify extension UIDocumentPickerViewController { @objc func fix_init(forOpeningContentTypes contentTypes: [UTType], asCopy: Bool) -> UIDocumentPickerViewController { @@ -59,19 +60,6 @@ struct HomeView: View { Color(colorScheme == .dark ? .black : .white) .edgesIgnoringSafeArea(.all) - if useDefaultScript { - VideoPlayerView(view: RunJSViewPiP(model: $jsModel)) - .frame(width: 200, height: 150) - .background(Color.clear) - .cornerRadius(10) - .onAppear() { - Timer.scheduledTimer(withTimeInterval: 0.3, repeats: false) { _ in - PiPController.shared.startPiP() - } - } - .overlay(colorScheme == .dark ? .black : .white) - } - VStack(spacing: 25) { Spacer() VStack(spacing: 5) { @@ -237,12 +225,7 @@ struct HomeView: View { .onReceive(timer) { _ in refreshBackground() checkPairingFileExists() - - if useDefaultScript { - PiPController.shared.startPiP() - } else { - PiPController.shared.stopPiP() - } + } .fileImporter(isPresented: $isShowingPairingFilePicker, allowedContentTypes: [UTType(filenameExtension: "mobiledevicepairing", conformingTo: .data)!, .propertyList]) {result in switch result { @@ -320,6 +303,9 @@ struct HomeView: View { startJITInBackground(with: selectedBundle) } } + .pipify(isPresented: $isProcessing) { + RunJSViewPiP(model: $jsModel) + } .sheet(isPresented: $scriptViewShow) { NavigationView { if let jsModel { @@ -328,6 +314,7 @@ struct HomeView: View { ToolbarItem(placement: .topBarTrailing) { Button("Done") { scriptViewShow = false + isProcessing = false } } } @@ -433,6 +420,7 @@ struct HomeView: View { DispatchQueue.global(qos: .background).async { do { try jsModel?.runScript(path: selectedScriptURL) + isProcessing = false } catch { showAlert(title: "Error Occurred While Executing the Default Script.".localized, message: error.localizedDescription, showOk: true) } diff --git a/StikJIT/black.MP4 b/StikJIT/black.MP4 deleted file mode 100644 index a9b395941efa2ca9a0099a3d74eb7d13da2a80b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1005 zcmZQzV30{GsVvAXFfn3aU|`HF&d&w14PpG;l*AICI0$?IVmUTeDfck1J25`a_EX*; z6|}kNyn^xWnKO5p7&u-wGyqjptL{)>{r{1v_)lj0vyNl+Th5#BUmkumF(h@po}U8) z12d!5mP7mNYM<<%73AEi7-{IDjV;(Tf#u~E1A)t#$NlCg3N5@6Cg!uZqkK!7|AV|xpy>?KiVrzl z{xh((De`Lni{gJ6^`ONv-+7*5Zt$&-KxrApM*?pDpE!C7AMyLEWz<`9Vu~Q|tYcF* zPx43zW?*1?n46zp22_`mTb7Xmq#161FgXRnK)DD81~y&>MqnU;BwzrA0~AE#JHXUo zrTO0vOrG9v7RnFh2M9iZ6*QwdY%0Aw)E0}>a&A}XmVCB;CNOlnRs zEIxrIfXoH@3v3qS)7+HIM4%dl+!Snn=SU4b0yfJqBPFK@%mNZ+nJK9-Hi&l4FG^ML zO$C~y5C)XW2MRIvmWV5ooyBassS{{wm2KvSI= zQy6&vKV%S42hwZ84ldlcbA^C_sDc9{1B=Exn+<<|`~c}wC@ukqDu@k?Ne~955|CBQ zTY