diff --git a/VoidLink/Base.lproj/iPad.storyboard b/VoidLink/Base.lproj/iPad.storyboard index 4bd30eb5..561911a1 100644 --- a/VoidLink/Base.lproj/iPad.storyboard +++ b/VoidLink/Base.lproj/iPad.storyboard @@ -60,7 +60,7 @@ - + @@ -969,16 +969,38 @@ + + + + + + + + + + + + + + - + + + @@ -1076,7 +1098,7 @@ - + diff --git a/VoidLink/Base.lproj/iPhone.storyboard b/VoidLink/Base.lproj/iPhone.storyboard index 5294dce9..3ec54b5e 100644 --- a/VoidLink/Base.lproj/iPhone.storyboard +++ b/VoidLink/Base.lproj/iPhone.storyboard @@ -1541,6 +1541,25 @@ + + + + + + + + + + + + + @@ -1551,6 +1570,8 @@ + + diff --git a/VoidLink/Database/DataManager.h b/VoidLink/Database/DataManager.h index 8648762b..dcf52f0a 100644 --- a/VoidLink/Database/DataManager.h +++ b/VoidLink/Database/DataManager.h @@ -79,6 +79,7 @@ typedef NS_ENUM(NSInteger, SettingsMenuMode) { useFramePacing:(BOOL)useFramePacing enableHdr:(BOOL)enableHdr btMouseSupport:(BOOL)btMouseSupport + // absoluteTouchMode:(BOOL)absoluteTouchMode touchMode:(NSInteger)touchMode statsOverlayLevel:(NSInteger)statsOverlayLevel statsOverlayEnabled:(BOOL)statsOverlayEnabled @@ -90,6 +91,7 @@ typedef NS_ENUM(NSInteger, SettingsMenuMode) { enableGraphs:(BOOL)enableGraphs graphOpacity:(NSInteger)graphOpacity renderingBackend:(NSInteger)renderingBackend; + backgroundSessionTimer:(NSInteger)backgroundSessionTimer; - (NSArray*) getHosts; - (void) updateHost:(TemporaryHost*)host; diff --git a/VoidLink/Database/DataManager.m b/VoidLink/Database/DataManager.m index e92c9215..15fe83e5 100644 --- a/VoidLink/Database/DataManager.m +++ b/VoidLink/Database/DataManager.m @@ -97,7 +97,8 @@ enableGraphs:(BOOL)enableGraphs graphOpacity:(NSInteger)graphOpacity renderingBackend:(NSInteger)renderingBackend -{ + backgroundSessionTimer:(NSInteger)backgroundSessionTimer{ + [_managedObjectContext performBlockAndWait:^{ Settings* settingsToSave = [self retrieveSettings]; settingsToSave.framerate = [NSNumber numberWithInteger:framerate]; @@ -137,6 +138,7 @@ settingsToSave.resolutionSelected = [NSNumber numberWithInteger:resolutionSelected]; settingsToSave.externalDisplayMode = [NSNumber numberWithInteger:externalDisplayMode]; settingsToSave.localMousePointerMode = [NSNumber numberWithInteger:localMousePointerMode]; + settingsToSave.backroundSessionTimer = [NSNumber numberWithInteger:backgroundSessionTimer]; settingsToSave.frameQueueSize = [NSNumber numberWithInteger:frameQueueSize]; settingsToSave.enableGraphs = enableGraphs; diff --git a/VoidLink/Database/TemporarySettings.h b/VoidLink/Database/TemporarySettings.h index a4f5c966..08105bc8 100644 --- a/VoidLink/Database/TemporarySettings.h +++ b/VoidLink/Database/TemporarySettings.h @@ -39,6 +39,7 @@ @property (nonatomic, retain) NSNumber * resolutionSelected; @property (nonatomic, retain) NSNumber * externalDisplayMode; @property (nonatomic, retain) NSNumber * localMousePointerMode; +@property (nonatomic, retain) NSNumber * backgroundSessionTimer; @property (nonatomic) enum { CODEC_PREF_AUTO, CODEC_PREF_H264, diff --git a/VoidLink/Database/TemporarySettings.m b/VoidLink/Database/TemporarySettings.m index 469c0fc1..ac1a87ff 100644 --- a/VoidLink/Database/TemporarySettings.m +++ b/VoidLink/Database/TemporarySettings.m @@ -120,6 +120,7 @@ self.enableGraphs = settings.enableGraphs; self.graphOpacity = settings.graphOpacity; self.renderingBackend = settings.renderingBackend; + self.backgroundSessionTimer = settings.backroundSessionTimer; #endif self.uniqueId = settings.uniqueId; diff --git a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents index 1aae1e21..6bfa5d3c 100644 --- a/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents +++ b/VoidLink/Limelight.xcdatamodeld/VoidLink v1.0.xcdatamodel/contents @@ -23,12 +23,13 @@ + - + diff --git a/VoidLink/Localization/Localizable.xcstrings b/VoidLink/Localization/Localizable.xcstrings index 730e22b9..ab4b6d92 100644 --- a/VoidLink/Localization/Localizable.xcstrings +++ b/VoidLink/Localization/Localizable.xcstrings @@ -47,6 +47,75 @@ } } }, + " disconnect " : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " 断开 " + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : " 斷開 " + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : " 斷開 " + } + } + } + }, + " keep %d min " : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " 保持%d分钟 " + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : " 保持%d分鐘 " + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : " 保持%d分鐘 " + } + } + } + }, + " keep alive " : { + "extractionState" : "manual", + "localizations" : { + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : " 保活 " + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : " 保持連線 " + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : " 保持連線 " + } + } + } + }, " Launch" : { "extractionState" : "manual", "localizations" : { diff --git a/VoidLink/ViewControllers/MainFrameViewController.m b/VoidLink/ViewControllers/MainFrameViewController.m index 3ed807bd..8d229f7e 100644 --- a/VoidLink/ViewControllers/MainFrameViewController.m +++ b/VoidLink/ViewControllers/MainFrameViewController.m @@ -74,6 +74,7 @@ SettingsViewController* settingsViewController; StreamFrameViewController* streamFrameViewController; id navBarAppearanceStandard; +bool _viewJustAppeared; NSTimer *_foregroundHostUpdateTimer; @@ -207,7 +208,7 @@ static NSMutableSet* hostList; AppListResponse* appListResp = [ConnectionHelper getAppListForHost:host]; [self->_discMan resumeDiscoveryForHost:host]; - + if (![appListResp isStatusOk] || [appListResp getAppList] == nil) { Log(LOG_W, @"Failed to get applist: %@", appListResp.statusMessage); dispatch_async(dispatch_get_main_queue(), ^{ @@ -230,7 +231,7 @@ static NSMutableSet* hostList; } else { dispatch_async(dispatch_get_main_queue(), ^{ [self updateApplist:[appListResp getAppList] forHost:host]; - + if (host != self->_selectedHost) { [self hideLoadingFrame: nil]; return; @@ -248,7 +249,7 @@ static NSMutableSet* hostList; - (void) updateAppEntry:(TemporaryApp*)app forHost:(TemporaryHost*)host { DataManager* database = [[DataManager alloc] init]; NSMutableSet* newHostAppList = [NSMutableSet setWithSet:host.appList]; - + for (TemporaryApp* savedApp in newHostAppList) { if ([app.id isEqualToString:savedApp.id]) { savedApp.name = app.name; @@ -256,7 +257,7 @@ static NSMutableSet* hostList; savedApp.hidden = app.hidden; host.appList = newHostAppList; - + [database updateAppsForExistingHost:host]; return; } @@ -315,7 +316,7 @@ static NSMutableSet* hostList; } while (appWasRemoved); host.appList = newHostAppList; - + [database updateAppsForExistingHost:host]; // This host may be eligible for a shortcut now that the app list @@ -573,11 +574,11 @@ static NSMutableSet* hostList; - (UIViewController*) activeViewController { UIViewController *topController = [UIApplication sharedApplication].keyWindow.rootViewController; - + while (topController.presentedViewController) { topController = topController.presentedViewController; } - + return topController; } @@ -605,7 +606,7 @@ static NSMutableSet* hostList; message = [LocalizationHelper localizedStringForKey:@"Online - Not Paired"]; } break; - + case StateUnknown: message = [LocalizationHelper localizedStringForKey:@"Connecting"]; break; @@ -816,12 +817,12 @@ static NSMutableSet* hostList; _streamConfig.asyncNativeTouchPriority = streamSettings.asyncNativeTouchPriority; // new streamConfig segment _streamConfig.gyroMode = [streamSettings.gyroMode intValue]; //NSLog(@"gyroMode from settings: %ld", _streamConfig.gyroMode); - + // multiController must be set before calling getConnectedGamepadMask _streamConfig.multiController = streamSettings.multiController; _streamConfig.gamepadMask = [ControllerSupport getConnectedGamepadMask:_streamConfig]; _streamConfig.localMousePointerMode = streamSettings.localMousePointerMode.intValue; - + // Probe for supported channel configurations int physicalOutputChannels = (int)[AVAudioSession sharedInstance].maximumOutputNumberOfChannels; Log(LOG_I, @"Audio device supports %d channels", physicalOutputChannels); @@ -841,7 +842,7 @@ static NSMutableSet* hostList; else { _streamConfig.audioConfiguration = AUDIO_CONFIGURATION_STEREO; } - + switch (streamSettings.preferredCodec) { case CODEC_PREF_AV1: @@ -1209,7 +1210,7 @@ static NSMutableSet* hostList; - (BOOL)isFullScreenRequired { NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; NSNumber *requiresFullScreen = infoDictionary[@"UIRequiresFullScreen"]; - + if (requiresFullScreen != nil) { return [requiresFullScreen boolValue]; } @@ -1229,7 +1230,7 @@ static NSMutableSet* hostList; self->waterMark.text = [LocalizationHelper localizedStringForKey:@"waterMarkText"]; CGFloat labelHeight = 60; // the app is unable to automatically lock screen orientation in app window resizable mode(aka. not require fullscreen) - + // if(![self isFullScreenRequired]){ if(false){ NSString* screenRotationTip = [LocalizationHelper localizedStringForKey:@"screenRotationTIp"]; @@ -1238,7 +1239,7 @@ static NSMutableSet* hostList; self->waterMark.font = [UIFont systemFontOfSize:19]; labelHeight = 80; } - + self->waterMark.textColor = UIColor.blackColor; self->waterMark.alpha = 0.35; self->waterMark.textAlignment = NSTextAlignmentCenter; @@ -1451,7 +1452,7 @@ static NSMutableSet* hostList; self.settingsExpandedInStreamView = false; // init this flag self.revealViewController.isStreaming = false; //init this flag for rvlVC self.revealViewController.mainFrameIsInHostView = true; - + [self setupNavBar]; // Set the gesture @@ -1515,15 +1516,16 @@ static NSMutableSet* hostList; // [self.view addSubview:hostScrollView]; // if ([hostList count] == 1) [self hostClicked:[hostList anyObject] view:nil]; // auto click for single host + //if([SettingsViewController isLandscapeNow] != _streamConfig.width > _streamConfig.height) //[self simulateSettingsButtonPress]; //force expand setting view if orientation changed since last quit from app. //[self simulateSettingsButtonPress]; //force expand setting view if orientation changed since last quit from app. //[self updateResolutionAccordingly]; - + // SettingsViewController* settingsViewController = (SettingsViewController*)[self.revealViewController rearViewController]; // [settingsViewController updateResolutionTable]; - + UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(handleMenuResize:)]; [self.view addGestureRecognizer:longPress]; @@ -1542,7 +1544,7 @@ static NSMutableSet* hostList; // this will also be called back when device orientation changes //- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id)coordinator { // [super viewWillTransitionToSize:size withTransitionCoordinator:coordinator]; -// +// // double delayInSeconds = 0.7; // // Convert the delay into a dispatch_time_t value // dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); @@ -1561,7 +1563,7 @@ static NSMutableSet* hostList; CGFloat appWindowHeight = CGRectGetHeight(window.frame) * screenScale; CGFloat screenWidthInPoints = CGRectGetWidth([[UIScreen mainScreen] bounds]); CGFloat screenHeightInPoints = CGRectGetHeight([[UIScreen mainScreen] bounds]); - + if(currentSettings.externalDisplayMode.intValue == 1 && UIScreen.screens.count > 1){ CGRect bounds = [UIScreen.screens.lastObject bounds]; screenScale = [UIScreen.screens.lastObject scale]; @@ -1570,7 +1572,7 @@ static NSMutableSet* hostList; } bool needSwap = false; - + if([self isFullScreenRequired]){ // if force fullscreen is enabled in app bundle, we use screen bounds to tell if a swap between width & height is needed needSwap = (currentSettings.width.floatValue - currentSettings.height.floatValue) * (screenWidthInPoints - screenHeightInPoints) < 0; //update the current resolution accordingly NSLog(@"need to swap width & height (non-app window mode): %d", needSwap); @@ -1623,15 +1625,19 @@ static NSMutableSet* hostList; -(void)beginForegroundRefresh { - if (!_background) { + if (!_background || _viewJustAppeared) { // This will kick off box art caching + _viewJustAppeared = false; + [_foregroundHostUpdateTimer invalidate]; + _foregroundHostUpdateTimer = nil; + _foregroundHostUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:5 repeats:YES block:^(NSTimer *timer) { [self updateHosts]; }]; - + [_discMan startDiscovery]; // This will refresh the applist when a paired host is selected @@ -1692,6 +1698,10 @@ static NSMutableSet* hostList; { [super viewDidAppear:NO]; + _viewJustAppeared = true; + + [self beginForegroundRefresh]; + // [self setupHostViewTitle]; // [self reloadScrollHostView]; //remove this for proper test [self attachWaterMark]; @@ -1701,7 +1711,7 @@ static NSMutableSet* hostList; selector:@selector(handleOrientationChange) // //force expand settings view to update resolution table, and all setting includes current fullscreen resolution will be updated. name:UIDeviceOrientationDidChangeNotification object:nil]; - + [[self revealViewController] setPrimaryViewController:self]; self.revealViewController.isStreaming = false; // tell the revealViewController streaming is finished //[self.settingsButton setEnabled:![self isIPhonePortrait]]; //make sure settings button is disabled in iphone portrait mode. @@ -1772,8 +1782,6 @@ static NSMutableSet* hostList; [self retrieveSavedHosts]; _discMan = [[DiscoveryManager alloc] initWithHosts:[hostList allObjects] andCallback:self]; - - [self beginForegroundRefresh]; } - (void)viewDidDisappear:(BOOL)animated @@ -1863,26 +1871,7 @@ static NSMutableSet* hostList; NSArray* sortedHostList = [[hostList allObjects] sortedArrayUsingSelector:@selector(compareName:)]; for (TemporaryHost* comp in sortedHostList) { -if(comp.state == StateOnline || comp.pairState == PairStatePaired || comp.pairState == PairStateUnknown) [self.hostCollectionVC addHost:comp]; - - // new host card test - // if([comp.name isEqualToString: @"ASRockPC"]) { - /* - if([comp.name isEqualToString: @"ASRockPC"]) { - //if([comp.name isEqualToString: @"PianoServer"]) { - //if([comp.name isEqualToString: @"TrueZj"]) { - HostCardView *testCard = [[HostCardView alloc] initWithHost:comp]; - - [self.view addSubview:testCard]; - // [testCard resizeBySizeFactor:3]; - [NSLayoutConstraint activateConstraints:@[ - [testCard.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor constant:0], - [testCard.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor constant:150], - ]]; - - }*/ - // - + if(comp.state == StateOnline || comp.pairState == PairStatePaired || comp.pairState == PairStateUnknown) [self.hostCollectionVC addHost:comp]; // Start jobs to decode the box art in advance for (TemporaryApp* app in comp.appList) { @@ -1898,7 +1887,7 @@ if(comp.state == StateOnline || comp.pairState == PairStatePaired || comp.pairSt // Update the title in case we now have a PC [self updateTitle]; - + // Reset state first so we can rediscover hosts that were deleted before [_discMan resetDiscoveryState]; } diff --git a/VoidLink/ViewControllers/SettingsViewController.h b/VoidLink/ViewControllers/SettingsViewController.h index af4150d6..ae9cec6b 100644 --- a/VoidLink/ViewControllers/SettingsViewController.h +++ b/VoidLink/ViewControllers/SettingsViewController.h @@ -119,6 +119,9 @@ @property (strong, nonatomic) IBOutlet UIStepper *graphOpacityStepper; @property (strong, nonatomic) IBOutlet UISegmentedControl *renderingBackendSelector; +@property (strong, nonatomic) IBOutlet UIStackView *backgroundSessionTimerStack; +@property (strong, nonatomic) IBOutlet UISlider *backgroundSessionTimerSlider; + #pragma clang diagnostic push diff --git a/VoidLink/ViewControllers/SettingsViewController.m b/VoidLink/ViewControllers/SettingsViewController.m index 800756a6..778d8299 100644 --- a/VoidLink/ViewControllers/SettingsViewController.m +++ b/VoidLink/ViewControllers/SettingsViewController.m @@ -557,6 +557,7 @@ BOOL isCustomResolution(CGSize res) { } [self addSetting:self.statsOverlayStack ofId:@"statsOverlayStack" withInfoTag:NO withDynamicLabel:NO to:otherSection]; [self addSetting:self.unlockDisplayOrientationStack ofId:@"unlockDisplayOrientationStack" withInfoTag:YES withDynamicLabel:NO to:otherSection]; + [self addSetting:self.backgroundSessionTimerStack ofId:@"backgroundSessionTimerStack" withInfoTag:NO withDynamicLabel:YES to:otherSection]; [self addSetting:self.optimizeGamesStack ofId:@"optimizeGamesStack" withInfoTag:YES withDynamicLabel:NO to:otherSection]; [self addSetting:self.multiControllerStack ofId:@"multiControllerStack" withInfoTag:YES withDynamicLabel:NO to:otherSection]; [self addSetting:self.softKeyboardToolbarStack ofId:@"softKeyboardToolbarStack" withInfoTag:YES withDynamicLabel:NO to:otherSection]; @@ -1328,6 +1329,9 @@ BOOL isCustomResolution(CGSize res) { else [self.unlockDisplayOrientationSelector setSelectedSegmentIndex:1]; // can't lock screen orientation in this mode = Display Orientation always unlocked [self.unlockDisplayOrientationSelector setEnabled:unlockDisplayOrientationSelectorEnabled]; + [self.backgroundSessionTimerSlider setValue:(uint32_t)currentSettings.backgroundSessionTimer.floatValue]; + [self.backgroundSessionTimerSlider addTarget:self action:@selector(backgroundSessionTimerSliderMoved:) forControlEvents:UIControlEventValueChanged]; + [self backgroundSessionTimerSliderMoved:self.backgroundSessionTimerSlider]; // lift streamview setting [self.liftStreamViewForKeyboardSelector setSelectedSegmentIndex:currentSettings.liftStreamViewForKeyboard ? 1 : 0];// Load old setting @@ -1542,6 +1546,14 @@ BOOL isCustomResolution(CGSize res) { [self findDynamicLabelFromStack:self.gyroSensitivityStack].text = [NSString stringWithFormat:@" %d%% ", (uint16_t)sender.value]; // Update label display } +- (void) backgroundSessionTimerSliderMoved:(UISlider* )sender { + NSString* labelString; + labelString = [LocalizationHelper localizedStringForKey:@" keep %d min ", (uint16_t)sender.value]; + if(sender.value == 0) labelString = [LocalizationHelper localizedStringForKey:@" disconnect "]; + if(sender.value == sender.maximumValue) labelString = [LocalizationHelper localizedStringForKey:@" keep alive "]; + [self findDynamicLabelFromStack:self.backgroundSessionTimerStack].text = labelString; // Update label display +} + // veloc factor upto 700% - (uint16_t) map_velocFactorDisplay_fromSliderValue:(CGFloat)sliderValue{ @@ -2024,6 +2036,7 @@ BOOL isCustomResolution(CGSize res) { NSInteger resolutionSelected = [self.resolutionSelector selectedSegmentIndex]; NSInteger externalDisplayMode = [self.externalDisplayModeSelector selectedSegmentIndex]; NSInteger localMousePointerMode = [self.localMousePointerModeSelector selectedSegmentIndex]; + NSInteger backgroundSessionTimer = self.backgroundSessionTimerSlider.value == self.backgroundSessionTimerSlider.maximumValue ? (uint32_t) INT16_MAX : (uint32_t)self.backgroundSessionTimerSlider.value; [dataMan saveSettingsWithBitrate:_bitrate framerate:framerate height:height @@ -2065,6 +2078,7 @@ BOOL isCustomResolution(CGSize res) { enableGraphs:enableGraphs graphOpacity:_graphOpacity renderingBackend:renderingBackend]; + backgroundSessionTimer:backgroundSessionTimer]; } - (void)didReceiveMemoryWarning { diff --git a/VoidLink/ViewControllers/StreamFrameViewController.m b/VoidLink/ViewControllers/StreamFrameViewController.m index 1c4e44dd..708325e3 100644 --- a/VoidLink/ViewControllers/StreamFrameViewController.m +++ b/VoidLink/ViewControllers/StreamFrameViewController.m @@ -118,7 +118,7 @@ self.imguiView.mtkView.hidden = NO; Log(LOG_I, @"Showing ImGui view after PiP stop."); } - + if (!_isRestoringFromPiP) { [self returnToMainFrame]; } @@ -361,7 +361,7 @@ if (self.imguiView && self.imguiView.mtkView.superview) { [self.view bringSubviewToFront:self.imguiView.mtkView]; } - + NSLog(@"frameview gestures: %d", (uint32_t)[self.view.gestureRecognizers count]); NSLog(@"streamview gestures: %d", (uint32_t)[_streamView.gestureRecognizers count]); } @@ -819,7 +819,7 @@ [_statsUpdateTimer invalidate]; _statsUpdateTimer = nil; - + [self.navigationController popToRootViewControllerAnimated:YES]; _extWindow = nil; @@ -901,21 +901,8 @@ //[self.pipController startPictureInPicture]; //sleep(1); - - if (_inactivityTimer != nil) { - [_inactivityTimer invalidate]; - _inactivityTimer = nil; - } - - + #if !TARGET_OS_TV - // Terminate the stream if the app is inactive for 60 seconds - Log(LOG_I, @"Starting inactivity termination timer"); - _inactivityTimer = [NSTimer scheduledTimerWithTimeInterval:18000 - target:self - selector:@selector(inactiveTimerExpired:) - userInfo:nil - repeats:NO]; #endif } @@ -928,13 +915,13 @@ } - (void)applicationDidBecomeActive:(NSNotification *)notification { - // Stop the background timer + // Stop the background timer, since we're foregrounded again if (_inactivityTimer != nil) { Log(LOG_I, @"Stopping inactivity timer after becoming active again"); [_inactivityTimer invalidate]; _inactivityTimer = nil; } - + // Check if we were in PiP if (self.pipController && self.pipController.isPictureInPictureActive) { [self.pipController stopPictureInPicture]; @@ -943,7 +930,7 @@ if (_isRestoringFromPiP) { [self->_streamMan.videoRenderer resetFramePacing]; } - + _isRestoringFromPiP = NO; } @@ -952,10 +939,22 @@ NSLog(@"did enter background, %d, %@, %d", _settings.enablePIP, self.pipController, self.pipController.isPictureInPictureActive); if (_settings.enablePIP && self.pipController && self.pipController.isPictureInPictureActive) { - Log(LOG_I, @"PIP is active, not terminating stream"); - return; + //Log(LOG_I, @"PIP is active, not terminating stream"); } + if (_inactivityTimer != nil) { + [_inactivityTimer invalidate]; + _inactivityTimer = nil; + } + + // Terminate the stream if the app is inactive for ... + Log(LOG_I, @"Starting inactivity termination timer with %d min", _settings.backgroundSessionTimer.intValue); + _inactivityTimer = [NSTimer scheduledTimerWithTimeInterval:60*(double)_settings.backgroundSessionTimer.intValue + target:self + selector:@selector(inactiveTimerExpired:) + userInfo:nil + repeats:NO]; + #if !TARGET_OS_TV #endif @@ -1204,7 +1203,7 @@ if (@available(tvOS 11.2, *)) { UIWindow* window = [[[UIApplication sharedApplication] delegate] window]; AVDisplayManager* displayManager = [window avDisplayManager]; - + // This logic comes from Kodi and MrMC if (streamActive) { int dynamicRange; @@ -1215,7 +1214,7 @@ else { dynamicRange = 0; // SDR } - + AVDisplayCriteria* displayCriteria = [[AVDisplayCriteria alloc] initWithRefreshRate:[_settings.framerate floatValue] videoDynamicRange:dynamicRange]; displayManager.preferredDisplayCriteria = displayCriteria; @@ -1392,7 +1391,7 @@ Log(LOG_I, @"View size changed during PiP restore, skipping redundant reconfiguration."); return; } - + Log(LOG_I, @"View size changed, terminating stream"); double delayInSeconds = 0.2; diff --git a/VoidLink/mul.lproj/iPad.xcstrings b/VoidLink/mul.lproj/iPad.xcstrings index 053fc628..0870c224 100644 --- a/VoidLink/mul.lproj/iPad.xcstrings +++ b/VoidLink/mul.lproj/iPad.xcstrings @@ -349,6 +349,36 @@ } } }, + "9pX-93-bC2.text" : { + "comment" : "Class = \"UILabel\"; text = \"Background Session\"; ObjectID = \"9pX-93-bC2\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Background Session" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "后台会话" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "背景執行" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "背景執行" + } + } + } + }, "9XL-xt-Q57.text" : { "comment" : "Class = \"UILabel\"; text = \"Gyro Sensitivity\"; ObjectID = \"9XL-xt-Q57\";", "extractionState" : "extracted_with_value", diff --git a/VoidLink/mul.lproj/iPhone.xcstrings b/VoidLink/mul.lproj/iPhone.xcstrings index 49375568..d7694217 100644 --- a/VoidLink/mul.lproj/iPhone.xcstrings +++ b/VoidLink/mul.lproj/iPhone.xcstrings @@ -727,6 +727,36 @@ } } }, + "9et-1g-y3D.text" : { + "comment" : "Class = \"UILabel\"; text = \"Background Session\"; ObjectID = \"9et-1g-y3D\";", + "extractionState" : "extracted_with_value", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "new", + "value" : "Background Session" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "后台会话" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "背景執行" + } + }, + "zh-HK" : { + "stringUnit" : { + "state" : "translated", + "value" : "背景執行" + } + } + } + }, "9Gg-66-UdB.text" : { "comment" : "Class = \"UILabel\"; text = \"Tap any widget to load configuration (per widget)\"; ObjectID = \"9Gg-66-UdB\";", "extractionState" : "extracted_with_value",