Commit 6f5a9002 authored by Carola Nitz's avatar Carola Nitz

Folder functionality for the library

parent 79e1b498
......@@ -63,6 +63,9 @@ Copyright &copy; 2011-2013 Google Inc. - <a href="#apache2">Apache 2 License</a>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://www.inappsettingskit.com">InAppSettingsKit</a><br />
Copyright &copy; 2009-2013 Luc Vandal, Edovia Inc., Ortwin Gentz, FutureTap GmbH - <a href="#bsd2clause">2-clause BSD License</a><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://github.com/lxcid/LXReorderableCollectionViewFlowLayout">LXReorderableCollectionViewFlowLayout</a><br />
Copyright &copy; 2012 Stan Chang Khin Boon and contributors - <a href="#mit">MIT License</a><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;<a href="https://github.com/ole/OBSlider">OBSlider</a><br />
Copyright &copy; 2011 Ole Begemann and contributors - <a href="#mit">MIT License</a><br />
<br />
......
B"CHOOSE_AUDIO_TRACK"="Choose Audio Track";
......
/*****************************************************************************
* VLCFolderCollectionViewFlowLayout.h
* VLC for iOS
*****************************************************************************
* Copyright (c) 2014 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Carola Nitz <nitz.carola # googlemail.com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
@interface VLCFolderCollectionViewFlowLayout : UICollectionViewFlowLayout
@property (assign, nonatomic) CGFloat scrollingSpeed;
@property (assign, nonatomic) UIEdgeInsets scrollingTriggerEdgeInsets;
@property (strong, nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer;
@end
@protocol VLCFolderCollectionViewDelegateFlowLayout <UICollectionViewDelegateFlowLayout>
- (void)collectionView:(UICollectionView *)collectionView requestToMoveItemAtIndexPath:(NSIndexPath *)currentPath intoFolderAtIndexPath:(NSIndexPath *)newIndexPath;
@end
\ No newline at end of file
/*****************************************************************************
* VLCFolderCollectionViewFlowLayout.m
* VLC for iOS
*****************************************************************************
* Copyright (c) 2014 VideoLAN. All rights reserved.
* $Id$
*
* Authors: Carola Nitz <nitz.carola # googlemail.com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
#import "VLCFolderCollectionViewFlowLayout.h"
#import <objc/runtime.h>
#import "VLCPlaylistViewController.h"
//framrate were motion appears fluent
#define LX_FRAMES_PER_SECOND 60.0
#ifndef CGGEOMETRY_LXSUPPORT_H_
CG_INLINE CGPoint
LXS_CGPointAdd(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x + point2.x, point1.y + point2.y);
}
#endif
typedef NS_ENUM(NSInteger, LXScrollingDirection) {
LXScrollingDirectionUnknown = 0,
LXScrollingDirectionUp,
LXScrollingDirectionDown,
LXScrollingDirectionLeft,
LXScrollingDirectionRight
};
static NSString * const kLXScrollingDirectionKey = @"LXScrollingDirection";
static NSString * const kLXCollectionViewKeyPath = @"collectionView";
@interface CADisplayLink (LX_userInfo)
@property (nonatomic, copy) NSDictionary *LX_userInfo;
@end
@implementation CADisplayLink (LX_userInfo)
- (void) setLX_userInfo:(NSDictionary *) LX_userInfo {
objc_setAssociatedObject(self, "LX_userInfo", LX_userInfo, OBJC_ASSOCIATION_COPY);
}
- (NSDictionary *) LX_userInfo {
return objc_getAssociatedObject(self, "LX_userInfo");
}
@end
@interface UICollectionViewCell (VLCFolderCollectionViewLayout)
- (UIImage *)LX_rasterizedImage;
@end
@implementation UICollectionViewCell (VLCFolderCollectionViewLayout)
- (UIImage *)LX_rasterizedImage {
UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0f);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}
@end
@interface VLCFolderCollectionViewFlowLayout ()
{
NSIndexPath *_selectedItemIndexPath;
UIView *_currentView;
CGPoint _currentViewCenter;
CGPoint _panTranslationInCollectionView;
CADisplayLink *_displayLink;
UIView *_folderView;
}
@end
@implementation VLCFolderCollectionViewFlowLayout
- (void)setDefaults {
_scrollingSpeed = 300.0f;
_scrollingTriggerEdgeInsets = UIEdgeInsetsMake(50.0f, 50.0f, 50.0f, 50.0f);
}
- (void)setupCollectionView {
_panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self
action:@selector(handlePanGesture:)];
for (UIGestureRecognizer *gestureRecognizer in self.collectionView.gestureRecognizers) {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
[gestureRecognizer requireGestureRecognizerToFail:_panGestureRecognizer];
}
[self.collectionView addGestureRecognizer:_panGestureRecognizer];
// Useful in multiple scenarios: one common scenario being when the Notification Center drawer is pulled down
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleApplicationWillResignActive:) name: UIApplicationWillResignActiveNotification object:nil];
}
- (id)init {
self = [super init];
if (self) {
[self setDefaults];
[self addObserver:self forKeyPath:kLXCollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setDefaults];
[self addObserver:self forKeyPath:kLXCollectionViewKeyPath options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)dealloc {
[self invalidatesScrollTimer];
[self removeObserver:self forKeyPath:kLXCollectionViewKeyPath];
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIApplicationWillResignActiveNotification object:nil];
}
- (void)applyLayoutAttributes:(UICollectionViewLayoutAttributes *)layoutAttributes {
if ([layoutAttributes.indexPath isEqual:_selectedItemIndexPath])
layoutAttributes.hidden = YES;
}
- (id<VLCFolderCollectionViewDelegateFlowLayout>)delegate {
return (id<VLCFolderCollectionViewDelegateFlowLayout>)self.collectionView.delegate;
}
- (void)invalidateLayoutIfNecessary {
NSIndexPath *newIndexPath = [self.collectionView indexPathForItemAtPoint:_currentView.center];
NSIndexPath *previousIndexPath = _selectedItemIndexPath;
if ((newIndexPath == nil) || [newIndexPath isEqual:previousIndexPath]) {
_currentView.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
[_folderView removeFromSuperview];
return;
}
UICollectionViewCell *cell = [self.collectionView.dataSource collectionView:self.collectionView cellForItemAtIndexPath:newIndexPath];
if (!_folderView) {
_folderView = [[UIView alloc] initWithFrame:cell.frame];
_folderView.backgroundColor = [UIColor colorWithRed:(236.0f/255.0f) green:(111.0f/255.0f) blue:0.0f alpha:1.f];
_folderView.layer.cornerRadius = 8;
}
[self.collectionView insertSubview:_folderView atIndex:0];
if (!CGPointEqualToPoint(_folderView.center,cell.center))
_folderView.frame = cell.frame;
[UIView
animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
_currentView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
}
completion:nil];
}
- (void)invalidatesScrollTimer {
if (!_displayLink.paused)
[_displayLink invalidate];
_displayLink = nil;
}
- (void)setupScrollTimerInDirection:(LXScrollingDirection)direction {
if (!_displayLink.paused) {
LXScrollingDirection oldDirection = [_displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue];
if (direction == oldDirection)
return;
}
[self invalidatesScrollTimer];
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleScroll:)];
_displayLink.LX_userInfo = @{ kLXScrollingDirectionKey : @(direction) };
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
#pragma mark - Target/Action methods
// Tight loop, allocate memory sparely, even if they are stack allocation.
- (void)handleScroll:(CADisplayLink *)displayLink {
LXScrollingDirection direction = (LXScrollingDirection)[displayLink.LX_userInfo[kLXScrollingDirectionKey] integerValue];
if (direction == LXScrollingDirectionUnknown)
return;
CGSize frameSize = self.collectionView.bounds.size;
CGSize contentSize = self.collectionView.contentSize;
CGPoint contentOffset = self.collectionView.contentOffset;
// Important to have an integer `distance` as the `contentOffset` property automatically gets rounded
// and it would diverge from the view's center resulting in a "cell is slipping away under finger"-bug.
CGFloat distance = rint(self.scrollingSpeed / LX_FRAMES_PER_SECOND);
CGPoint translation = CGPointZero;
switch(direction) {
case LXScrollingDirectionUp: {
distance = -distance;
CGFloat minY = 0.0f;
if ((contentOffset.y + distance) <= minY)
distance = -contentOffset.y;
translation = CGPointMake(0.0f, distance);
} break;
case LXScrollingDirectionDown: {
CGFloat maxY = MAX(contentSize.height, frameSize.height) - frameSize.height;
if ((contentOffset.y + distance) >= maxY)
distance = maxY - contentOffset.y;
translation = CGPointMake(0.0f, distance);
} break;
case LXScrollingDirectionLeft: {
distance = -distance;
CGFloat minX = 0.0f;
if ((contentOffset.x + distance) <= minX)
distance = -contentOffset.x;
translation = CGPointMake(distance, 0.0f);
} break;
case LXScrollingDirectionRight: {
CGFloat maxX = MAX(contentSize.width, frameSize.width) - frameSize.width;
if ((contentOffset.x + distance) >= maxX)
distance = maxX - contentOffset.x;
translation = CGPointMake(distance, 0.0f);
} break;
default: {
// Do nothing...
} break;
}
_currentViewCenter = LXS_CGPointAdd(_currentViewCenter, translation);
_currentView.center = LXS_CGPointAdd(_currentViewCenter, _panTranslationInCollectionView);
self.collectionView.contentOffset = LXS_CGPointAdd(contentOffset, translation);
}
- (void)handlePanGesture:(UIPanGestureRecognizer *)gestureRecognizer {
//keeps the controller from dragging while not in editmode
if (!((VLCPlaylistViewController *)self.delegate).isEditing) return;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan: {
NSIndexPath *currentIndexPath = [self.collectionView indexPathForItemAtPoint:[gestureRecognizer locationInView:self.collectionView]];
_selectedItemIndexPath = currentIndexPath;
UICollectionViewCell *collectionViewCell = [self.collectionView cellForItemAtIndexPath:_selectedItemIndexPath];
_currentView = [[UIView alloc] initWithFrame:collectionViewCell.frame];
collectionViewCell.highlighted = YES;
UIImageView *highlightedImageView = [[UIImageView alloc] initWithImage:[collectionViewCell LX_rasterizedImage]];
highlightedImageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
highlightedImageView.alpha = 1.0f;
collectionViewCell.highlighted = NO;
UIImageView *imageView = [[UIImageView alloc] initWithImage:[collectionViewCell LX_rasterizedImage]];
imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
imageView.alpha = 0.0f;
[_currentView addSubview:imageView];
[_currentView addSubview:highlightedImageView];
[self.collectionView addSubview:_currentView];
_currentViewCenter = _currentView.center;
[UIView
animateWithDuration:0.3
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
_currentView.transform = CGAffineTransformMakeScale(1.1f, 1.1f);
highlightedImageView.alpha = 0.0f;
imageView.alpha = 1.0f;
}
completion:^(BOOL finished) {
[highlightedImageView removeFromSuperview];
}];
[self invalidateLayout];
break;
}
case UIGestureRecognizerStateChanged: {
_panTranslationInCollectionView = [gestureRecognizer translationInView:self.collectionView];
CGPoint viewCenter = _currentView.center = LXS_CGPointAdd(_currentViewCenter, _panTranslationInCollectionView);
[self invalidateLayoutIfNecessary];
switch (self.scrollDirection) {
case UICollectionViewScrollDirectionVertical: {
if (viewCenter.y < (CGRectGetMinY(self.collectionView.bounds) + self.scrollingTriggerEdgeInsets.top)) {
[self setupScrollTimerInDirection:LXScrollingDirectionUp];
} else {
if (viewCenter.y > (CGRectGetMaxY(self.collectionView.bounds) - self.scrollingTriggerEdgeInsets.bottom)) {
[self setupScrollTimerInDirection:LXScrollingDirectionDown];
} else {
[self invalidatesScrollTimer];
}
}
} break;
case UICollectionViewScrollDirectionHorizontal: {
if (viewCenter.x < (CGRectGetMinX(self.collectionView.bounds) + self.scrollingTriggerEdgeInsets.left)) {
[self setupScrollTimerInDirection:LXScrollingDirectionLeft];
} else {
if (viewCenter.x > (CGRectGetMaxX(self.collectionView.bounds) - self.scrollingTriggerEdgeInsets.right)) {
[self setupScrollTimerInDirection:LXScrollingDirectionRight];
} else {
[self invalidatesScrollTimer];
}
}
} break;
}
} break;
case UIGestureRecognizerStateCancelled:
case UIGestureRecognizerStateEnded: {
[_folderView removeFromSuperview];
_folderView = nil;
NSIndexPath *newIndexPath = [self.collectionView indexPathForItemAtPoint:_currentView.center];
NSIndexPath *currentIndexPath = _selectedItemIndexPath;
if (newIndexPath != nil && ![currentIndexPath isEqual:newIndexPath]) {
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
_currentView.transform = CGAffineTransformMakeScale(0.1f, 0.1f);
_currentView.center = [self layoutAttributesForItemAtIndexPath:newIndexPath].center;
} completion:^(BOOL finished) {
[self.delegate collectionView:self.collectionView requestToMoveItemAtIndexPath:currentIndexPath intoFolderAtIndexPath:newIndexPath];
_selectedItemIndexPath = nil;
_currentViewCenter = CGPointZero;
[_currentView removeFromSuperview];
_currentView = nil;
}];
} else if (currentIndexPath) {
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState animations:^{
_currentView.center = [self layoutAttributesForItemAtIndexPath:currentIndexPath].center;
} completion:^(BOOL finished) {
_selectedItemIndexPath = nil;
_currentViewCenter = CGPointZero;
[_currentView removeFromSuperview];
_currentView = nil;
[self invalidateLayout];
}];
}
[self invalidatesScrollTimer];
} break;
default: {
// Do nothing...
} break;
}
}
#pragma mark - UICollectionViewLayout overridden methods
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect {
NSArray *layoutAttributesForElementsInRect = [super layoutAttributesForElementsInRect:rect];
for (UICollectionViewLayoutAttributes *layoutAttributes in layoutAttributesForElementsInRect) {
switch (layoutAttributes.representedElementCategory) {
case UICollectionElementCategoryCell: {
[self applyLayoutAttributes:layoutAttributes];
} break;
default: {
// Do nothing...
} break;
}
}
return layoutAttributesForElementsInRect;
}
- (UICollectionViewLayoutAttributes *)layoutAttributesForItemAtIndexPath:(NSIndexPath *)indexPath {
UICollectionViewLayoutAttributes *layoutAttributes = [super layoutAttributesForItemAtIndexPath:indexPath];
switch (layoutAttributes.representedElementCategory) {
case UICollectionElementCategoryCell: {
[self applyLayoutAttributes:layoutAttributes];
} break;
default: {
// Do nothing...
} break;
}
return layoutAttributes;
}
#pragma mark - Key-Value Observing methods
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:kLXCollectionViewKeyPath]) {
if (self.collectionView != nil) {
[self setupCollectionView];
} else {
[self invalidatesScrollTimer];
}
}
}
#pragma mark - Notifications
- (void)handleApplicationWillResignActive:(NSNotification *)notification {
self.panGestureRecognizer.enabled = NO;
self.panGestureRecognizer.enabled = YES;
}
@end
......@@ -30,5 +30,6 @@
- (void)setEditing:(BOOL)editing animated:(BOOL)animated;
- (void)selectionUpdate;
- (void)shake:(BOOL)shake;
@end
......@@ -8,6 +8,7 @@
* Authors: Felix Paul Kühne <fkuehne # videolan.org>
* Tamas Timar <ttimar.vlc # gmail.com>
* Gleb Pinigin <gpinigin # gmail.com>
* Carola Nitz <nitz.carola # googlemail.com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
......@@ -40,6 +41,8 @@
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
self.isSelectedView.hidden = !editing;
if ([_mediaObject isKindOfClass:[MLLabel class]] || [_mediaObject.labels count] == 0)
[self shake:editing];
[self selectionUpdate];
[self _updatedDisplayedInformationForKeyPath:@"editing"];
}
......@@ -52,6 +55,32 @@
self.isSelectedView.image = _checkboxEmptyImage;
}
- (void)shake:(BOOL)shake
{
if (shake) {
[UIView animateWithDuration:0.3 animations:^{
self.contentView.transform = CGAffineTransformMakeScale(0.9f, 0.9f);
}];
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
CGFloat shakeAngle = 0.02f;
animation.values = @[@(-shakeAngle), @(shakeAngle)];
animation.autoreverses = YES;
animation.duration = 0.125;
animation.repeatCount = HUGE_VALF;
[[self layer] addAnimation:animation forKey:@"shakeAnimation"];
self.contentView.layer.cornerRadius = 10.0;
self.contentView.clipsToBounds = YES;
} else {
[UIView animateWithDuration:0.3 animations:^{
self.contentView.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
self.contentView.layer.cornerRadius = 0.0;
self.contentView.clipsToBounds = NO;
}];
[[self layer] removeAnimationForKey:@"shakeAnimation"];
}
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
[self _updatedDisplayedInformationForKeyPath:keyPath];
......@@ -97,12 +126,50 @@
- (void)_updatedDisplayedInformationForKeyPath:(NSString *)keyPath
{
BOOL isFolder = [self.mediaObject isKindOfClass:[MLLabel class]];
self.thumbnailView.contentMode = isFolder ? UIViewContentModeScaleAspectFit : UIViewContentModeScaleAspectFill;
if ([self.mediaObject isKindOfClass:[MLFile class]]) {
MLFile *mediaObject = self.mediaObject;
[self _configureForMLFile:mediaObject];
if (([keyPath isEqualToString:@"computedThumbnail"] || !keyPath) || (!self.thumbnailView.image && [keyPath isEqualToString:@"editing"]))
self.thumbnailView.image = [VLCThumbnailsCache thumbnailForMediaFile:mediaObject];
} else if ([self.mediaObject isKindOfClass:[MLLabel class]]) {
MLLabel *mediaObject = (MLLabel *)self.mediaObject;
[self _configureForFolder:mediaObject];
if (([keyPath isEqualToString:@"computedThumbnail"] || !keyPath) || (!self.thumbnailView.image && [keyPath isEqualToString:@"editing"])) {
NSUInteger fileNumber = [mediaObject.files count] > 3 ? 3 : [mediaObject.files count];
if (fileNumber == 0) {
self.thumbnailView.image = [UIImage imageNamed:@"folderIcon"];
} else {
NSArray *files = [mediaObject.files allObjects];
CGSize frameSize = self.thumbnailView.frame.size;
UIImage *displayedImage;
UIGraphicsBeginImageContext(frameSize);
for (NSUInteger i = 0; i < fileNumber; i++) {
MLFile *file = [files objectAtIndex:i];
displayedImage = [VLCThumbnailsCache thumbnailForMediaFile:file];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat imagePartWidth = (frameSize.width / fileNumber);
//the rect in which the image should be drawn
CGRect clippingRect = CGRectMake(imagePartWidth * i, 0, imagePartWidth, frameSize.height);
CGContextSaveGState(context);
CGContextClipToRect(context, clippingRect);
//take the center of the clippingRect and calculate the offset from the original center
CGFloat centerOffset = (imagePartWidth * i + imagePartWidth / 2) - frameSize.width / 2;
//shift the rect to draw the middle of the image in the clippingrect
CGRect drawingRect = CGRectMake(centerOffset, 0, frameSize.width, frameSize.height);
[displayedImage drawInRect:drawingRect];
//get rid of the old clippingRect
CGContextRestoreGState(context);
}
displayedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.thumbnailView.image = displayedImage;
}
}
} else if ([self.mediaObject isKindOfClass:[MLAlbum class]]) {
MLAlbum *mediaObject = (MLAlbum *)self.mediaObject;
[self _configureForAlbum:mediaObject];
......@@ -174,7 +241,6 @@
self.subtitleLabel.text = [NSString stringWithFormat:@"%@", [VLCTime timeWithNumber:[anyFileFromEpisode duration]]];
} else
self.subtitleLabel.text = [NSString stringWithFormat:@"S%02dE%02d — %@", showEpisode.seasonNumber.intValue, showEpisode.episodeNumber.intValue, [VLCTime timeWithNumber:[anyFileFromEpisode duration]]];
[self _showPositionOfItem:anyFileFromEpisode];
}
......@@ -196,6 +262,15 @@
self.progressView.hidden = YES;
}
- (void)_configureForFolder:(MLLabel *)label
{
self.titleLabel.text = label.name;
NSUInteger count = label.files.count;
self.subtitleLabel.text = [NSString stringWithFormat:(count == 1) ? NSLocalizedString(@"LIBRARY_TRACKS", @"") : NSLocalizedString(@"LIBRARY_SINGLE_TRACK", @""), count];
self.mediaIsUnreadView.hidden = YES;
self.progressView.hidden = YES;
}
- (void)_configureForMLFile:(MLFile *)mediaFile
{
if (mediaFile.isAlbumTrack) {
......@@ -221,7 +296,6 @@
self.subtitleLabel.text = [self.subtitleLabel.text stringByAppendingFormat:@" — %@x%@", width, height];
}
}
[self _showPositionOfItem:mediaFile];
}
......
......@@ -7,6 +7,7 @@
*
* Authors: Felix Paul Kühne <fkuehne # videolan.org>
* Gleb Pinigin <gpinigin # gmail.com>
* Carola Nitz <nitz.carola #googlemail.com>
*
* Refer to the COPYING file of the official project for license.
*****************************************************************************/
......@@ -84,12 +85,57 @@
- (void)_updatedDisplayedInformationForKeyPath:(NSString *)keyPath
{
BOOL isFolder = [self.mediaObject isKindOfClass:[MLLabel class]];
self.thumbnailView.contentMode = isFolder ? UIViewContentModeScaleAspectFit : UIViewContentModeScaleAspectFill;
if ([self.mediaObject isKindOfClass:[MLFile class]]) {
MLFile *mediaObject = (MLFile*)self.mediaObject;
[self _configureForMLFile:mediaObject];
if (([keyPath isEqualToString:@"computedThumbnail"] || !keyPath || (!self.thumbnailView.image && [keyPath isEqualToString:@"editing"])))
self.thumbnailView.image = [VLCThumbnailsCache thumbnailForMediaFile:mediaObject];
} else if (isFolder) {
MLLabel *mediaObject = (MLLabel *)self.mediaObject;
[self _configureForFolder:mediaObject];
if (([keyPath isEqualToString:@"computedThumbnail"] || !keyPath) || (!self.thumbnailView.image && [keyPath isEqualToString:@"editing"])) {
NSUInteger fileNumber = [mediaObject.files count] > 4 ? 4 : [mediaObject.files count];
if (fileNumber == 0) {
self.thumbnailView.image = [UIImage imageNamed:@"folderIcon"];
} else {
NSArray *files = [mediaObject.files allObjects];
CGSize frameSize = self.thumbnailView.frame.size;
UIImage *displayedImage;
UIGraphicsBeginImageContext(frameSize);
for (NSUInteger i = 0; i < fileNumber; i++) {
MLFile *file = [files objectAtIndex:i];
displayedImage = [VLCThumbnailsCache thumbnailForMediaFile:file];
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat imagePartWidth = (frameSize.width / fileNumber);
//calculate imagesize to fit to the framewidth
CGFloat ratio = frameSize.width / displayedImage.size.width;
CGSize calculatedImageSize = CGSizeMake(displayedImage.size.width * ratio, displayedImage.size.height * ratio);
//the rect in which the image should be drawn
CGRect clippingRect = CGRectMake(imagePartWidth * i, 0, imagePartWidth, frameSize.height);
CGContextSaveGState(context);
CGContextClipToRect(context, clippingRect);
//take the center of the clippingRect and calculate the offset from the original center
CGFloat centerOffsetX = (imagePartWidth * i + imagePartWidth / 2) - frameSize.width / 2;
//offset to center vertical
CGFloat centerOffsetY = (frameSize.height - calculatedImageSize.height) / 2;
//shift the rect to draw the middle of the image in the clippingrect
CGRect drawingRect = CGRectMake(centerOffsetX, centerOffsetY, calculatedImageSize.width, calculatedImageSize.height);
[displayedImage drawInRect:drawingRect];
//get rid of the old clippingRect
CGContextRestoreGState(context);
}
displayedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.thumbnailView.image = displayedImage;
}
}
} else if ([self.mediaObject isKindOfClass:[MLAlbum class]]) {
MLAlbum *mediaObject = (MLAlbum *)self.mediaObject;
[self _configureForAlbum:mediaObject];
......@@ -154,7 +200,6 @@
MLFile *anyFileFromTrack = albumTrack.files.anyObject;
self.subtitleLabel.text = [NSString stringWithFormat:@"%@ — %@ — %@", albumTrack.artist, [NSString stringWithFormat:NSLocalizedString(@"LIBRARY_TRACK_N", @""), albumTrack.trackNumber.intValue], [VLCTime timeWithNumber:[anyFileFromTrack duration]]];
self.titleLabel.text = albumTrack.title;
[self _showPositionOfItem:anyFileFromTrack];
}
......@@ -190,6 +235,15 @@
self.progressIndicator.hidden = YES;
}
- (void)_configureForFolder:(MLLabel *)label
{
self.titleLabel.text = label.name;
NSUInteger count = label.files.count;
self.subtitleLabel.text = [NSString stringWithFormat:(count == 1) ? NSLocalizedString(@"LIBRARY_TRACKS", @"") : NSLocalizedString(@"LIBRARY_SINGLE_TRACK", @""), count];
self.mediaIsUnreadView.hidden = YES;
self.progressIndicator.hidden = YES;
}
- (void)_configureForMLFile:(MLFile *)mediaFile
{