VLCServerListTVViewController.m 14 KB
Newer Older
Felix Paul Kühne's avatar
Felix Paul Kühne committed
1
2
3
4
5
6
7
8
9
10
/*****************************************************************************
 * VLC for iOS
 *****************************************************************************
 * Copyright (c) 2015 VideoLAN. All rights reserved.
 * $Id$
 *
 * Authors: Tobias Conradi <videolan # tobias-conradi.de>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
Tobias's avatar
Tobias committed
11

12
#import "VLCServerListTVViewController.h"
13
#import "VLCServerBrowsingTVViewController.h"
14
15
16
17
#import "VLCNetworkServerLoginInformation.h"

#import "VLCNetworkServerBrowserPlex.h"
#import "VLCNetworkServerBrowserVLCMedia.h"
18
#import "VLCNetworkServerBrowserFTP.h"
19
#import <SSKeychain/SSKeychain.h>
Tobias's avatar
Tobias committed
20

21
22
23
24
25
26
27
28
29
30
#import "VLCLocalNetworkServiceBrowserManualConnect.h"
#import "VLCLocalNetworkServiceBrowserPlex.h"
#import "VLCLocalNetworkServiceBrowserFTP.h"
#import "VLCLocalNetworkServiceBrowserUPnP.h"
#ifndef NDEBUG
#import "VLCLocalNetworkServiceBrowserSAP.h"
#endif
#import "VLCLocalNetworkServiceBrowserDSM.h"
#import "VLCLocalNetworkServiceBrowserHTTP.h"

31
32
33
#import "VLCRemoteBrowsingTVCell.h"

@interface VLCServerListTVViewController ()
34
35
36
{
    UILabel *_nothingFoundLabel;
}
37
@property (nonatomic) NSArray <NSIndexPath *> *indexPaths;
Tobias's avatar
Tobias committed
38
39
@end

40
41
42
43
44
45
@implementation VLCServerListTVViewController

- (instancetype)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    return [super initWithNibName:@"VLCRemoteBrowsingCollectionViewController" bundle:nil];
}
Tobias's avatar
Tobias committed
46
47
48

- (void)viewDidLoad {
    [super viewDidLoad];
49
50
    self.automaticallyAdjustsScrollViewInsets = NO;
    self.edgesForExtendedLayout = UIRectEdgeAll ^ UIRectEdgeTop;
51
52

    UICollectionViewFlowLayout *flowLayout = (UICollectionViewFlowLayout *)self.collectionViewLayout;
53
54
55
    flowLayout.itemSize = CGSizeMake(250.0, 300.0);
    flowLayout.minimumInteritemSpacing = 48.0;
    flowLayout.minimumLineSpacing = 100.0;
56

57
58
59
60
61
62
    self.nothingFoundLabel.text = NSLocalizedString(@"NO_SERVER_FOUND", nil);
    [self.nothingFoundLabel sizeToFit];
    UIView *nothingFoundView = self.nothingFoundView;
    [nothingFoundView sizeToFit];
    [nothingFoundView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.view addSubview:nothingFoundView];
63

64
    NSLayoutConstraint *yConstraint = [NSLayoutConstraint constraintWithItem:nothingFoundView
65
66
67
68
69
70
71
                                                                   attribute:NSLayoutAttributeCenterY
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:self.view
                                                                   attribute:NSLayoutAttributeCenterY
                                                                  multiplier:1.0
                                                                    constant:0.0];
    [self.view addConstraint:yConstraint];
72
    NSLayoutConstraint *xConstraint = [NSLayoutConstraint constraintWithItem:nothingFoundView
73
74
75
76
77
78
79
                                                                   attribute:NSLayoutAttributeCenterX
                                                                   relatedBy:NSLayoutRelationEqual
                                                                      toItem:self.view
                                                                   attribute:NSLayoutAttributeCenterX
                                                                  multiplier:1.0
                                                                    constant:0.0];
    [self.view addConstraint:xConstraint];
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

    NSArray *classes = @[
                         //                         [VLCLocalNetworkServiceBrowserManualConnect class],
                         [VLCLocalNetworkServiceBrowserHTTP class],
                         [VLCLocalNetworkServiceBrowserUPnP class],
                         [VLCLocalNetworkServiceBrowserDSM class],
                         [VLCLocalNetworkServiceBrowserPlex class],
                         [VLCLocalNetworkServiceBrowserFTP class],
#ifndef NDEBUG
                         [VLCLocalNetworkServiceBrowserSAP class],
#endif
                         ];
    self.discoveryController = [[VLCLocalServerDiscoveryController alloc] initWithServiceBrowserClasses:classes];
    self.discoveryController.delegate = self;
}

- (NSString *)title {
    return NSLocalizedString(@"LOCAL_NETWORK", nil);
98
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
99
100
101

- (void)viewDidAppear:(BOOL)animated
{
102
103
104
    [super viewDidAppear:animated];
    [self.discoveryController startDiscovery];
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
105
106
107

- (void)viewDidDisappear:(BOOL)animated
{
108
109
    [super viewDidDisappear:animated];
    [self.discoveryController stopDiscovery];
Tobias's avatar
Tobias committed
110
111
}

112
#pragma mark - Collection view data source
Tobias's avatar
Tobias committed
113

114
115
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
116
117
118
    NSInteger count = self.indexPaths.count;
    self.nothingFoundView.hidden = count > 0;
    return count;
119
120
}

121
122
123
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(nonnull NSIndexPath *)indexPath
{
    VLCRemoteBrowsingTVCell *browsingCell = (VLCRemoteBrowsingTVCell *) [collectionView dequeueReusableCellWithReuseIdentifier:VLCRemoteBrowsingTVCellIdentifier forIndexPath:indexPath];
Tobias's avatar
Tobias committed
124

125
126
    NSIndexPath *discoveryIndexPath = self.indexPaths[indexPath.row];
    id<VLCLocalNetworkService> service = [self.discoveryController networkServiceForIndexPath:discoveryIndexPath];
127
128
    if (service == nil)
        return browsingCell;
Tobias's avatar
Tobias committed
129

130
131
    browsingCell.isDirectory = YES;
    browsingCell.title = service.title;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
132
    browsingCell.titleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption2];
133
134
    browsingCell.subtitle = [self.discoveryController titleForSection:discoveryIndexPath.section];
    browsingCell.subtitleLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleCaption1];
135
136
    UIImage *serviceIcon = service.icon;
    browsingCell.thumbnailImage = serviceIcon ? serviceIcon : [UIImage imageNamed:@"serverIcon"];
137

138
139
    return browsingCell;
}
140

141
142
143
144
145
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
    NSIndexPath *discoveryIndexPath = self.indexPaths[indexPath.row];
    id<VLCLocalNetworkService> service = [self.discoveryController networkServiceForIndexPath:discoveryIndexPath];
    [self didSelectService:service];
146
147
}

148
149
150
151
#pragma mark - Service specific stuff

- (void)didSelectService:(id<VLCLocalNetworkService>)service
{
152
153
154
    if ([service respondsToSelector:@selector(serverBrowser)]) {
        id <VLCNetworkServerBrowser> browser = [service serverBrowser];
        if (browser) {
155
            VLCServerBrowsingTVViewController *browsingViewController = [[VLCServerBrowsingTVViewController alloc] initWithServerBrowser:browser];
156
157
158
            [self presentViewController:[[UINavigationController alloc] initWithRootViewController:browsingViewController]
                               animated:YES
                             completion:nil];
159
160
161
162
163
            return;
        }
    }

    if ([service respondsToSelector:@selector(loginInformation)]) {
164
165
166
167
        VLCNetworkServerLoginInformation *login = service.loginInformation;
        if (!login) return;
        [self showLoginAlertWithLogin:login];

168
169
        return;
    }
170

171
    if ([service respondsToSelector:@selector(directPlaybackURL)]) {
172
173
174
175
176
177
178
179
180

        NSURL *url = service.directPlaybackURL;
        if (!url) return;

        VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
        [vpc playURL:url subtitlesFilePath:nil];
        [self presentViewController:[VLCFullscreenMovieTVViewController fullscreenMovieTVViewController]
                           animated:YES
                         completion:nil];
181
182
        return;
    }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
}

- (void)showLoginAlertWithLogin:(nonnull VLCNetworkServerLoginInformation *)login
{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"CONNECT_TO_SERVER", nil)
                                                                             message:login.address preferredStyle:UIAlertControllerStyleAlert];

    NSURLComponents *components = [[NSURLComponents alloc] init];
    components.scheme = login.protocolIdentifier;
    components.host = login.address;
    components.port = login.port;
    NSString *serviceIdentifier = components.URL.absoluteString;
    NSString *accountName = [SSKeychain accountsForService:serviceIdentifier].firstObject[kSSKeychainAccountKey];
    NSString *password = [SSKeychain passwordForService:serviceIdentifier account:accountName];

    __block UITextField *usernameField = nil;
    __block UITextField *passwordField = nil;
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.placeholder = NSLocalizedString(@"USER_LABEL", nil);
        textField.text = accountName;
        usernameField = textField;
    }];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.secureTextEntry = YES;
        textField.placeholder = NSLocalizedString(@"PASSWORD_LABEL", nil);
        textField.text = password;
        passwordField = textField;
    }];
    [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"LOGIN", nil)
                                                        style:UIAlertActionStyleDefault
                                                      handler:^(UIAlertAction * _Nonnull action) {
                                                          login.username = usernameField.text;
                                                          login.password = passwordField.text;
                                                          [self showBrowserWithLogin:login];
                                                      }]];

    [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_SAVE", nil)
                                                        style:UIAlertActionStyleDefault
                                                      handler:^(UIAlertAction * _Nonnull action) {
                                                          NSString *accountName = usernameField.text;
                                                          NSString *password = passwordField.text;
                                                          [SSKeychain setPassword:password forService:serviceIdentifier account:accountName];
                                                          login.username = accountName;
                                                          login.password = password;
                                                          [self showBrowserWithLogin:login];
                                                      }]];
    if (accountName.length && password.length) {
        [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_DELETE", nil)
                                                            style:UIAlertActionStyleDestructive
                                                          handler:^(UIAlertAction * _Nonnull action) {
                                                              [SSKeychain deletePasswordForService:serviceIdentifier account:accountName];
                                                          }]];
235
236
237
238
239
240
241
242
    } else {
        [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_ANONYMOUS_LOGIN", nil)
                                                            style:UIAlertActionStyleDefault
                                                          handler:^(UIAlertAction * _Nonnull action) {
                                                              login.username = nil;
                                                              login.password = nil;
                                                              [self showBrowserWithLogin:login];
                                                          }]];
243
244
245
246
247
    }

    [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
                                                        style:UIAlertActionStyleCancel
                                                      handler:nil]];
248

249
250
251
252
253
254
255
256
    [self presentViewController:alertController animated:YES completion:nil];
}

- (void)showBrowserWithLogin:(nonnull VLCNetworkServerLoginInformation *)login
{
    id<VLCNetworkServerBrowser> serverBrowser = nil;
    NSString *identifier = login.protocolIdentifier;

257
258
259
    if ([identifier isEqualToString:VLCNetworkServerProtocolIdentifierFTP]) {
        serverBrowser = [[VLCNetworkServerBrowserFTP alloc] initWithLogin:login];
    } else if ([identifier isEqualToString:VLCNetworkServerProtocolIdentifierPlex]) {
260
261
262
263
264
265
        serverBrowser = [[VLCNetworkServerBrowserPlex alloc] initWithLogin:login];
    } else if ([identifier isEqualToString:VLCNetworkServerProtocolIdentifierSMB]) {
        serverBrowser = [VLCNetworkServerBrowserVLCMedia SMBNetworkServerBrowserWithLogin:login];
    }

    if (serverBrowser) {
266
        VLCServerBrowsingTVViewController *targetViewController = [[VLCServerBrowsingTVViewController alloc] initWithServerBrowser:serverBrowser];
267
268
269
270
        [self presentViewController:[[UINavigationController alloc] initWithRootViewController:targetViewController]
                           animated:YES
                         completion:nil];
    }
Tobias's avatar
Tobias committed
271
272
273
}

#pragma mark - VLCLocalServerDiscoveryController
274
275
- (void)discoveryFoundSomethingNew
{
276
277
278
279
280
281
282
283
284
285
286
287
288
    NSMutableArray<NSIndexPath *> *indexPaths = [NSMutableArray array];
    VLCLocalServerDiscoveryController *discoveryController = self.discoveryController;
    NSUInteger sectionCount = [discoveryController numberOfSections];
    for (NSUInteger section = 0; section < sectionCount; ++section) {
        NSUInteger itemsCount = [discoveryController numberOfItemsInSection:section];
        for (NSUInteger index = 0; index < itemsCount; ++index) {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:section];
            [indexPaths addObject:indexPath];
        }
    }
    self.indexPaths = [indexPaths copy];

    [self.collectionView reloadData];
289
290

    _nothingFoundLabel.hidden = self.discoveryController.foundAnythingAtAll;
Tobias's avatar
Tobias committed
291
292
293
}

@end