VLCServerListViewController.m 16.8 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * VLCLocalServerListViewController.m
 * VLC for iOS
 *****************************************************************************
5
 * Copyright (c) 2013-2018 VideoLAN. All rights reserved.
6 7 8 9 10
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Pierre SAGASPE <pierre.sagaspe # me.com>
 *          Gleb Pinigin <gpinigin # gmail.com>
11
 *          Tobias Conradi <videolan # tobias-conradi.de>
12
 *          Vincent L. Cone <vincent.l.cone # tuta.io>
13
 *          Carola Nitz <caro # videolan.org>
14 15 16
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
17

18
#import "VLCServerListViewController.h"
19 20
#import "VLCLocalServerDiscoveryController.h"

21
#import "VLCPlaybackController.h"
22
#import "VLCNetworkListCell.h"
23
#import "VLCNetworkLoginViewController.h"
24
#import "VLCNetworkServerBrowserViewController.h"
25

26
#import "VLCNetworkServerLoginInformation+Keychain.h"
27

28
#import "VLCNetworkServerBrowserFTP.h"
29
#import "VLCNetworkServerBrowserVLCMedia.h"
30
#import "VLCNetworkServerBrowserPlex.h"
31

32 33 34 35 36 37 38
#import "VLCLocalNetworkServiceBrowserManualConnect.h"
#import "VLCLocalNetworkServiceBrowserPlex.h"
#import "VLCLocalNetworkServiceBrowserFTP.h"
#import "VLCLocalNetworkServiceBrowserUPnP.h"
#import "VLCLocalNetworkServiceBrowserHTTP.h"
#import "VLCLocalNetworkServiceBrowserSAP.h"
#import "VLCLocalNetworkServiceBrowserDSM.h"
39
#import "VLCLocalNetworkServiceBrowserBonjour.h"
40

41 42 43 44
#import "VLCWiFiUploadTableViewCell.h"

#import "VLC_iOS-Swift.h"

45
@interface VLCServerListViewController () <UITableViewDataSource, UITableViewDelegate, VLCLocalServerDiscoveryControllerDelegate, VLCNetworkLoginViewControllerDelegate, VLCRemoteNetworkDataSourceDelegate>
46
{
47 48
    VLCLocalServerDiscoveryController *_discoveryController;

49
    UIRefreshControl *_refreshControl;
50
    UIActivityIndicatorView *_activityIndicator;
51 52
    UITableView *_localNetworkTableView;
    UITableView *_remoteNetworkTableView;
53
    UIScrollView *_scrollView;
54
    VLCRemoteNetworkDataSourceAndDelegate *_remoteNetworkDataSourceAndDelegate;
55
    NSLayoutConstraint* _localNetworkHeight;
56 57 58 59
}

@end

60
@implementation VLCServerListViewController
61 62 63

- (void)loadView
{
64 65
    [super loadView];

66 67 68 69 70 71 72 73 74 75 76
    _scrollView = [[UIScrollView alloc] init];
    _scrollView.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:_scrollView];

    [NSLayoutConstraint activateConstraints:@[
                                              [_scrollView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
                                              [_scrollView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
                                              [_scrollView.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor],
                                              [_scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
                                              ]];

77 78
    _remoteNetworkDataSourceAndDelegate = [VLCRemoteNetworkDataSourceAndDelegate new];
    _remoteNetworkDataSourceAndDelegate.delegate = self;
79 80 81 82 83 84 85 86 87 88 89 90 91 92

    _localNetworkTableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
    _localNetworkTableView.translatesAutoresizingMaskIntoConstraints = NO;
    _localNetworkTableView.backgroundColor = PresentationTheme.current.colors.background;
    _localNetworkTableView.delegate = self;
    _localNetworkTableView.dataSource = self;
    _localNetworkTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    _localNetworkTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _localNetworkTableView.rowHeight = [VLCNetworkListCell heightOfCell];
    _localNetworkTableView.separatorColor = PresentationTheme.current.colors.background;

    _remoteNetworkTableView = [[UITableView alloc] initWithFrame:[UIScreen mainScreen].bounds style:UITableViewStylePlain];
    _remoteNetworkTableView.translatesAutoresizingMaskIntoConstraints = NO;
    _remoteNetworkTableView.backgroundColor = PresentationTheme.current.colors.background;
93 94
    _remoteNetworkTableView.delegate = _remoteNetworkDataSourceAndDelegate;
    _remoteNetworkTableView.dataSource = _remoteNetworkDataSourceAndDelegate;
95 96 97
    _remoteNetworkTableView.indicatorStyle = UIScrollViewIndicatorStyleWhite;
    _remoteNetworkTableView.separatorStyle = UITableViewCellSeparatorStyleNone;
    _remoteNetworkTableView.bounces = NO;
98

99
    [_remoteNetworkTableView registerClass:[VLCWiFiUploadTableViewCell class] forCellReuseIdentifier:[VLCWiFiUploadTableViewCell cellIdentifier]];
100
    [_remoteNetworkTableView registerClass:[VLCRemoteNetworkCell class] forCellReuseIdentifier:VLCRemoteNetworkCell.cellIdentifier];
101 102 103 104 105 106 107

    _refreshControl = [[UIRefreshControl alloc] init];
    _refreshControl.backgroundColor = PresentationTheme.current.colors.background;
    _refreshControl.tintColor = [UIColor whiteColor];
    [_refreshControl addTarget:self action:@selector(handleRefresh) forControlEvents:UIControlEventValueChanged];
    [_localNetworkTableView addSubview:_refreshControl];

108
    _activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
109
    _activityIndicator.center = _localNetworkTableView.center;
110 111
    _activityIndicator.autoresizingMask = UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin;
    _activityIndicator.hidesWhenStopped = YES;
112 113
    [_localNetworkTableView addSubview:_activityIndicator];

114 115 116 117 118 119 120
    [_scrollView addSubview:_localNetworkTableView];
    [_scrollView addSubview:_remoteNetworkTableView];

    [_remoteNetworkTableView layoutIfNeeded];
    CGSize contentSize = [_remoteNetworkTableView contentSize];
    _localNetworkHeight = [_localNetworkTableView.heightAnchor constraintEqualToConstant:_localNetworkTableView.contentSize.height];

121 122 123
    [NSLayoutConstraint activateConstraints:@[
                                              [_remoteNetworkTableView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
                                              [_remoteNetworkTableView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
124 125
                                              [_remoteNetworkTableView.topAnchor constraintEqualToAnchor:_scrollView.topAnchor],
                                              [_remoteNetworkTableView.heightAnchor constraintEqualToConstant:contentSize.height],
126 127 128
                                              [_localNetworkTableView.topAnchor constraintEqualToAnchor:_remoteNetworkTableView.bottomAnchor],
                                              [_localNetworkTableView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor],
                                              [_localNetworkTableView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor],
129 130
                                              [_localNetworkTableView.bottomAnchor constraintEqualToAnchor:_scrollView.bottomAnchor],
                                              _localNetworkHeight
131
                                              ]];
132
    _scrollView.backgroundColor = PresentationTheme.current.colors.background;
133 134 135 136 137 138
}

- (void)viewDidLoad
{
    [super viewDidLoad];

139 140
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(themeDidChange) name:kVLCThemeDidChangeNotification object:nil];

141 142 143 144 145 146
    NSArray *browserClasses = @[
                                [VLCLocalNetworkServiceBrowserManualConnect class],
                                [VLCLocalNetworkServiceBrowserUPnP class],
                                [VLCLocalNetworkServiceBrowserPlex class],
                                [VLCLocalNetworkServiceBrowserFTP class],
                                [VLCLocalNetworkServiceBrowserHTTP class],
147
#ifndef NDEBUG
148
                                [VLCLocalNetworkServiceBrowserSAP class],
149
#endif
150
                                [VLCLocalNetworkServiceBrowserDSM class],
151
                                [VLCLocalNetworkServiceBrowserBonjour class],
152 153 154
                                ];

    _discoveryController = [[VLCLocalServerDiscoveryController alloc] initWithServiceBrowserClasses:browserClasses];
155
    _discoveryController.delegate = self;
156 157 158 159 160
}

- (void)viewWillDisappear:(BOOL)animated
{
    [super viewWillDisappear:animated];
161
    [_activityIndicator stopAnimating];
162 163

    [_discoveryController stopDiscovery];
164 165 166 167 168
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
169
    [_discoveryController startDiscovery];
170 171
}

172
- (BOOL)shouldAutorotate
173
{
174
    UIInterfaceOrientation toInterfaceOrientation = [[UIApplication sharedApplication] statusBarOrientation];
175 176 177 178 179
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
        return NO;
    return YES;
}

180 181
#pragma mark - table view handling

182 183
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
184
    return _discoveryController.numberOfSections;
185 186 187 188
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
189
    return [_discoveryController numberOfItemsInSection:section];
190 191
}

192
- (void)tableView:(UITableView *)tableView willDisplayCell:(VLCNetworkListCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
193
{
194 195
    if (tableView == _remoteNetworkTableView) return;
    UIColor *color = (indexPath.row % 2 == 0)? PresentationTheme.current.colors.cellBackgroundB : PresentationTheme.current.colors.cellBackgroundA;
196
    cell.backgroundColor = cell.titleLabel.backgroundColor = cell.folderTitleLabel.backgroundColor = cell.subtitleLabel.backgroundColor = color;
197
    cell.titleLabel.textColor = cell.folderTitleLabel.textColor = cell.subtitleLabel.textColor = cell.thumbnailView.tintColor = PresentationTheme.current.colors.cellTextColor;
198 199 200 201 202 203
}

- (void)tableView:(UITableView *)tableView willDisplayHeaderView:(UIView *)view forSection:(NSInteger)section
{
    // Text Color
    UITableViewHeaderFooterView *header = (UITableViewHeaderFooterView *)view;
204
    header.textLabel.textColor = PresentationTheme.current.colors.sectionHeaderTextColor;
205 206
    header.textLabel.font = [UIFont boldSystemFontOfSize:([UIFont systemFontSize] * 0.8f)];

207
    header.tintColor = PresentationTheme.current.colors.sectionHeaderTintColor;
208 209 210 211 212 213
}

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

214
    VLCNetworkListCell *cell = (VLCNetworkListCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
215
    if (cell == nil)
216
        cell = [VLCNetworkListCell cellWithReuseIdentifier:CellIdentifier];
217

218
    id<VLCLocalNetworkService> service = [_discoveryController networkServiceForIndexPath:indexPath];
219 220

    [cell setIsDirectory:YES];
221 222
    [cell setIcon:service.icon];
    [cell setTitle:service.title];
223 224 225 226 227 228

    return cell;
}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
229 230
    [tableView deselectRowAtIndexPath:indexPath animated:YES];

231
    id<VLCLocalNetworkService> service = [_discoveryController networkServiceForIndexPath:indexPath];
232

233 234 235 236 237 238
    if ([service respondsToSelector:@selector(serverBrowser)]) {
        id<VLCNetworkServerBrowser> serverBrowser = [service serverBrowser];
        if (serverBrowser) {
            VLCNetworkServerBrowserViewController *vc = [[VLCNetworkServerBrowserViewController alloc] initWithServerBrowser:serverBrowser];
            [self.navigationController pushViewController:vc animated:YES];
            return;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
239
        }
240 241 242 243 244
    }

    if ([service respondsToSelector:@selector(directPlaybackURL)]) {
        NSURL *playbackURL = [service directPlaybackURL];
        if (playbackURL) {
245 246 247 248

            VLCMediaList *medialist = [[VLCMediaList alloc] init];
            [medialist addMedia:[VLCMedia mediaWithURL:playbackURL]];
            [[VLCPlaybackController sharedInstance] playMediaList:medialist firstIndex:0 subtitlesFilePath:nil];
249
            return;
250
        }
251
    }
252 253 254 255 256 257

    VLCNetworkServerLoginInformation *login;
    if ([service respondsToSelector:@selector(loginInformation)]) {
        login = [service loginInformation];
    }

258
    [login loadLoginInformationFromKeychainWithError:nil];
259

260
    VLCNetworkLoginViewController *loginViewController = [[VLCNetworkLoginViewController alloc] initWithNibName:@"VLCNetworkLoginViewController" bundle:nil];
261

262
    loginViewController.loginInformation = login;
263 264
    loginViewController.delegate = self;
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
265
        UINavigationController *navCon = [[UINavigationController alloc] initWithRootViewController:loginViewController];
266 267 268 269 270
        navCon.navigationBarHidden = NO;
        navCon.modalPresentationStyle = UIModalPresentationFormSheet;
        [self presentViewController:navCon animated:YES completion:nil];

        if (loginViewController.navigationItem.leftBarButtonItem == nil)
271
            loginViewController.navigationItem.leftBarButtonItem = [UIBarButtonItem themedDarkToolbarButtonWithTitle:NSLocalizedString(@"BUTTON_DONE", nil) target:self andSelector:@selector(_dismissLogin)];
272 273 274
    } else {
        [self.navigationController pushViewController:loginViewController animated:YES];
    }
275
}
276 277 278 279 280 281

- (void)showViewController:(UIViewController *)viewController
{
    [self.navigationController pushViewController:viewController animated:YES];
}

282 283 284 285
#pragma mark -
- (void)themeDidChange
{
    _localNetworkTableView.backgroundColor = PresentationTheme.current.colors.background;
286 287
    _remoteNetworkTableView.backgroundColor = PresentationTheme.current.colors.background;
    _scrollView.backgroundColor = PresentationTheme.current.colors.background;
288 289 290
    _localNetworkTableView.separatorColor = PresentationTheme.current.colors.background;
    _refreshControl.backgroundColor = PresentationTheme.current.colors.background;
}
291

292 293 294 295 296 297 298 299
- (void)_dismissLogin
{
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
        [self.navigationController popViewControllerAnimated:YES];
    else
        [self dismissViewControllerAnimated:YES completion:nil];
}

Pierre SAGASPE's avatar
Pierre SAGASPE committed
300 301
#pragma mark - Refresh

302
- (void)handleRefresh
Pierre SAGASPE's avatar
Pierre SAGASPE committed
303 304
{
    //set the title while refreshing
305
    _refreshControl.attributedTitle = [[NSAttributedString alloc]initWithString:NSLocalizedString(@"LOCAL_SERVER_REFRESH",nil)];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
306 307 308
    //set the date and time of refreshing
    NSDateFormatter *formattedDate = [[NSDateFormatter alloc]init];
    [formattedDate setDateFormat:@"MMM d, h:mm a"];
309
    NSString *lastupdated = [NSString stringWithFormat:NSLocalizedString(@"LOCAL_SERVER_LAST_UPDATE",nil),[formattedDate stringFromDate:[NSDate date]]];
310 311
    NSDictionary *attrsDictionary = [NSDictionary dictionaryWithObject:[UIColor whiteColor] forKey:NSForegroundColorAttributeName];
    _refreshControl.attributedTitle = [[NSAttributedString alloc] initWithString:lastupdated attributes:attrsDictionary];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
312 313
    //end the refreshing

314
    if ([_discoveryController refreshDiscoveredData])
315
        [_localNetworkTableView reloadData];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
316

317
    [_refreshControl endRefreshing];
Pierre SAGASPE's avatar
Pierre SAGASPE committed
318 319
}

320
#pragma mark - VLCNetworkLoginViewControllerDelegate
321

322
- (void)loginWithLoginViewController:(VLCNetworkLoginViewController *)loginViewController loginInfo:(VLCNetworkServerLoginInformation *)loginInformation
323
{
324
    id<VLCNetworkServerBrowser> serverBrowser = nil;
325 326 327 328 329 330 331 332 333 334
    NSString *identifier = loginInformation.protocolIdentifier;

    if ([identifier isEqualToString:VLCNetworkServerProtocolIdentifierFTP]) {
        serverBrowser = [[VLCNetworkServerBrowserFTP alloc] initWithLogin:loginInformation];
    } else if ([identifier isEqualToString:VLCNetworkServerProtocolIdentifierPlex]) {
        serverBrowser = [[VLCNetworkServerBrowserPlex alloc] initWithLogin:loginInformation];
    } else if ([identifier isEqualToString:VLCNetworkServerProtocolIdentifierSMB]) {
        serverBrowser = [VLCNetworkServerBrowserVLCMedia SMBNetworkServerBrowserWithLogin:loginInformation];
    } else {
        APLog(@"Unsupported URL Scheme requested %@", identifier);
335
    }
336 337 338

    [self _dismissLogin];

339 340 341 342
    if (serverBrowser) {
        VLCNetworkServerBrowserViewController *targetViewController = [[VLCNetworkServerBrowserViewController alloc] initWithServerBrowser:serverBrowser];
        [self.navigationController pushViewController:targetViewController animated:YES];
    }
343 344
}

345 346
- (void)discoveryFoundSomethingNew
{
347
    [_localNetworkTableView reloadData];
348 349
    [_localNetworkTableView layoutIfNeeded];
    _localNetworkHeight.constant = _localNetworkTableView.contentSize.height;
350 351
}

352 353
#pragma mark - custom table view appearance

354 355
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
356 357 358
    // always hide the header of the first section
    if (section == 0)
        return 0.;
359

360 361
    if ([_discoveryController numberOfItemsInSection:section] == 0)
        return 0.;
362

363 364 365
    return 21.f;
}

366
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
367
{
368
    return [_discoveryController titleForSection:section];
369 370
}

371
@end