Finish three tabs in book library

This commit is contained in:
automactic 2016-05-03 12:01:50 -04:00
parent 0bd0a5cafc
commit 1471350fcc
11 changed files with 342 additions and 205 deletions

View File

@ -36,6 +36,7 @@ class SpaceNotEnoughAlert: AlertOperation {
}
class LanguageFilterAlert: AlertOperation {
let context = UIApplication.appDelegate.managedObjectContext
init(libraryOnlineTBVC: LibraryOnlineTBVC) {
super.init(presentationContext: libraryOnlineTBVC)
@ -70,12 +71,14 @@ class LanguageFilterAlert: AlertOperation {
title = NSLocalizedString("Only Show Preferred Language?", comment: comment)
message = NSLocalizedString("We have found you may know \(languageString), would you like to filter the library by these languages?", comment: comment)
addAction(LocalizedStrings.ok, style: .Default) { (action) in
let languages = Language.fetchAll(UIApplication.appDelegate.managedObjectContext)
for language in languages {
guard let code = language.code else {continue}
language.isDisplayed = preferredLanguageCodes.contains(code)
}
libraryOnlineTBVC.refreshFetchedResultController()
self.context.performBlock({
let languages = Language.fetchAll(self.context)
for language in languages {
guard let code = language.code else {continue}
language.isDisplayed = preferredLanguageCodes.contains(code)
}
libraryOnlineTBVC.refreshFetchedResultController()
})
}
addAction(LocalizedStrings.cancel)
}
@ -87,4 +90,4 @@ class LanguageFilterAlert: AlertOperation {
func andJoinedString(a: String, b: String) -> String {
return a + " " + LocalizedStrings.and + " " + b
}
}
}

View File

@ -15,14 +15,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate {
var window: UIWindow?
let globalOperationQueue = OperationQueue()
var networkTaskCount = 0 {
didSet {
NSOperationQueue.mainQueue().addOperationWithBlock { () -> Void in
UIApplication.sharedApplication().networkActivityIndicatorVisible = self.networkTaskCount > 0 ? true : false
}
}
}
func recordActiveSession() {
Preference.activeUseHistory.append(NSDate())
}
@ -30,20 +22,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate {
func operationQueue(operationQueue: OperationQueue, operationDidFinish operation: NSOperation, withErrors errors: [NSError]) {
print(globalOperationQueue.operationCount)
}
func setupNotification() {
let bookDownloadFinishCategory = UIMutableUserNotificationCategory()
bookDownloadFinishCategory.identifier = "KIWIX_BOOK_DOWNLOAD_FINISH"
let settings = UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: Set(arrayLiteral: bookDownloadFinishCategory))
UIApplication.sharedApplication().registerUserNotificationSettings(settings)
}
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSURLProtocol.registerClass(KiwixURLProtocol)
setupNotification()
Network.sharedInstance.restoreProgresses()
// Register notification
let settings = UIUserNotificationSettings(forTypes: [.Sound, .Alert, .Badge], categories: nil) // Here are the notification permission the app wants
application.registerUserNotificationSettings(settings)
return true
}
func application(application: UIApplication, didRegisterUserNotificationSettings notificationSettings: UIUserNotificationSettings) {
// Here we get what notification permission user currently allows
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
@ -63,7 +56,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate {
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(AppDelegate.recordActiveSession), userInfo: nil, repeats: false)
ZIMMultiReader.sharedInstance.rescan()
}
@ -74,7 +66,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, OperationQueueDelegate {
}
func application(application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: () -> Void) {
//downloader.rejoinSessionWithIdentifier(identifier, completionHandler: completionHandler)
Network.sharedInstance.rejoinSessionWithIdentifier(identifier, completionHandler: completionHandler)
}
// MARK: - Core Data stack

View File

@ -16,9 +16,6 @@ extension UIApplication {
get {return appDelegate.globalOperationQueue}
}
// MARK: -
class func updateApplicationIconBadgeNumber() {
guard let settings = UIApplication.sharedApplication().currentUserNotificationSettings() else {return}
guard settings.types.contains(UIUserNotificationType.Badge) else {return}

View File

@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "sky.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -2,6 +2,8 @@
Downloaded books will show up here automatically.
## Import a book
Books added through iTunes will be automatically detected too. They instantly become searchable.
Books added through iTunes File Sharing will be detected automatically. They instantly become searchable.
You can also include / exclude a book in search by going to main interface -> searchbar -> search setting (the wrench icon).
You can also include / exclude a book in search by going to main interface -> search bar -> search setting (the wrench icon).
> Q: What does I

View File

@ -37,6 +37,67 @@ class LibraryDownloadTBVC: UITableViewController, NSFetchedResultsControllerDele
Network.sharedInstance.delegate = nil
}
// MARK: - BookTableCellDelegate
func didTapOnAccessoryViewForCell(cell: BookTableCell) {
guard let indexPath = tableView.indexPathForCell(cell),
let downloadTask = fetchedResultController.objectAtIndexPath(indexPath) as? DownloadTask,
let book = downloadTask.book else {return}
switch downloadTask.state {
case .Downloading:
Network.sharedInstance.pause(book)
case .Paused, .Error:
Network.sharedInstance.resume(book)
default:
break
}
}
// MARK: - DownloadProgressReporting
func refreshProgress(animated animated: Bool) {
guard let downloadTasks = fetchedResultController.fetchedObjects as? [DownloadTask] else {return}
for downloadTask in downloadTasks {
guard let id = downloadTask.book?.id,
let indexPath = fetchedResultController.indexPathForObject(downloadTask),
let cell = tableView.cellForRowAtIndexPath(indexPath) as? DownloadBookCell,
let progress = Network.sharedInstance.progresses[id] else {return}
cell.progressView.setProgress(Float(progress.fractionCompleted), animated: animated)
cell.subtitleLabel.text = progress.description
}
}
// MARK: - ToolBar Button
@IBOutlet weak var segmentedControl: UISegmentedControl!
@IBAction func segmentedControlValueChanged(sender: UISegmentedControl) {
tabBarController?.selectedIndex = sender.selectedSegmentIndex
}
@IBAction func dismissSelf(sender: UIBarButtonItem) {
dismissViewControllerAnimated(true, completion: nil)
}
var messageButton = MessageBarButtonItem()
func configureToolBar() {
guard var toolBarItems = self.toolbarItems else {return}
toolBarItems[1] = messageButton
setToolbarItems(toolBarItems, animated: false)
configureToolBarVisibility(animated: false)
configureMessage()
}
func configureToolBarVisibility(animated animated: Bool) {
navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated)
}
func configureMessage() {
guard let count = fetchedResultController.fetchedObjects?.count else {return}
let localizedString = String.localizedStringWithFormat(NSLocalizedString("%d download tasks", comment: "Book Library, book downloader message"), count)
messageButton.text = localizedString
}
// MARK: - Empty table datasource & delegate
func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! {
@ -73,85 +134,11 @@ class LibraryDownloadTBVC: UITableViewController, NSFetchedResultsControllerDele
func emptyDataSetDidTapButton(scrollView: UIScrollView!) {
guard let navController = UIStoryboard.setting.instantiateViewControllerWithIdentifier("WebViewNav") as? UINavigationController,
let controller = navController.topViewController as? WebViewVC else {return}
let controller = navController.topViewController as? WebViewVC else {return}
controller.page = .DownloaderLearnMore
presentViewController(navController, animated: true, completion: nil)
}
// MARK: - BookTableCellDelegate
func didTapOnAccessoryViewForCell(cell: BookTableCell) {
guard let indexPath = tableView.indexPathForCell(cell),
let downloadTask = fetchedResultController.objectAtIndexPath(indexPath) as? DownloadTask,
let book = downloadTask.book else {return}
switch downloadTask.state {
case .Downloading:
Network.sharedInstance.pause(book)
case .Paused, .Error:
Network.sharedInstance.resume(book)
default:
break
}
}
func refreshProgress(animated animated: Bool) {
guard let downloadTasks = fetchedResultController.fetchedObjects as? [DownloadTask] else {return}
for downloadTask in downloadTasks {
guard let id = downloadTask.book?.id,
let indexPath = fetchedResultController.indexPathForObject(downloadTask),
let cell = tableView.cellForRowAtIndexPath(indexPath) as? DownloadBookCell,
let progress = Network.sharedInstance.progresses[id] else {return}
cell.progressView.setProgress(Float(progress.fractionCompleted), animated: animated)
cell.subtitleLabel.text = progress.description
}
}
// MARK: - ToolBar Button
@IBOutlet weak var segmentedControl: UISegmentedControl!
@IBAction func segmentedControlValueChanged(sender: UISegmentedControl) {
tabBarController?.selectedIndex = sender.selectedSegmentIndex
}
@IBAction func dismissSelf(sender: UIBarButtonItem) {
dismissViewControllerAnimated(true, completion: nil)
}
var messageButton = MessageBarButtonItem()
func configureToolBar() {
guard var toolBarItems = self.toolbarItems else {return}
toolBarItems[1] = messageButton
configureToolBarVisibility(animated: false)
configureMessage()
setToolbarItems(toolBarItems, animated: false)
configureToolBarVisibility(animated: false)
}
func configureToolBarVisibility(animated animated: Bool) {
navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated)
}
func configureMessage() {
guard let count = fetchedResultController.fetchedObjects?.count else {return}
let localizedString = String.localizedStringWithFormat(NSLocalizedString("%d download tasks", comment: "Book Library, book downloader message"), count)
messageButton.text = localizedString
}
// MARK: - Fetched Results Controller
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
lazy var fetchedResultController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "DownloadTask")
let creationTimeDescriptor = NSSortDescriptor(key: "creationTime", ascending: true)
fetchRequest.sortDescriptors = [creationTimeDescriptor]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: "DownloadFRC")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)
return fetchedResultsController
}()
// MARK: - TableView Data Source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
@ -250,6 +237,20 @@ class LibraryDownloadTBVC: UITableViewController, NSFetchedResultsControllerDele
return [remove]
}
// MARK: - Fetched Results Controller
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
lazy var fetchedResultController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "DownloadTask")
let creationTimeDescriptor = NSSortDescriptor(key: "creationTime", ascending: true)
fetchRequest.sortDescriptors = [creationTimeDescriptor]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: "DownloadFRC")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)
return fetchedResultsController
}()
// MARK: - Fetched Result Controller Delegate
func controllerWillChangeContent(controller: NSFetchedResultsController) {

View File

@ -39,6 +39,38 @@ class LibraryLocalTBVC: UITableViewController, NSFetchedResultsControllerDelegat
controller.book = book
}
// MARK: - ToolBar Button Actions
@IBOutlet weak var segmentedControl: UISegmentedControl!
@IBAction func segmentedControlValueChanged(sender: UISegmentedControl) {
tabBarController?.selectedIndex = sender.selectedSegmentIndex
}
@IBAction func dismissSelf(sender: UIBarButtonItem) {
dismissViewControllerAnimated(true, completion: nil)
}
var messageButton = MessageBarButtonItem()
func configureToolBar() {
guard var toolBarItems = self.toolbarItems else {return}
toolBarItems[1] = messageButton
setToolbarItems(toolBarItems, animated: false)
configureToolBarVisibility(animated: false)
configureMessage()
}
func configureToolBarVisibility(animated animated: Bool) {
navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated)
}
func configureMessage() {
guard let books = fetchedResultController.fetchedObjects as? [Book] else {return}
let totalSize = books.reduce(0) {$0 + ($1.fileSize)}
let spaceString = Utilities.formattedFileSizeStringFromByteCount(totalSize)
let localizedString = String.localizedStringWithFormat(NSLocalizedString("Taking up %@ in total", comment: "Book Library, local book message"), spaceString)
messageButton.text = localizedString
}
// MARK: - Empty table datasource & delegate
func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! {
@ -69,6 +101,10 @@ class LibraryLocalTBVC: UITableViewController, NSFetchedResultsControllerDelegat
return NSAttributedString(string: text, attributes: attributes)
}
func verticalOffsetForEmptyDataSet(scrollView: UIScrollView!) -> CGFloat {
return -64.0
}
func spaceHeightForEmptyDataSet(scrollView: UIScrollView!) -> CGFloat {
return 30.0
}
@ -80,52 +116,6 @@ class LibraryLocalTBVC: UITableViewController, NSFetchedResultsControllerDelegat
presentViewController(navController, animated: true, completion: nil)
}
// MARK: - ToolBar Button Actions
@IBOutlet weak var segmentedControl: UISegmentedControl!
@IBAction func segmentedControlValueChanged(sender: UISegmentedControl) {
tabBarController?.selectedIndex = sender.selectedSegmentIndex
}
@IBAction func dismissSelf(sender: UIBarButtonItem) {
dismissViewControllerAnimated(true, completion: nil)
}
var messageButton = MessageBarButtonItem()
func configureToolBar() {
guard var toolBarItems = self.toolbarItems else {return}
toolBarItems[1] = messageButton
configureMessage()
setToolbarItems(toolBarItems, animated: false)
}
func configureToolBarVisibility(animated animated: Bool) {
print(fetchedResultController.fetchedObjects?.count)
navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated)
}
func configureMessage() {
guard let books = fetchedResultController.fetchedObjects as? [Book] else {return}
let totalSize = books.reduce(0) {$0 + ($1.fileSize)}
let spaceString = Utilities.formattedFileSizeStringFromByteCount(totalSize)
let localizedString = String.localizedStringWithFormat(NSLocalizedString("Taking up %@ in total", comment: "Book Library, local book message"), spaceString)
messageButton.text = localizedString
}
// MARK: - Fetched Results Controller
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
lazy var fetchedResultController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Book")
let langDescriptor = NSSortDescriptor(key: "language.name", ascending: true)
let titleDescriptor = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [langDescriptor, titleDescriptor]
fetchRequest.predicate = NSPredicate(format: "isLocal == true")
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "language.name", cacheName: "LocalFRC")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)
return fetchedResultsController
}()
// MARK: - TableView Data Source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
@ -195,19 +185,35 @@ class LibraryLocalTBVC: UITableViewController, NSFetchedResultsControllerDelegat
let delete = UITableViewRowAction(style: .Destructive, title: LocalizedStrings.delete) { (action, indexPath) -> Void in
guard let book = self.fetchedResultController.objectAtIndexPath(indexPath) as? Book else {return}
self.managedObjectContext.performBlock({ () -> Void in
if let id = book.id, let zimURL = ZIMMultiReader.sharedInstance.readers[id]?.fileURL {
FileManager.removeItem(atURL: zimURL)
}
if let _ = book.url {
book.isLocal = false
} else {
self.managedObjectContext.deleteObject(book)
}
guard let id = book.id, let zimURL = ZIMMultiReader.sharedInstance.readers[id]?.fileURL else {return}
FileManager.removeItem(atURL: zimURL)
})
}
return [delete]
}
// MARK: - Fetched Results Controller
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
lazy var fetchedResultController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Book")
let langDescriptor = NSSortDescriptor(key: "language.name", ascending: true)
let titleDescriptor = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [langDescriptor, titleDescriptor]
fetchRequest.predicate = NSPredicate(format: "isLocal == true")
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "language.name", cacheName: "LocalFRC")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)
return fetchedResultsController
}()
// MARK: - Fetched Result Controller Delegate
func controllerWillChangeContent(controller: NSFetchedResultsController) {

View File

@ -9,10 +9,11 @@
import UIKit
import CoreData
class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelegate, BookTableCellDelegate, LTBarButtonItemDelegate, RefreshLibraryOperationDelegate {
class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelegate, BookTableCellDelegate, LTBarButtonItemDelegate, RefreshLibraryOperationDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate {
var booksShowingDetail = Set<Book>()
weak var refreshOperation: RefreshLibraryOperation?
var messsageLabelConfigTimer: NSTimer?
// MARK: - Override
@ -21,6 +22,12 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
tableView.tableFooterView = UIView()
tableView.emptyDataSetSource = self
tableView.emptyDataSetDelegate = self
reconnectToExistingRefreshOperation()
refreshLibraryForTheFirstTime()
configureToolBar()
}
@ -28,16 +35,15 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
super.viewWillAppear(animated)
segmentedControl.selectedSegmentIndex = 0
configureRefreshStatus()
messsageLabelConfigTimer = NSTimer.scheduledTimerWithTimeInterval(60.0, target: self, selector: #selector(configureMessage), userInfo: nil, repeats: true)
}
func configureRefreshStatus() {
guard let operation = refreshOperation ?? UIApplication.globalOperationQueue.operation(String(RefreshLibraryOperation)) as? RefreshLibraryOperation else {return}
refreshOperation = operation
operation.delegate = self
operation.executing ? refreshLibButton.startRotating() : refreshLibButton.stopRotating()
configureMessage(isRefreshing: operation.executing)
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
messsageLabelConfigTimer?.invalidate()
}
// MARK: - BookTableCellDelegate
func didTapOnAccessoryViewForCell(cell: BookTableCell) {
@ -59,27 +65,47 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
func barButtonTapped(sender: LTBarButtonItem, gestureRecognizer: UIGestureRecognizer) {
guard sender === refreshLibButton else {return}
let refreshOperation = RefreshLibraryOperation(invokedAutomatically: false)
refreshOperation.delegate = self
UIApplication.globalOperationQueue.addOperation(refreshOperation)
startRefresh(invokedAutomatically: false)
}
// MARK: - RefreshLibraryOperationDelegate
func refreshDidStart() {
refreshLibButton.startRotating()
configureMessage(isRefreshing: true)
configureRefreshStatus()
configureToolBarVisibility(animated: true)
}
func refreshDidFinish() {
refreshLibButton.stopRotating()
configureMessage(isRefreshing: false)
configureRefreshStatus()
configureToolBarVisibility(animated: true)
guard !Preference.libraryHasShownPreferredLanguagePrompt else {return}
let langFilterOperation = LanguageFilterAlert(libraryOnlineTBVC: self)
UIApplication.appDelegate.globalOperationQueue.addOperation(langFilterOperation)
}
// MARK: - Others
func reconnectToExistingRefreshOperation() {
guard let operation = refreshOperation ??
UIApplication.globalOperationQueue.operation(String(RefreshLibraryOperation)) as? RefreshLibraryOperation
else {return}
refreshOperation = operation
operation.delegate = self
}
func refreshLibraryForTheFirstTime() {
guard Preference.libraryLastRefreshTime == nil else {return}
startRefresh(invokedAutomatically: true)
}
func startRefresh(invokedAutomatically invokedAutomatically: Bool) {
let refreshOperation = RefreshLibraryOperation(invokedAutomatically: invokedAutomatically)
refreshOperation.delegate = self
UIApplication.globalOperationQueue.addOperation(refreshOperation)
self.refreshOperation = refreshOperation
}
// MARK: - ToolBar Button
@IBOutlet weak var segmentedControl: UISegmentedControl!
@ -97,62 +123,95 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
guard var toolBarItems = self.toolbarItems else {return}
toolBarItems[0] = refreshLibButton
toolBarItems[2] = messageButton
configureMessage(isRefreshing: false)
let negativeSpace = UIBarButtonItem(barButtonSystemItem: .FixedSpace)
negativeSpace.width = -10
toolBarItems.insert(negativeSpace, atIndex: 0)
setToolbarItems(toolBarItems, animated: false)
configureToolBarVisibility(animated: false)
configureMessage(isRefreshing: false)
}
func configureMessage(isRefreshing isRefreshing: Bool) {
func configureToolBarVisibility(animated animated: Bool) {
navigationController?.setToolbarHidden(fetchedResultController.fetchedObjects?.count == 0, animated: animated)
}
func configureMessage(isRefreshing isRefreshing: Bool = false) {
if !isRefreshing {
guard let count = fetchedResultController.fetchedObjects?.count else {messageButton.text = nil; return}
let localizedBookCountString = String.localizedStringWithFormat(NSLocalizedString("%d book(s) available for download", comment: "Book Library, online book catalogue message"), count)
guard count > 0 else {messageButton.text = localizedBookCountString; return}
guard let lastRefreshTime = Preference.libraryLastRefreshTime else {messageButton.text = localizedBookCountString; return}
let localizedRefreshTimeString = NSLocalizedString("Last Refresh: ", comment: "Book Library, online book catalogue message") + lastRefreshTime.timeAgoSinceNow()
let localizedRefreshTimeString: String = {
var string = NSLocalizedString("Last Refresh: ", comment: "Book Library, online book catalogue refresh time")
if NSDate().timeIntervalSinceDate(lastRefreshTime) > 60.0 {
string += lastRefreshTime.timeAgoSinceNow()
} else {
string += NSLocalizedString("just now", comment: "Book Library, online book catalogue refresh time")
}
return string
}()
messageButton.text = localizedBookCountString + "\n" + localizedRefreshTimeString
} else {
messageButton.text = LocalizedStrings.refreshing
}
}
// MARK: - Fetched Results Controller
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
lazy var fetchedResultController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Book")
let langDescriptor = NSSortDescriptor(key: "language.name", ascending: true)
let titleDescriptor = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [langDescriptor, titleDescriptor]
fetchRequest.predicate = self.onlineCompoundPredicate
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "language.name", cacheName: "OnlineFRC")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)
return fetchedResultsController
}()
func refreshFetchedResultController() {
fetchedResultController.fetchRequest.predicate = onlineCompoundPredicate
fetchedResultController.performFetch(deleteCache: true)
tableView.reloadData()
configureMessage(isRefreshing: false)
func configureRefreshStatus() {
let executing = refreshOperation?.executing ?? false
executing ? refreshLibButton.startRotating() : refreshLibButton.stopRotating()
configureMessage(isRefreshing: executing)
tableView.reloadEmptyDataSet()
}
private var langPredicate: NSPredicate {
let displayedLanguages = Language.fetch(displayed: true, context: managedObjectContext)
if displayedLanguages.count > 0 {
return NSPredicate(format: "language IN %@", displayedLanguages)
// MARK: - Empty table datasource & delegate
func imageForEmptyDataSet(scrollView: UIScrollView!) -> UIImage! {
return UIImage(named: "CloudColor")
}
func titleForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let text = NSLocalizedString("There are some books in the cloud", comment: "Book Library, book online catalogue, no book center title")
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(18.0),
NSForegroundColorAttributeName: UIColor.darkGrayColor()]
return NSAttributedString(string: text, attributes: attributes)
}
func descriptionForEmptyDataSet(scrollView: UIScrollView!) -> NSAttributedString! {
let text = NSLocalizedString("Refresh the library to show all books available for download.", comment: "Book Library, book online catalogue, no book center description")
let style = NSMutableParagraphStyle()
style.lineBreakMode = .ByWordWrapping
style.alignment = .Center
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(14.0),
NSForegroundColorAttributeName: UIColor.lightGrayColor(),
NSParagraphStyleAttributeName: style]
return NSAttributedString(string: text, attributes: attributes)
}
func buttonTitleForEmptyDataSet(scrollView: UIScrollView!, forState state: UIControlState) -> NSAttributedString! {
if let _ = refreshOperation {
let text = NSLocalizedString("Refreshing...", comment: "Book Library, book downloader, refreshing button text")
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(17.0), NSForegroundColorAttributeName: UIColor.darkGrayColor()]
return NSAttributedString(string: text, attributes: attributes)
} else {
return NSPredicate(format: "language.name != nil")
let text = NSLocalizedString("Refresh Now", comment: "Book Library, book downloader, refresh now button text")
let attributes = [NSFontAttributeName: UIFont.boldSystemFontOfSize(17.0), NSForegroundColorAttributeName: segmentedControl.tintColor]
return NSAttributedString(string: text, attributes: attributes)
}
}
private var onlineCompoundPredicate: NSCompoundPredicate {
let isCloudPredicate = NSPredicate(format: "isLocal == false")
return NSCompoundPredicate(andPredicateWithSubpredicates: [langPredicate, isCloudPredicate])
func verticalOffsetForEmptyDataSet(scrollView: UIScrollView!) -> CGFloat {
return -64.0
}
func spaceHeightForEmptyDataSet(scrollView: UIScrollView!) -> CGFloat {
return 30.0
}
func emptyDataSetDidTapButton(scrollView: UIScrollView!) {
guard self.refreshOperation == nil else {return}
startRefresh(invokedAutomatically: false)
}
// MARK: - TableView Data Source
@ -234,6 +293,43 @@ class LibraryOnlineTBVC: UITableViewController, NSFetchedResultsControllerDelega
header.textLabel?.font = UIFont.boldSystemFontOfSize(14)
}
// MARK: - Fetched Results Controller
let managedObjectContext = UIApplication.appDelegate.managedObjectContext
lazy var fetchedResultController: NSFetchedResultsController = {
let fetchRequest = NSFetchRequest(entityName: "Book")
let langDescriptor = NSSortDescriptor(key: "language.name", ascending: true)
let titleDescriptor = NSSortDescriptor(key: "title", ascending: true)
fetchRequest.sortDescriptors = [langDescriptor, titleDescriptor]
fetchRequest.predicate = self.onlineCompoundPredicate
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: "language.name", cacheName: "OnlineFRC")
fetchedResultsController.delegate = self
fetchedResultsController.performFetch(deleteCache: false)
return fetchedResultsController
}()
func refreshFetchedResultController() {
fetchedResultController.fetchRequest.predicate = onlineCompoundPredicate
fetchedResultController.performFetch(deleteCache: true)
tableView.reloadData()
configureMessage(isRefreshing: false)
}
private var langPredicate: NSPredicate {
let displayedLanguages = Language.fetch(displayed: true, context: managedObjectContext)
if displayedLanguages.count > 0 {
return NSPredicate(format: "language IN %@", displayedLanguages)
} else {
return NSPredicate(format: "language.name != nil")
}
}
private var onlineCompoundPredicate: NSCompoundPredicate {
let isCloudPredicate = NSPredicate(format: "isLocal == false")
return NSCompoundPredicate(andPredicateWithSubpredicates: [langPredicate, isCloudPredicate])
}
// MARK: - Fetched Result Controller Delegate
func controllerWillChangeContent(controller: NSFetchedResultsController) {

View File

@ -20,8 +20,7 @@
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No book available for download.
Try refresh the library.</string>
<string>No book available for download.</string>
<key>one</key>
<string>%d book available for download</string>
<key>other</key>

View File

@ -12,12 +12,13 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU
static let sharedInstance = Network()
weak var delegate: DownloadProgressReporting?
let context = UIApplication.appDelegate.managedObjectContext
private let context = UIApplication.appDelegate.managedObjectContext
let operationQueue = OperationQueue()
var timer: NSTimer?
var progresses = [String: DownloadProgress]()
var shouldReportProgress = false
private var timer: NSTimer?
private var shouldReportProgress = false
private var completionHandler: (()-> Void)?
lazy var session: NSURLSession = {
let configuration = NSURLSessionConfiguration.backgroundSessionConfigurationWithIdentifier("org.kiwix.www")
@ -47,6 +48,11 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU
}
}
func rejoinSessionWithIdentifier(identifier: String, completionHandler: ()-> Void) {
guard identifier == session.configuration.identifier else {return}
self.completionHandler = completionHandler
}
func resetProgressReportingFlag() {shouldReportProgress = true}
// MARK: - Tasks
@ -115,6 +121,20 @@ class Network: NSObject, NSURLSessionDelegate, NSURLSessionDownloadDelegate, NSU
}
}
// MARK: - NSURLSessionDelegate
func URLSessionDidFinishEventsForBackgroundURLSession(session: NSURLSession) {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.completionHandler?()
let notification = UILocalNotification()
notification.alertTitle = NSLocalizedString("Book download finished", comment: "Notification: Book download finished")
notification.alertBody = NSLocalizedString("All download tasks are finished.", comment: "Notification: Book download finished")
notification.soundName = UILocalNotificationDefaultSoundName
UIApplication.sharedApplication().presentLocalNotificationNow(notification)
}
}
// MARK: - NSURLSessionTaskDelegate
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {