VLCLocalServerListViewController.m 12.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
//
//  VLCLocalServerListViewController.m
//  VLC for iOS
//
//  Created by Felix Paul Kühne on 10.08.13.
//  Copyright (c) 2013 VideoLAN. All rights reserved.
//
//  Refer to the COPYING file of the official project for license.
//

#import "VLCLocalServerListViewController.h"
#import "UIBarButtonItem+Theme.h"
#import "VLCAppDelegate.h"
#import "UPnPManager.h"
#import "VLCLocalNetworkListCell.h"
16
#import "VLCLocalServerFolderListViewController.h"
17 18
#import <QuartzCore/QuartzCore.h>
#import "GHRevealViewController.h"
19 20
#import "VLCNetworkLoginViewController.h"
#import "UINavigationController+Theme.h"
21

22
@interface VLCLocalServerListViewController () <UITableViewDataSource, UITableViewDelegate, NSNetServiceBrowserDelegate, VLCNetworkLoginViewController, NSNetServiceDelegate>
23 24
{
    UIBarButtonItem *_backToMenuButton;
25 26
    NSArray *_sectionHeaderTexts;

27
    NSNetServiceBrowser *_netServiceBrowser;
28
    NSMutableArray *_rawServices;
29 30
    NSMutableArray *_ftpServices;

31 32
    NSArray *_filteredUPNPDevices;
    NSArray *_UPNPdevices;
33 34

    VLCNetworkLoginViewController *_loginViewController;
35 36 37 38 39 40
}

@end

@implementation VLCLocalServerListViewController

41 42 43 44 45
- (void)dealloc
{
    [_netServiceBrowser stop];
}

46 47 48 49 50 51 52 53 54 55 56 57 58
- (void)loadView
{
    _tableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
    _tableView.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    self.view = _tableView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];

59 60 61 62 63
    _sectionHeaderTexts = @[@"Universal Plug'n'Play (UPNP)", @"File Transfer Protocol (FTP)"];

    _backToMenuButton = [UIBarButtonItem themedRevealMenuButtonWithTarget:self andSelector:@selector(goBack:)];
    self.navigationItem.leftBarButtonItem = _backToMenuButton;

64
    self.tableView.rowHeight = [VLCLocalNetworkListCell heightOfCell];
65 66 67 68 69 70
    self.tableView.separatorColor = [UIColor colorWithWhite:.122 alpha:1.];
    self.view.backgroundColor = [UIColor colorWithWhite:.122 alpha:1.];

    self.title = NSLocalizedString(@"LOCAL_NETWORK", @"");

    [self performSelectorInBackground:@selector(_startUPNPDiscovery) withObject:nil];
71 72 73 74

    _ftpServices = [[NSMutableArray alloc] init];
    [_ftpServices addObject:@"Connect to Server"];

75 76
    _rawServices = [[NSMutableArray alloc] init];

77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
    _netServiceBrowser = [[NSNetServiceBrowser alloc] init];
    _netServiceBrowser.delegate = self;
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
    [_netServiceBrowser stop];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self _triggerNetServiceBrowser];
}

- (void)_triggerNetServiceBrowser
{
    [_netServiceBrowser searchForServicesOfType:@"_ftp._tcp." inDomain:@""];
96 97 98 99
}

- (void)_startUPNPDiscovery
{
100
    UPnPDB* db = [[UPnPManager GetInstance] DB];
101
    _UPNPdevices = [db rootDevices];
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

    [db addObserver:(UPnPDBObserver*)self];

    //Optional; set User Agent
    [[[UPnPManager GetInstance] SSDP] setUserAgentProduct:[NSString stringWithFormat:@"VLC for iOS/%@", [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] andOS:@"iOS"];

    //Search for UPnP Devices
    [[[UPnPManager GetInstance] SSDP] searchSSDP];
}

- (IBAction)goBack:(id)sender
{
    [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController] toggleSidebar:![(VLCAppDelegate*)[UIApplication sharedApplication].delegate revealController].sidebarShowing duration:kGHRevealSidebarDefaultAnimationDuration];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
        return NO;
    return YES;
}

124 125
#pragma mark - table view handling

126 127
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
128
    return _sectionHeaderTexts.count;
129 130 131 132
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
133 134
    if (section == 0)
        return _filteredUPNPDevices.count;
135 136
    else if (section == 1)
        return _ftpServices.count;
137 138

    return 0;
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
}

- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    cell.backgroundColor = (indexPath.row % 2 == 0)? [UIColor blackColor]: [UIColor colorWithWhite:.122 alpha:1.];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"LocalNetworkCell";

    VLCLocalNetworkListCell *cell = (VLCLocalNetworkListCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil)
        cell = [VLCLocalNetworkListCell cellWithReuseIdentifier:CellIdentifier];

154 155 156 157 158 159 160
    NSUInteger row = indexPath.row;
    NSUInteger section = indexPath.section;

    [cell setIsDirectory:YES];

    if (section == 0) {
        BasicUPnPDevice *device = _filteredUPNPDevices[row];
161 162
        [cell setTitle:[device friendlyName]];
        [cell setIcon:[device smallIcon]];
163 164 165 166 167
    } else if (section == 1) {
        if (row == 0)
            [cell setTitle:_ftpServices[row]];
        else
            [cell setTitle:[_ftpServices[row] name]];
168
    }
169 170 171 172 173 174

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
175 176 177 178
    if (indexPath.section == 0) {
        BasicUPnPDevice *device = _filteredUPNPDevices[indexPath.row];
        if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"]) {
            MediaServer1Device *server = (MediaServer1Device*)device;
179
            VLCLocalServerFolderListViewController *targetViewController = [[VLCLocalServerFolderListViewController alloc] initWithUPNPDevice:server header:[device friendlyName] andRootID:@"0"];
180
            [self.navigationController pushViewController:targetViewController animated:YES];
181
        }
182
    } else if (indexPath.section == 1) {
183 184 185 186 187 188 189 190 191 192 193 194 195 196
        if (_loginViewController == nil) {
            _loginViewController = [[VLCNetworkLoginViewController alloc] initWithNibName:nil bundle:nil];
            _loginViewController.delegate = self;
        }

        UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:_loginViewController];
        [navCon loadTheme];
        navCon.navigationBarHidden = NO;

        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
            navCon.modalPresentationStyle = UIModalPresentationFormSheet;
            [self presentModalViewController:navCon animated:YES];

            if (_loginViewController.navigationItem.leftBarButtonItem == nil) {
197
                UIBarButtonItem *doneButton = [UIBarButtonItem themedDoneButtonWithTarget:_loginViewController andSelector:@selector(dismissWithAnimation:)];
198 199 200

                _loginViewController.navigationItem.leftBarButtonItem = doneButton;
            }
201
        } else
202
            [self.navigationController pushViewController:_loginViewController animated:YES];
203 204 205 206 207 208 209

        if (indexPath.row != 0) { // FTP Connect To Server Special Item
            if ([_ftpServices[indexPath.row] hostName].length > 0)
                _loginViewController.serverAddressField.text = [NSString stringWithFormat:@"ftp://%@", [_ftpServices[indexPath.row] hostName]];
        } else
            _loginViewController.serverAddressField.text = @"";

210
    }
211 212
}

213 214
#pragma mark - login panel protocol

215
- (void)loginToURL:(NSURL *)url confirmedWithUsername:(NSString *)username andPassword:(NSString *)password
216 217
{
    _loginViewController = nil;
218 219 220 221 222 223 224
    if ([url.scheme isEqualToString:@"ftp"]) {
        if (url.host.length > 0) {
            VLCLocalServerFolderListViewController *targetViewController = [[VLCLocalServerFolderListViewController alloc] initWithFTPServer:url.host userName:username andPassword:password atPath:@"/"];
            [self.navigationController pushViewController:targetViewController animated:YES];
        }
    } else
        APLog(@"Unsupported URL Scheme requested %@", url.scheme);
225 226
}

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
#pragma mark - custom table view appearance

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
    return 21.f;
}

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
	NSObject *headerText = NSLocalizedString(_sectionHeaderTexts[section], @"");
	UIView *headerView = nil;
	if (headerText != [NSNull null]) {
		headerView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, [UIScreen mainScreen].bounds.size.height, 21.0f)];
		CAGradientLayer *gradient = [CAGradientLayer layer];
		gradient.frame = headerView.bounds;
		gradient.colors = @[
                      (id)[UIColor colorWithRed:(67.0f/255.0f) green:(74.0f/255.0f) blue:(94.0f/255.0f) alpha:1.0f].CGColor,
                      (id)[UIColor colorWithRed:(57.0f/255.0f) green:(64.0f/255.0f) blue:(82.0f/255.0f) alpha:1.0f].CGColor,
                      ];
		[headerView.layer insertSublayer:gradient atIndex:0];

		UILabel *textLabel = [[UILabel alloc] initWithFrame:CGRectInset(headerView.bounds, 12.0f, 5.0f)];
		textLabel.text = (NSString *) headerText;
		textLabel.font = [UIFont fontWithName:@"Helvetica-Bold" size:([UIFont systemFontSize] * 0.8f)];
		textLabel.shadowOffset = CGSizeMake(0.0f, 1.0f);
		textLabel.shadowColor = [UIColor colorWithWhite:0.0f alpha:0.25f];
		textLabel.textColor = [UIColor colorWithRed:(125.0f/255.0f) green:(129.0f/255.0f) blue:(146.0f/255.0f) alpha:1.0f];
		textLabel.backgroundColor = [UIColor clearColor];
		[headerView addSubview:textLabel];

		UIView *topLine = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, [UIScreen mainScreen].bounds.size.height, 1.0f)];
		topLine.backgroundColor = [UIColor colorWithRed:(78.0f/255.0f) green:(86.0f/255.0f) blue:(103.0f/255.0f) alpha:1.0f];
		[headerView addSubview:topLine];

		UIView *bottomLine = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 21.0f, [UIScreen mainScreen].bounds.size.height, 1.0f)];
		bottomLine.backgroundColor = [UIColor colorWithRed:(36.0f/255.0f) green:(42.0f/255.0f) blue:(5.0f/255.0f) alpha:1.0f];
		[headerView addSubview:bottomLine];
	}
	return headerView;
}

266 267 268
#pragma mark - bonjour discovery
- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didFindService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
{
269 270 271
    APLog(@"found bonjour service: %@ (%@)", aNetService.name, aNetService.type);
    [_rawServices addObject:aNetService];
    aNetService.delegate = self;
272
    [aNetService resolveWithTimeout:5.];
273 274 275 276
}

- (void)netServiceBrowser:(NSNetServiceBrowser *)aNetServiceBrowser didRemoveService:(NSNetService *)aNetService moreComing:(BOOL)moreComing
{
277 278 279 280 281
    APLog(@"bonjour service disappeared: %@ (%i)", aNetService.name, moreComing);
    if ([_rawServices containsObject:aNetService])
        [_rawServices removeObject:aNetService];
    if ([aNetService.type isEqualToString:@"_ftp._tcp."])
        [_ftpServices removeObject:aNetService];
282 283 284 285
    if (!moreComing)
        [self.tableView reloadData];
}

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
- (void)netServiceDidResolveAddress:(NSNetService *)aNetService
{
    if ([aNetService.type isEqualToString:@"_ftp._tcp."]) {
        if (![_ftpServices containsObject:aNetService])
            [_ftpServices addObject:aNetService];
    }
    [_rawServices removeObject:aNetService];
    [self.tableView reloadData];
}

- (void)netService:(NSNetService *)aNetService didNotResolve:(NSDictionary *)errorDict
{
    APLog(@"failed to resolve: %@", aNetService.name);
    [_rawServices removeObject:aNetService];
}

302
#pragma mark - UPNP details
303
//protocol UPnPDBObserver
304
- (void)UPnPDBWillUpdate:(UPnPDB*)sender{
305
    APLog(@"UPnPDBWillUpdate %d", _UPNPdevices.count);
306 307
}

308
- (void)UPnPDBUpdated:(UPnPDB*)sender{
309
    APLog(@"UPnPDBUpdated %d", _UPNPdevices.count);
310

311
    NSUInteger count = _UPNPdevices.count;
312 313 314
    BasicUPnPDevice *device;
    NSMutableArray *mutArray = [[NSMutableArray alloc] init];
    for (NSUInteger x = 0; x < count; x++) {
315
        device = _UPNPdevices[x];
316 317 318
        if ([[device urn] isEqualToString:@"urn:schemas-upnp-org:device:MediaServer:1"])
            [mutArray addObject:device];
    }
319 320
    _filteredUPNPDevices = nil;
    _filteredUPNPDevices = [NSArray arrayWithArray:mutArray];
321

322 323 324 325
    [self.tableView performSelectorOnMainThread : @ selector(reloadData) withObject:nil waitUntilDone:YES];
}

@end