mirror of
https://github.com/kiwix/kiwix-apple.git
synced 2025-09-23 03:32:13 -04:00
Finish three tabs in book library
This commit is contained in:
parent
0bd0a5cafc
commit
1471350fcc
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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}
|
||||
|
21
Kiwix/Assets.xcassets/CloudColor.imageset/Contents.json
vendored
Normal file
21
Kiwix/Assets.xcassets/CloudColor.imageset/Contents.json
vendored
Normal 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"
|
||||
}
|
||||
}
|
BIN
Kiwix/Assets.xcassets/CloudColor.imageset/sky.png
vendored
Normal file
BIN
Kiwix/Assets.xcassets/CloudColor.imageset/sky.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
@ -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
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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>
|
||||
|
@ -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?) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user