VLCBoxTableViewController.m 12.2 KB
Newer Older
Carola Nitz's avatar
Carola Nitz committed
1 2 3 4
/*****************************************************************************
 * VLCBoxTableViewController.m
 * VLC for iOS
 *****************************************************************************
5
 * Copyright (c) 2014-2015 VideoLAN. All rights reserved.
Carola Nitz's avatar
Carola Nitz committed
6 7 8 9 10 11 12 13
 * $Id$
 *
 * Authors: Carola Nitz <nitz.carola # googlemail.com>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/

#import "VLCBoxTableViewController.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
14
#import "VLCCloudStorageTableViewCell.h"
Carola Nitz's avatar
Carola Nitz committed
15
#import "VLCBoxController.h"
16
#import <XKKeychain/XKKeychainGenericPasswordItem.h>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
17
#import "UIDevice+VLC.h"
18
#import "VLCPlaybackController.h"
Carola Nitz's avatar
Carola Nitz committed
19

Felix Paul Kühne's avatar
Felix Paul Kühne committed
20
#if TARGET_OS_IOS
21
@interface VLCBoxTableViewController () <VLCCloudStorageTableViewCell, BoxAuthorizationViewControllerDelegate, VLCCloudStorageDelegate, NSURLConnectionDataDelegate>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
22
#else
23
@interface VLCBoxTableViewController () <VLCCloudStorageTableViewCell, VLCCloudStorageDelegate, NSURLConnectionDataDelegate>
Felix Paul Kühne's avatar
Felix Paul Kühne committed
24
#endif
Carola Nitz's avatar
Carola Nitz committed
25 26 27
{
    BoxFile *_selectedFile;
    VLCBoxController *_boxController;
28
    NSArray *_listOfFiles;
Carola Nitz's avatar
Carola Nitz committed
29 30 31 32 33 34
}

@end

@implementation VLCBoxTableViewController

35 36 37 38 39 40 41 42 43
- (instancetype)initWithPath:(NSString *)path
{
    self = [super init];
    if (self) {
        self.currentPath = path;
    }
    return self;
}

44 45 46 47 48
- (void)dealloc
{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

Carola Nitz's avatar
Carola Nitz committed
49 50 51 52
- (void)viewDidLoad
{
    [super viewDidLoad];

53
    _boxController = [VLCBoxController sharedInstance];
54 55
    self.controller = _boxController;
    self.controller.delegate = self;
Carola Nitz's avatar
Carola Nitz committed
56

57
#if TARGET_OS_IOS
58
    self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"Box"]];
Carola Nitz's avatar
Carola Nitz committed
59

60
    [self.cloudStorageLogo setImage:[UIImage imageNamed:@"Box"]];
Carola Nitz's avatar
Carola Nitz committed
61

62 63
    [self.cloudStorageLogo sizeToFit];
    self.cloudStorageLogo.center = self.view.center;
64 65
#else
    self.title = @"Box";
66 67
#endif

Carola Nitz's avatar
Carola Nitz committed
68
    // Handle logged in
Felix Paul Kühne's avatar
Felix Paul Kühne committed
69 70 71 72 73
    NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter];
    [defaultCenter addObserver:self
                      selector:@selector(boxApiTokenDidRefresh)
                          name:BoxOAuth2SessionDidRefreshTokensNotification
                        object:[BoxSDK sharedSDK].OAuth2Session];
Carola Nitz's avatar
Carola Nitz committed
74

Felix Paul Kühne's avatar
Felix Paul Kühne committed
75 76 77 78
    [defaultCenter addObserver:self
                      selector:@selector(boxApiTokenDidRefresh)
                          name:BoxOAuth2SessionDidBecomeAuthenticatedNotification
                        object:[BoxSDK sharedSDK].OAuth2Session];
79 80

#if TARGET_OS_IOS
Carola Nitz's avatar
Carola Nitz committed
81
    // Handle logout
Felix Paul Kühne's avatar
Felix Paul Kühne committed
82 83 84 85 86 87 88 89
    [defaultCenter addObserver:self
                      selector:@selector(boxDidGetLoggedOut)
                          name:BoxOAuth2SessionDidReceiveAuthenticationErrorNotification
                        object:[BoxSDK sharedSDK].OAuth2Session];
    [defaultCenter addObserver:self
                      selector:@selector(boxDidGetLoggedOut)
                          name:BoxOAuth2SessionDidReceiveRefreshErrorNotification
                        object:[BoxSDK sharedSDK].OAuth2Session];
Carola Nitz's avatar
Carola Nitz committed
90

Felix Paul Kühne's avatar
Felix Paul Kühne committed
91 92 93 94 95 96 97 98
    [defaultCenter addObserver:self
                      selector:@selector(boxAPIAuthenticationDidFail)
                          name:BoxOAuth2SessionDidReceiveAuthenticationErrorNotification
                        object:[BoxSDK sharedSDK].OAuth2Session];
    [defaultCenter addObserver:self
                      selector:@selector(boxAPIInitiateLogin)
                          name:BoxOAuth2SessionDidReceiveRefreshErrorNotification
                        object:[BoxSDK sharedSDK].OAuth2Session];
99
#endif
Carola Nitz's avatar
Carola Nitz committed
100 101
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
102
#if TARGET_OS_IOS
Carola Nitz's avatar
Carola Nitz committed
103 104 105
- (UIViewController *)createAuthController
{
    NSURL *authorizationURL = [[BoxSDK sharedSDK].OAuth2Session authorizeURL];
106 107
    NSString *redirectURLString = [[BoxSDK sharedSDK].OAuth2Session redirectURIString];
    BoxAuthorizationViewController *authorizationController = [[BoxAuthorizationViewController alloc] initWithAuthorizationURL:authorizationURL redirectURI:redirectURLString];
Carola Nitz's avatar
Carola Nitz committed
108 109 110
    authorizationController.delegate = self;
    return authorizationController;
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
111
#endif
Carola Nitz's avatar
Carola Nitz committed
112 113 114

- (void)viewWillAppear:(BOOL)animated
{
115
    [super viewWillAppear:animated];
116 117 118 119 120 121

    _boxController = [VLCBoxController sharedInstance];
    self.controller = _boxController;
    self.controller.delegate = self;

    if (!_listOfFiles || _listOfFiles.count == 0)
122
        [self requestInformationForCurrentPath];
Carola Nitz's avatar
Carola Nitz committed
123 124 125 126
}

- (void)viewWillDisappear:(BOOL)animated
{
127
    [super viewWillDisappear:animated];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
128
    if ([UIApplication sharedApplication].delegate.window.rootViewController.presentedViewController == nil) {
Carola Nitz's avatar
Carola Nitz committed
129 130 131 132 133 134 135
        [_boxController stopSession];
        [self.tableView reloadData];
    }
}

#pragma mark - Table view data source

136 137 138 139 140 141
- (void)mediaListUpdated
{
    _listOfFiles = [[VLCBoxController sharedInstance].currentListFiles copy];
    [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO];
}

142
- (VLCCloudStorageTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
Carola Nitz's avatar
Carola Nitz committed
143 144 145 146 147 148 149
{
    static NSString *CellIdentifier = @"BoxCell";

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

150 151 152 153 154 155 156
    NSUInteger index = indexPath.row;
    if (_listOfFiles) {
        if (index < _listOfFiles.count) {
            cell.boxFile = _listOfFiles[index];
            cell.delegate = self;
        }
    }
Carola Nitz's avatar
Carola Nitz committed
157 158 159 160

    return cell;
}

161 162 163 164 165
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _listOfFiles.count;
}

Carola Nitz's avatar
Carola Nitz committed
166 167
#pragma mark - Table view delegate

168

Carola Nitz's avatar
Carola Nitz committed
169 170
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
Felix Paul Kühne's avatar
Felix Paul Kühne committed
171
    [self.tableView deselectRowAtIndexPath:indexPath animated:NO];
172
    if (indexPath.row >= _listOfFiles.count)
Felix Paul Kühne's avatar
Felix Paul Kühne committed
173 174
        return;

175
    _selectedFile = _listOfFiles[indexPath.row];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
176
    if (![_selectedFile.type isEqualToString:@"folder"])
177
        [self streamFile:(BoxFile *)_selectedFile];
Felix Paul Kühne's avatar
Felix Paul Kühne committed
178
    else {
Carola Nitz's avatar
Carola Nitz committed
179
        /* dive into subdirectory */
180 181 182 183 184 185
        NSString *path = self.currentPath;
        if (![path isEqualToString:@""])
            path = [path stringByAppendingString:@"/"];
        path = [path stringByAppendingString:_selectedFile.modelID];

        self.currentPath = path;
186
        [self requestInformationForCurrentPath];
Carola Nitz's avatar
Carola Nitz committed
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
- (void)streamFile:(BoxFile *)file
{
    /* the Box API requires us to set an HTTP header to get the actual URL:
     * curl -L https://api.box.com/2.0/files/FILE_ID/content -H "Authorization: Bearer ACCESS_TOKEN"
     *
     * ... however, libvlc does not support setting custom HTTP headers, so we are resolving the redirect ourselves with a NSURLConnection
     * and pass the final location to libvlc, which does not require a custom HTTP header */

    NSURL *baseURL = [[[BoxSDK sharedSDK] filesManager] URLWithResource:@"files"
                                                                     ID:file.modelID
                                                            subresource:@"content"
                                                                  subID:nil];

    NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:baseURL
                                                              cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                                          timeoutInterval:60];

    [urlRequest setValue:[NSString stringWithFormat:@"Bearer %@", [BoxSDK sharedSDK].OAuth2Session.accessToken] forHTTPHeaderField:@"Authorization"];

    NSURLConnection *theTestConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
    [theTestConnection start];
}

- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
{
    if (response != nil) {
        /* we have 1 redirect from the original URL, so as soon as we'd do that,
         * we grab the URL and cancel the connection */
        NSURL *theActualURL = request.URL;

        [connection cancel];

        /* now ask VLC to stream the URL we were just passed */
        VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
        [vpc playURL:theActualURL successCallback:nil errorCallback:nil];
    }

    return request;
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
230
#if TARGET_OS_IOS
Carola Nitz's avatar
Carola Nitz committed
231 232
- (void)triggerDownloadForCell:(VLCCloudStorageTableViewCell *)cell
{
233
    _selectedFile = _listOfFiles[[self.tableView indexPathForCell:cell].row];
Carola Nitz's avatar
Carola Nitz committed
234

235 236
    if (_selectedFile.size.longLongValue < [[UIDevice currentDevice] freeDiskspace].longLongValue) {
        /* selected item is a proper file, ask the user if s/he wants to download it */
237
        VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DROPBOX_DOWNLOAD", nil) message:[NSString stringWithFormat:NSLocalizedString(@"DROPBOX_DL_LONG", nil), _selectedFile.name, [[UIDevice currentDevice] model]] delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil) otherButtonTitles:NSLocalizedString(@"BUTTON_DOWNLOAD", nil), nil];
238 239
        [alert show];
    } else {
240
        VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil) message:[NSString stringWithFormat:NSLocalizedString(@"DISK_FULL_FORMAT", nil), _selectedFile.name, [[UIDevice currentDevice] model]] delegate:self cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil) otherButtonTitles:nil];
241 242
        [alert show];
    }
Carola Nitz's avatar
Carola Nitz committed
243 244 245 246 247 248 249 250
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1)
        [_boxController downloadFileToDocumentFolder:_selectedFile];
    _selectedFile = nil;
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
251
#endif
Carola Nitz's avatar
Carola Nitz committed
252

253
#pragma mark - box controller delegate
Carola Nitz's avatar
Carola Nitz committed
254 255 256

#pragma mark - BoxAuthorizationViewControllerDelegate

Felix Paul Kühne's avatar
Felix Paul Kühne committed
257 258 259
- (void)boxApiTokenDidRefresh
{
    NSString *token = [BoxSDK sharedSDK].OAuth2Session.refreshToken;
260 261 262 263 264 265 266

    XKKeychainGenericPasswordItem *keychainItem = [[XKKeychainGenericPasswordItem alloc] init];
    keychainItem.service = kVLCBoxService;
    keychainItem.account = kVLCBoxAccount;
    keychainItem.secret.stringValue = token;
    [keychainItem saveWithError:nil];

Felix Paul Kühne's avatar
Felix Paul Kühne committed
267 268 269 270 271 272 273 274
    NSUbiquitousKeyValueStore *ubiquitousStore = [NSUbiquitousKeyValueStore defaultStore];
    [ubiquitousStore setString:token forKey:kVLCStoreBoxCredentials];
    [ubiquitousStore synchronize];
    self.authorizationInProgress = YES;
    [self updateViewAfterSessionChange];
    self.authorizationInProgress = NO;
}

Felix Paul Kühne's avatar
Felix Paul Kühne committed
275
#if TARGET_OS_IOS
Carola Nitz's avatar
Carola Nitz committed
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
- (BOOL)authorizationViewController:(BoxAuthorizationViewController *)authorizationViewController shouldLoadReceivedOAuth2RedirectRequest:(NSURLRequest *)request
{
    [[BoxSDK sharedSDK].OAuth2Session performAuthorizationCodeGrantWithReceivedURL:request.URL];
    [self.navigationController popViewControllerAnimated:YES];
    return NO;
}

- (void)authorizationViewControllerDidStartLoading:(BoxAuthorizationViewController *)authorizationViewController
{
    //needs to be implemented
}

- (void)authorizationViewControllerDidFinishLoading:(BoxAuthorizationViewController *)authorizationViewController
{
    //needs to be implemented
}

- (void)boxDidGetLoggedOut
{
295
    [self performSelectorOnMainThread:@selector(showLoginPanel) withObject:nil waitUntilDone:NO];
Carola Nitz's avatar
Carola Nitz committed
296 297 298 299 300 301 302 303 304
}

- (void)boxAPIAuthenticationDidFail
{
    //needs to be implemented
}

- (void)boxAPIInitiateLogin
{
305
    [self performSelectorOnMainThread:@selector(showLoginPanel) withObject:nil waitUntilDone:NO];
Carola Nitz's avatar
Carola Nitz committed
306 307 308 309 310 311
}

- (void)authorizationViewControllerDidCancel:(BoxAuthorizationViewController *)authorizationViewController
{
    [self.navigationController popViewControllerAnimated:YES];
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
312
#endif
313

314
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
315
{
316 317 318 319 320
    NSInteger currentOffset = scrollView.contentOffset.y;
    NSInteger maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;

    if (maximumOffset - currentOffset <= - self.tableView.rowHeight) {
        if (_boxController.hasMoreFiles && !self.activityIndicator.isAnimating) {
321
            [self requestInformationForCurrentPath];
322 323 324
        }
    }
}
Carola Nitz's avatar
Carola Nitz committed
325 326
#pragma mark - login dialog

Felix Paul Kühne's avatar
Felix Paul Kühne committed
327
#if TARGET_OS_IOS
Carola Nitz's avatar
Carola Nitz committed
328 329 330
- (IBAction)loginAction:(id)sender
{
    if (![_boxController isAuthorized]) {
331
        self.authorizationInProgress = YES;
Carola Nitz's avatar
Carola Nitz committed
332 333 334 335 336
        [self.navigationController pushViewController:[self createAuthController] animated:YES];
    } else {
        [_boxController logout];
    }
}
Felix Paul Kühne's avatar
Felix Paul Kühne committed
337
#endif
Carola Nitz's avatar
Carola Nitz committed
338 339

@end