mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-06-17 22:23:52 +00:00
Add support for hiding apps
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
@property (nullable, nonatomic, retain) NSString *name;
|
@property (nullable, nonatomic, retain) NSString *name;
|
||||||
@property (nullable, nonatomic, retain) NSString *installPath;
|
@property (nullable, nonatomic, retain) NSString *installPath;
|
||||||
@property (nonatomic) BOOL hdrSupported;
|
@property (nonatomic) BOOL hdrSupported;
|
||||||
|
@property (nonatomic) BOOL hidden;
|
||||||
@property (nullable, nonatomic, retain) TemporaryHost *host;
|
@property (nullable, nonatomic, retain) TemporaryHost *host;
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
self.id = app.id;
|
self.id = app.id;
|
||||||
self.name = app.name;
|
self.name = app.name;
|
||||||
self.hdrSupported = app.hdrSupported;
|
self.hdrSupported = app.hdrSupported;
|
||||||
|
self.hidden = app.hidden;
|
||||||
self.host = tempHost;
|
self.host = tempHost;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
parent.id = self.id;
|
parent.id = self.id;
|
||||||
parent.name = self.name;
|
parent.name = self.name;
|
||||||
parent.hdrSupported = self.hdrSupported;
|
parent.hdrSupported = self.hdrSupported;
|
||||||
|
parent.hidden = self.hidden;
|
||||||
parent.host = host;
|
parent.host = host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17192" systemVersion="19H2" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17192" systemVersion="19H2" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||||
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
|
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
|
||||||
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
|
<attribute name="hidden" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||||
<attribute name="id" attributeType="String" syncable="YES"/>
|
<attribute name="id" attributeType="String" syncable="YES"/>
|
||||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||||
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
|
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
|
||||||
@@ -36,7 +37,7 @@
|
|||||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
|
<element name="App" positionX="0" positionY="54" width="128" height="118"/>
|
||||||
<element name="Host" positionX="0" positionY="0" width="128" height="210"/>
|
<element name="Host" positionX="0" positionY="0" width="128" height="210"/>
|
||||||
<element name="Settings" positionX="0" positionY="0" width="128" height="268"/>
|
<element name="Settings" positionX="0" positionY="0" width="128" height="268"/>
|
||||||
</elements>
|
</elements>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
@protocol AppCallback <NSObject>
|
@protocol AppCallback <NSObject>
|
||||||
|
|
||||||
- (void) appClicked:(TemporaryApp*) app;
|
- (void) appClicked:(TemporaryApp*) app;
|
||||||
|
- (void) appLongClicked:(TemporaryApp*) app;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|||||||
+15
-1
@@ -9,7 +9,7 @@
|
|||||||
#import "UIAppView.h"
|
#import "UIAppView.h"
|
||||||
#import "AppAssetManager.h"
|
#import "AppAssetManager.h"
|
||||||
|
|
||||||
static const float REFRESH_CYCLE = 2.0f;
|
static const float REFRESH_CYCLE = 1.0f;
|
||||||
|
|
||||||
@implementation UIAppView {
|
@implementation UIAppView {
|
||||||
TemporaryApp* _app;
|
TemporaryApp* _app;
|
||||||
@@ -40,10 +40,15 @@ static UIImage* noImage;
|
|||||||
self.frame = CGRectMake(0, 0, 150, 200);
|
self.frame = CGRectMake(0, 0, 150, 200);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
[self setAlpha:app.hidden ? 0.4 : 1.0];
|
||||||
|
|
||||||
_appImage = [[UIImageView alloc] initWithFrame:self.frame];
|
_appImage = [[UIImageView alloc] initWithFrame:self.frame];
|
||||||
[_appImage setImage:noImage];
|
[_appImage setImage:noImage];
|
||||||
[self addSubview:_appImage];
|
[self addSubview:_appImage];
|
||||||
|
|
||||||
|
UILongPressGestureRecognizer* longPressRecognizer = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(appLongClicked:)];
|
||||||
|
[self addGestureRecognizer:longPressRecognizer];
|
||||||
|
|
||||||
if (@available(iOS 9.0, tvOS 9.0, *)) {
|
if (@available(iOS 9.0, tvOS 9.0, *)) {
|
||||||
[self addTarget:self action:@selector(appClicked) forControlEvents:UIControlEventPrimaryActionTriggered];
|
[self addTarget:self action:@selector(appClicked) forControlEvents:UIControlEventPrimaryActionTriggered];
|
||||||
}
|
}
|
||||||
@@ -78,6 +83,12 @@ static UIImage* noImage;
|
|||||||
[_callback appClicked:_app];
|
[_callback appClicked:_app];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void) appLongClicked:(UILongPressGestureRecognizer*)gesture {
|
||||||
|
if (gesture.state == UIGestureRecognizerStateBegan) {
|
||||||
|
[_callback appLongClicked:_app];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void) updateAppImage {
|
- (void) updateAppImage {
|
||||||
if (_appOverlay != nil) {
|
if (_appOverlay != nil) {
|
||||||
[_appOverlay removeFromSuperview];
|
[_appOverlay removeFromSuperview];
|
||||||
@@ -185,6 +196,9 @@ static UIImage* noImage;
|
|||||||
[self updateAppImage];
|
[self updateAppImage];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update opacity if neccessary
|
||||||
|
[self setAlpha:_app.hidden ? 0.4 : 1.0];
|
||||||
|
|
||||||
// Stop updating when we detach from our parent view
|
// Stop updating when we detach from our parent view
|
||||||
if (self.superview != nil) {
|
if (self.superview != nil) {
|
||||||
[self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
|
[self performSelector:@selector(updateLoop) withObject:self afterDelay:REFRESH_CYCLE];
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
@implementation MainFrameViewController {
|
@implementation MainFrameViewController {
|
||||||
NSOperationQueue* _opQueue;
|
NSOperationQueue* _opQueue;
|
||||||
TemporaryHost* _selectedHost;
|
TemporaryHost* _selectedHost;
|
||||||
|
BOOL _showHiddenApps;
|
||||||
NSString* _uniqueId;
|
NSString* _uniqueId;
|
||||||
NSData* _clientCert;
|
NSData* _clientCert;
|
||||||
DiscoveryManager* _discMan;
|
DiscoveryManager* _discMan;
|
||||||
@@ -208,6 +209,24 @@ 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;
|
||||||
|
savedApp.hdrSupported = app.hdrSupported;
|
||||||
|
savedApp.hidden = app.hidden;
|
||||||
|
|
||||||
|
host.appList = newHostAppList;
|
||||||
|
|
||||||
|
[database updateAppsForExistingHost:host];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
- (void) updateApplist:(NSSet*) newList forHost:(TemporaryHost*)host {
|
- (void) updateApplist:(NSSet*) newList forHost:(TemporaryHost*)host {
|
||||||
DataManager* database = [[DataManager alloc] init];
|
DataManager* database = [[DataManager alloc] init];
|
||||||
NSMutableSet* newHostAppList = [NSMutableSet setWithSet:host.appList];
|
NSMutableSet* newHostAppList = [NSMutableSet setWithSet:host.appList];
|
||||||
@@ -218,6 +237,7 @@ static NSMutableSet* hostList;
|
|||||||
if ([app.id isEqualToString:savedApp.id]) {
|
if ([app.id isEqualToString:savedApp.id]) {
|
||||||
savedApp.name = app.name;
|
savedApp.name = app.name;
|
||||||
savedApp.hdrSupported = app.hdrSupported;
|
savedApp.hdrSupported = app.hdrSupported;
|
||||||
|
// Don't propagate hidden, because we want the local data to prevail
|
||||||
appAlreadyInList = YES;
|
appAlreadyInList = YES;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -275,6 +295,7 @@ static NSMutableSet* hostList;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
[_appManager stopRetrieving];
|
[_appManager stopRetrieving];
|
||||||
|
_showHiddenApps = NO;
|
||||||
_selectedHost = nil;
|
_selectedHost = nil;
|
||||||
_sortedAppList = nil;
|
_sortedAppList = nil;
|
||||||
|
|
||||||
@@ -466,6 +487,12 @@ static NSMutableSet* hostList;
|
|||||||
[[self activeViewController] presentViewController:wolAlert animated:YES completion:nil];
|
[[self activeViewController] presentViewController:wolAlert animated:YES completion:nil];
|
||||||
}]];
|
}]];
|
||||||
}
|
}
|
||||||
|
else if (host.pairState == PairStatePaired) {
|
||||||
|
[longClickAlert addAction:[UIAlertAction actionWithTitle:@"Show Hidden Apps" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
||||||
|
self->_showHiddenApps = YES;
|
||||||
|
[self hostClicked:host view:view];
|
||||||
|
}]];
|
||||||
|
}
|
||||||
[longClickAlert addAction:[UIAlertAction actionWithTitle:@"Test Network" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
|
[longClickAlert addAction:[UIAlertAction actionWithTitle:@"Test Network" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
|
||||||
[self showLoadingFrame:^{
|
[self showLoadingFrame:^{
|
||||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||||
@@ -642,8 +669,8 @@ static NSMutableSet* hostList;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) appClicked:(TemporaryApp *)app {
|
- (void) appLongClicked:(TemporaryApp*) app {
|
||||||
Log(LOG_D, @"Clicked app: %@", app.name);
|
Log(LOG_D, @"Long clicked app: %@", app.name);
|
||||||
|
|
||||||
[_appManager stopRetrieving];
|
[_appManager stopRetrieving];
|
||||||
|
|
||||||
@@ -655,18 +682,43 @@ static NSMutableSet* hostList;
|
|||||||
[[self revealViewController] revealToggleAnimated:NO];
|
[[self revealViewController] revealToggleAnimated:NO];
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TemporaryApp* currentApp = [self findRunningApp:app.host];
|
TemporaryApp* currentApp = [self findRunningApp:app.host];
|
||||||
|
|
||||||
|
NSString* message;
|
||||||
|
|
||||||
|
if (currentApp == nil || [app.id isEqualToString:currentApp.id]) {
|
||||||
|
if (app.hidden) {
|
||||||
|
message = @"Hidden";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = @"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
message = [NSString stringWithFormat:@"%@ is currently running", currentApp.name];
|
||||||
|
}
|
||||||
|
|
||||||
|
UIAlertController* alertController = [UIAlertController
|
||||||
|
alertControllerWithTitle: app.name
|
||||||
|
message:message
|
||||||
|
preferredStyle:UIAlertControllerStyleActionSheet];
|
||||||
|
|
||||||
|
[alertController addAction:[UIAlertAction
|
||||||
|
actionWithTitle:currentApp == nil ? @"Launch App" : ([app.id isEqualToString:currentApp.id] ? @"Resume App" : @"Resume Running App") style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
||||||
|
if (currentApp != nil) {
|
||||||
|
Log(LOG_I, @"Resuming application: %@", currentApp.name);
|
||||||
|
[self prepareToStreamApp:currentApp];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Log(LOG_I, @"Launching application: %@", app.name);
|
||||||
|
[self prepareToStreamApp:app];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||||
|
}]];
|
||||||
|
|
||||||
if (currentApp != nil) {
|
if (currentApp != nil) {
|
||||||
UIAlertController* alertController = [UIAlertController
|
|
||||||
alertControllerWithTitle: app.name
|
|
||||||
message: [app.id isEqualToString:currentApp.id] ? @"" : [NSString stringWithFormat:@"%@ is currently running", currentApp.name]preferredStyle:UIAlertControllerStyleAlert];
|
|
||||||
[alertController addAction:[UIAlertAction
|
|
||||||
actionWithTitle:[app.id isEqualToString:currentApp.id] ? @"Resume App" : @"Resume Running App" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action){
|
|
||||||
Log(LOG_I, @"Resuming application: %@", currentApp.name);
|
|
||||||
[self prepareToStreamApp:currentApp];
|
|
||||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
|
||||||
}]];
|
|
||||||
[alertController addAction:[UIAlertAction actionWithTitle:
|
[alertController addAction:[UIAlertAction actionWithTitle:
|
||||||
[app.id isEqualToString:currentApp.id] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
[app.id isEqualToString:currentApp.id] ? @"Quit App" : @"Quit Running App and Start" style:UIAlertActionStyleDestructive handler:^(UIAlertAction* action){
|
||||||
Log(LOG_I, @"Quitting application: %@", currentApp.name);
|
Log(LOG_I, @"Quitting application: %@", currentApp.name);
|
||||||
@@ -713,9 +765,6 @@ static NSMutableSet* hostList;
|
|||||||
else {
|
else {
|
||||||
app.host.currentGame = @"0";
|
app.host.currentGame = @"0";
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
// Refresh the UI
|
|
||||||
[self updateAppsForHost:app.host];
|
|
||||||
|
|
||||||
// If it succeeds and we're to start streaming, segue to the stream
|
// If it succeeds and we're to start streaming, segue to the stream
|
||||||
if (![app.id isEqualToString:currentApp.id]) {
|
if (![app.id isEqualToString:currentApp.id]) {
|
||||||
[self prepareToStreamApp:app];
|
[self prepareToStreamApp:app];
|
||||||
@@ -733,8 +782,39 @@ static NSMutableSet* hostList;
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
}]];
|
}]];
|
||||||
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
}
|
||||||
[[self activeViewController] presentViewController:alertController animated:YES completion:nil];
|
|
||||||
|
if (currentApp == nil || ![app.id isEqualToString:currentApp.id] || app.hidden) {
|
||||||
|
[alertController addAction:[UIAlertAction actionWithTitle:app.hidden ? @"Show App" : @"Hide App" style:UIAlertActionStyleDefault handler:^(UIAlertAction* action) {
|
||||||
|
app.hidden = !app.hidden;
|
||||||
|
[self updateAppEntry:app forHost:app.host];
|
||||||
|
|
||||||
|
// Don't call updateAppsForHost because that will nuke this
|
||||||
|
// app immediately if we're not showing hidden apps.
|
||||||
|
}]];
|
||||||
|
}
|
||||||
|
|
||||||
|
[alertController addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:nil]];
|
||||||
|
[[self activeViewController] presentViewController:alertController animated:YES completion:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) appClicked:(TemporaryApp *)app {
|
||||||
|
Log(LOG_D, @"Clicked app: %@", app.name);
|
||||||
|
|
||||||
|
[_appManager stopRetrieving];
|
||||||
|
|
||||||
|
#if !TARGET_OS_TV
|
||||||
|
if (currentPosition != FrontViewPositionLeft) {
|
||||||
|
// This must not be animated because we need the position
|
||||||
|
// to change (and notify our callback to save settings data)
|
||||||
|
// before we call prepareToStreamApp.
|
||||||
|
[[self revealViewController] revealToggleAnimated:NO];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if ([self findRunningApp:app.host]) {
|
||||||
|
// If there's a running app, display a menu
|
||||||
|
[self appLongClicked:app];
|
||||||
} else {
|
} else {
|
||||||
[self prepareToStreamApp:app];
|
[self prepareToStreamApp:app];
|
||||||
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
[self performSegueWithIdentifier:@"createStreamFrame" sender:nil];
|
||||||
@@ -1188,6 +1268,16 @@ static NSMutableSet* hostList;
|
|||||||
_sortedAppList = [host.appList allObjects];
|
_sortedAppList = [host.appList allObjects];
|
||||||
_sortedAppList = [_sortedAppList sortedArrayUsingSelector:@selector(compareName:)];
|
_sortedAppList = [_sortedAppList sortedArrayUsingSelector:@selector(compareName:)];
|
||||||
|
|
||||||
|
if (!_showHiddenApps) {
|
||||||
|
NSMutableArray* visibleAppList = [NSMutableArray array];
|
||||||
|
for (TemporaryApp* app in _sortedAppList) {
|
||||||
|
if (!app.hidden) {
|
||||||
|
[visibleAppList addObject:app];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_sortedAppList = visibleAppList;
|
||||||
|
}
|
||||||
|
|
||||||
[hostScrollView removeFromSuperview];
|
[hostScrollView removeFromSuperview];
|
||||||
[self.collectionView reloadData];
|
[self.collectionView reloadData];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user