From 588b8143890a3cb37931c8464161987ef0099864 Mon Sep 17 00:00:00 2001 From: Maxime Date: Thu, 13 Jul 2017 00:07:55 +0200 Subject: [PATCH 01/20] SRP Between Setting And Theme fix #106 --- wallabag.xcodeproj/project.pbxproj | 24 +++ wallabag/AppDelegate.swift | 2 +- wallabag/Cell/ArticleTableViewCell.swift | 10 +- wallabag/Cell/ThemedTableViewCell.swift | 6 +- wallabag/Controller/AboutViewController.swift | 2 +- .../Controller/ArticleViewController.swift | 6 +- .../ArticlesTableViewController.swift | 12 +- .../SettingsTableViewController.swift | 4 +- .../ThemeChoiceTableViewController.swift | 16 +- wallabag/Controller/TipViewController.swift | 4 +- wallabag/Lib/Setting.swift | 11 +- wallabag/Lib/Theme/Dusk.swift | 19 ++ wallabag/Lib/Theme/Light.swift | 19 ++ wallabag/Lib/Theme/Night.swift | 19 ++ wallabag/Lib/Theme/White.swift | 19 ++ wallabag/Lib/ThemeManager.swift | 166 +++++++----------- 16 files changed, 198 insertions(+), 141 deletions(-) create mode 100644 wallabag/Lib/Theme/Dusk.swift create mode 100644 wallabag/Lib/Theme/Light.swift create mode 100644 wallabag/Lib/Theme/Night.swift create mode 100644 wallabag/Lib/Theme/White.swift diff --git a/wallabag.xcodeproj/project.pbxproj b/wallabag.xcodeproj/project.pbxproj index b4cd2fe7..a4939ba1 100644 --- a/wallabag.xcodeproj/project.pbxproj +++ b/wallabag.xcodeproj/project.pbxproj @@ -42,6 +42,10 @@ 9C987A511DB79826005515F4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9C987A501DB79826005515F4 /* Assets.xcassets */; }; 9C987A541DB79826005515F4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9C987A521DB79826005515F4 /* LaunchScreen.storyboard */; }; 9CA232301DFECE74006B29B8 /* light.css in Resources */ = {isa = PBXBuildFile; fileRef = 9CA2322F1DFECE74006B29B8 /* light.css */; }; + 9CA9659B1F16B68B009C6377 /* White.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA9659A1F16B68B009C6377 /* White.swift */; }; + 9CA9659D1F16B6B9009C6377 /* Light.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA9659C1F16B6B9009C6377 /* Light.swift */; }; + 9CA9659F1F16B71E009C6377 /* Dusk.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA9659E1F16B71E009C6377 /* Dusk.swift */; }; + 9CA965A11F16B729009C6377 /* Night.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA965A01F16B729009C6377 /* Night.swift */; }; 9CBA6C601E16FC2B000B9AE6 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CBA6C5F1E16FC2B000B9AE6 /* ShareViewController.swift */; }; 9CBA6C671E16FC2B000B9AE6 /* bagit.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 9CBA6C5D1E16FC2B000B9AE6 /* bagit.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 9CC0D71A1E22AB15002E4E94 /* wallabagScreenshotUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CC0D7191E22AB15002E4E94 /* wallabagScreenshotUITests.swift */; }; @@ -167,6 +171,10 @@ 9C987A651DB79826005515F4 /* wallabagUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = wallabagUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9C987A6B1DB79826005515F4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 9CA2322F1DFECE74006B29B8 /* light.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = light.css; sourceTree = ""; }; + 9CA9659A1F16B68B009C6377 /* White.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = White.swift; sourceTree = ""; }; + 9CA9659C1F16B6B9009C6377 /* Light.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Light.swift; sourceTree = ""; }; + 9CA9659E1F16B71E009C6377 /* Dusk.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dusk.swift; sourceTree = ""; }; + 9CA965A01F16B729009C6377 /* Night.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Night.swift; sourceTree = ""; }; 9CBA6C5D1E16FC2B000B9AE6 /* bagit.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = bagit.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 9CBA6C5F1E16FC2B000B9AE6 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = ""; }; 9CBA6C641E16FC2B000B9AE6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -285,6 +293,7 @@ 9C181B831DB8D47600D941EB /* Lib */ = { isa = PBXGroup; children = ( + 9CA965991F16B669009C6377 /* Theme */, 9C7B79E51EBF8070004559B1 /* ArticleSync.swift */, 9C914A491EBD1D6000973BA6 /* CoreData.swift */, 9CE369751DE63D030005E20E /* Setting.swift */, @@ -430,6 +439,17 @@ path = wallabagUITests; sourceTree = ""; }; + 9CA965991F16B669009C6377 /* Theme */ = { + isa = PBXGroup; + children = ( + 9CA9659C1F16B6B9009C6377 /* Light.swift */, + 9CA9659E1F16B71E009C6377 /* Dusk.swift */, + 9CA965A01F16B729009C6377 /* Night.swift */, + 9CA9659A1F16B68B009C6377 /* White.swift */, + ); + path = Theme; + sourceTree = ""; + }; 9CBA6C5E1E16FC2B000B9AE6 /* bagit */ = { isa = PBXGroup; children = ( @@ -896,8 +916,11 @@ 9C21927D1DBFF44700F628C2 /* ArticleTableViewCell.swift in Sources */, 9CF1F2251DBE9D150086B98B /* LoginViewController.swift in Sources */, 9CE369741DE632030005E20E /* SettingsTableViewController.swift in Sources */, + 9CA9659B1F16B68B009C6377 /* White.swift in Sources */, + 9CA9659F1F16B71E009C6377 /* Dusk.swift in Sources */, 9C914A4A1EBD1D6000973BA6 /* CoreData.swift in Sources */, 9C4BFBE01EC08EF200B75EFB /* Entry+CoreDataClass.swift in Sources */, + 9CA965A11F16B729009C6377 /* Night.swift in Sources */, 9C4BFBE11EC08EF200B75EFB /* Entry+CoreDataProperties.swift in Sources */, 9C181B881DB8E27000D941EB /* ArticlesTableViewController.swift in Sources */, 9C75961E1DBD59330096614B /* ArticleViewController.swift in Sources */, @@ -912,6 +935,7 @@ 9C8311861E06F56C004ACD5D /* ThemedTableViewCell.swift in Sources */, 9C75961C1DBB567E0096614B /* String.swift in Sources */, 9CC4B0611DC5361000BC6830 /* Dictionary.swift in Sources */, + 9CA9659D1F16B6B9009C6377 /* Light.swift in Sources */, 9C65DEB11DC5EE85002CA068 /* Int.swift in Sources */, 9C298B131E0BF8F400B72503 /* ThemeChoiceTableViewController.swift in Sources */, 9C987A471DB79826005515F4 /* AppDelegate.swift in Sources */, diff --git a/wallabag/AppDelegate.swift b/wallabag/AppDelegate.swift index 3657efff..75af9396 100644 --- a/wallabag/AppDelegate.swift +++ b/wallabag/AppDelegate.swift @@ -53,7 +53,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { NetworkActivityIndicatorManager.shared.isEnabled = true NetworkActivityIndicatorManager.shared.startDelay = 0.1 - ThemeManager.apply(theme: Setting.getTheme()) + ThemeManager.manager.apply(Setting.getTheme()) return true } diff --git a/wallabag/Cell/ArticleTableViewCell.swift b/wallabag/Cell/ArticleTableViewCell.swift index 8ba216de..9f1e2c32 100644 --- a/wallabag/Cell/ArticleTableViewCell.swift +++ b/wallabag/Cell/ArticleTableViewCell.swift @@ -48,10 +48,10 @@ class ArticleTableViewCell: ThemedTableViewCell { override func setupTheme() { super.setupTheme() - title?.textColor = Setting.getTheme().color - website?.textColor = Setting.getTheme().color - readingTime?.textColor = Setting.getTheme().color - readed?.tintColor = Setting.getTheme().tintColor - starred?.tintColor = Setting.getTheme().tintColor + title?.textColor = ThemeManager.manager.getColor() + website?.textColor = ThemeManager.manager.getColor() + readingTime?.textColor = ThemeManager.manager.getColor() + readed?.tintColor = ThemeManager.manager.getTintColor() + starred?.tintColor = ThemeManager.manager.getTintColor() } } diff --git a/wallabag/Cell/ThemedTableViewCell.swift b/wallabag/Cell/ThemedTableViewCell.swift index 94d55228..24d36a1b 100644 --- a/wallabag/Cell/ThemedTableViewCell.swift +++ b/wallabag/Cell/ThemedTableViewCell.swift @@ -23,10 +23,10 @@ class ThemedTableViewCell: UITableViewCell { Apply the current theme to the cell */ func setupTheme() { - backgroundColor = Setting.getTheme().backgroundColor - textLabel?.textColor = Setting.getTheme().color + backgroundColor = ThemeManager.manager.getBackgroundColor() + textLabel?.textColor = ThemeManager.manager.getColor() selectedBackgroundView = UIView() - selectedBackgroundView?.backgroundColor = Setting.getTheme().backgroundSelectedColor + selectedBackgroundView?.backgroundColor = ThemeManager.manager.getBackgroundSelectedColor() } } diff --git a/wallabag/Controller/AboutViewController.swift b/wallabag/Controller/AboutViewController.swift index f4c5c9c8..becd7eff 100644 --- a/wallabag/Controller/AboutViewController.swift +++ b/wallabag/Controller/AboutViewController.swift @@ -14,7 +14,7 @@ class AboutViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Setting.getTheme().backgroundColor + view.backgroundColor = ThemeManager.manager.getBackgroundColor() let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String ?? "0" let build = Bundle.main.infoDictionary!["CFBundleVersion"] as? String ?? "0" diff --git a/wallabag/Controller/ArticleViewController.swift b/wallabag/Controller/ArticleViewController.swift index f5de9fab..6b3a0c9c 100644 --- a/wallabag/Controller/ArticleViewController.swift +++ b/wallabag/Controller/ArticleViewController.swift @@ -73,7 +73,7 @@ final class ArticleViewController: UIViewController { loadArticleContent() contentWeb.delegate = self contentWeb.scrollView.delegate = self - contentWeb.backgroundColor = Setting.getTheme().backgroundColor + contentWeb.backgroundColor = ThemeManager.manager.getBackgroundColor() } override func viewWillDisappear(_ animated: Bool) { @@ -90,11 +90,9 @@ final class ArticleViewController: UIViewController { func contentForWebView(_ entry: Entry) -> String { do { let html = try String(contentsOfFile: Bundle.main.path(forResource: "article", ofType: "html")!) - let justify = Setting.isJustifyArticle() ? "justify.css" : "" - let theme = Setting.getTheme() - return String(format: html, arguments: [justify, theme.rawValue, entry.title!, entry.content!]) + return String(format: html, arguments: [justify, Setting.getTheme(), entry.title!, entry.content!]) } catch { fatalError("Unable to load article view") } diff --git a/wallabag/Controller/ArticlesTableViewController.swift b/wallabag/Controller/ArticlesTableViewController.swift index 181c26b0..f391ee84 100644 --- a/wallabag/Controller/ArticlesTableViewController.swift +++ b/wallabag/Controller/ArticlesTableViewController.swift @@ -79,16 +79,16 @@ final class ArticlesTableViewController: UITableViewController { titleLabel.isUserInteractionEnabled = true titleLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.scrollTop))) titleLabel.text = mode.humainReadable().localized - titleLabel.textColor = Setting.getTheme().color + titleLabel.textColor = ThemeManager.manager.getColor() navigationItem.titleView = titleLabel - navigationController?.navigationBar.setBackgroundImage(Setting.getTheme().navigationBarBackground, for: .default) - menu.tintColor = Setting.getTheme().tintColor - add.tintColor = Setting.getTheme().tintColor + navigationController?.navigationBar.setBackgroundImage(ThemeManager.manager.getNavigationBarBackground(), for: .default) + menu.tintColor = ThemeManager.manager.getTintColor() + add.tintColor = ThemeManager.manager.getTintColor() - tableView.backgroundColor = Setting.getTheme().backgroundColor + tableView.backgroundColor = ThemeManager.manager.getBackgroundColor() for row in 0 ... tableView.numberOfRows(inSection: 0) { - tableView.cellForRow(at: IndexPath(row: row, section: 0))?.backgroundColor = Setting.getTheme().backgroundColor + tableView.cellForRow(at: IndexPath(row: row, section: 0))?.backgroundColor = ThemeManager.manager.getBackgroundColor() } } diff --git a/wallabag/Controller/SettingsTableViewController.swift b/wallabag/Controller/SettingsTableViewController.swift index e5283dbe..77721690 100644 --- a/wallabag/Controller/SettingsTableViewController.swift +++ b/wallabag/Controller/SettingsTableViewController.swift @@ -30,11 +30,11 @@ final class SettingsTableViewController: UITableViewController { justifySwitch.setOn(Setting.isJustifyArticle(), animated: false) badgeSwitch.setOn(Setting.isBadgeEnable(), animated: false) - currentThemeLabel.text = Setting.getTheme().rawValue.ucFirst + currentThemeLabel.text = Setting.getTheme().ucFirst } override func didMove(toParentViewController parent: UIViewController?) { - currentThemeLabel.text = Setting.getTheme().rawValue.ucFirst + currentThemeLabel.text = Setting.getTheme().ucFirst } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { diff --git a/wallabag/Controller/ThemeChoiceTableViewController.swift b/wallabag/Controller/ThemeChoiceTableViewController.swift index 682c99ac..68466ee6 100644 --- a/wallabag/Controller/ThemeChoiceTableViewController.swift +++ b/wallabag/Controller/ThemeChoiceTableViewController.swift @@ -9,23 +9,24 @@ import UIKit final class ThemeChoiceTableViewController: UITableViewController { + + var themes: [ThemeProtocol] = ThemeManager.manager.getThemes() // MARK: - Table view data source override func numberOfSections(in tableView: UITableView) -> Int { return 1 } override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return ThemeManager.Theme.allThemes.count + return themes.count } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + let theme: ThemeProtocol = themes[indexPath.row] - let theme: ThemeManager.Theme = ThemeManager.Theme.allThemes[indexPath.row] - - cell.textLabel?.text = theme.rawValue.ucFirst + cell.textLabel?.text = theme.name.ucFirst - if theme == Setting.getTheme() { + if theme.name == Setting.getTheme() { cell.accessoryType = .checkmark } @@ -39,9 +40,10 @@ final class ThemeChoiceTableViewController: UITableViewController { tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark tableView.deselectRow(at: indexPath, animated: true) - let selectedTheme = ThemeManager.Theme.allThemes[indexPath.row] + let selectedTheme = themes[indexPath.row] - Setting.setTheme(value: selectedTheme) + Setting.setTheme(value: selectedTheme.name) + ThemeManager.manager.apply(selectedTheme.name) _ = navigationController?.popViewController(animated: true) } diff --git a/wallabag/Controller/TipViewController.swift b/wallabag/Controller/TipViewController.swift index 9803a284..1072ce72 100644 --- a/wallabag/Controller/TipViewController.swift +++ b/wallabag/Controller/TipViewController.swift @@ -70,9 +70,9 @@ class TipViewController: UIViewController, SKProductsRequestDelegate, SKPaymentT override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = Setting.getTheme().backgroundColor + view.backgroundColor = ThemeManager.manager.getBackgroundColor() tipContent.text = "This application is developed on free time, it is free and will remain so. But you can contribute financially by making a donation whenever you want to support the project.".localized - tipContent.textColor = Setting.getTheme().color + tipContent.textColor = ThemeManager.manager.getColor() SKPaymentQueue.default().add(self) requestProductInfo() diff --git a/wallabag/Lib/Setting.swift b/wallabag/Lib/Setting.swift index fd47b568..d83b0202 100644 --- a/wallabag/Lib/Setting.swift +++ b/wallabag/Lib/Setting.swift @@ -73,17 +73,16 @@ class Setting { standard.set(value, forKey: Const.badge.rawValue) } - static func getTheme() -> ThemeManager.Theme { + static func getTheme() -> String { guard let value = standard.string(forKey: Const.articleTheme.rawValue) else { - return .white + return "white" } - return ThemeManager.Theme(rawValue: value) ?? .white + return value } - static func setTheme(value: ThemeManager.Theme) { - standard.set(value.rawValue, forKey: Const.articleTheme.rawValue) - ThemeManager.apply(theme: value) + static func setTheme(value: String) { + standard.set(value, forKey: Const.articleTheme.rawValue) } static func deleteServer() { diff --git a/wallabag/Lib/Theme/Dusk.swift b/wallabag/Lib/Theme/Dusk.swift new file mode 100644 index 00000000..29f2eac0 --- /dev/null +++ b/wallabag/Lib/Theme/Dusk.swift @@ -0,0 +1,19 @@ +// +// Dusk.swift +// wallabag +// +// Created by maxime marinel on 12/07/2017. +// Copyright © 2017 maxime marinel. All rights reserved. +// + +import UIKit + +class Dusk: ThemeProtocol { + var name: String = "dusk" + var color: UIColor = UIColor.init(red: 160.rgb, green: 160.rgb, blue: 160.rgb, alpha: 1) + var tintColor: UIColor = UIColor.init(red: 160.rgb, green: 160.rgb, blue: 160.rgb, alpha: 1) + var barStyle: UIBarStyle = .default + var backgroundColor: UIColor = UIColor.init(red: 60.rgb, green: 60.rgb, blue: 60.rgb, alpha: 1) + var navigationBarBackground: UIImage? = #imageLiteral(resourceName: "navBackgroundDusk") + var backgroundSelectedColor: UIColor = UIColor.init(red: 79.rgb, green: 79.rgb, blue: 79.rgb, alpha: 1) +} diff --git a/wallabag/Lib/Theme/Light.swift b/wallabag/Lib/Theme/Light.swift new file mode 100644 index 00000000..1216b6ec --- /dev/null +++ b/wallabag/Lib/Theme/Light.swift @@ -0,0 +1,19 @@ +// +// Light.swift +// wallabag +// +// Created by maxime marinel on 12/07/2017. +// Copyright © 2017 maxime marinel. All rights reserved. +// + +import UIKit + +class Light: ThemeProtocol { + var name: String = "light" + var color: UIColor = UIColor.init(red: 64.rgb, green: 64.rgb, blue: 64.rgb, alpha: 1) + var tintColor: UIColor = UIColor.init(red: 64.rgb, green: 64.rgb, blue: 64.rgb, alpha: 1) + var barStyle: UIBarStyle = .default + var backgroundColor: UIColor = UIColor.init(red: 246.rgb, green: 239.rgb, blue: 220.rgb, alpha: 1) + var navigationBarBackground: UIImage? = #imageLiteral(resourceName: "navBackgroundSoft") + var backgroundSelectedColor: UIColor = UIColor.init(red: 221.rgb, green: 215.rgb, blue: 198.rgb, alpha: 1) +} diff --git a/wallabag/Lib/Theme/Night.swift b/wallabag/Lib/Theme/Night.swift new file mode 100644 index 00000000..5903f1b2 --- /dev/null +++ b/wallabag/Lib/Theme/Night.swift @@ -0,0 +1,19 @@ +// +// Dark.swift +// wallabag +// +// Created by maxime marinel on 12/07/2017. +// Copyright © 2017 maxime marinel. All rights reserved. +// + +import UIKit + +class Night: ThemeProtocol { + var name: String = "night" + var color: UIColor = UIColor.init(red: 153.rgb, green: 153.rgb, blue: 153.rgb, alpha: 1) + var tintColor: UIColor = UIColor.init(red: 153.rgb, green: 153.rgb, blue: 153.rgb, alpha: 1) + var barStyle: UIBarStyle = .black + var backgroundColor: UIColor = UIColor.init(red: 34.rgb, green: 34.rgb, blue: 34.rgb, alpha: 1) + var navigationBarBackground: UIImage? = #imageLiteral(resourceName: "navBackgroundNight") + var backgroundSelectedColor: UIColor = UIColor.init(red: 56.rgb, green: 56.rgb, blue: 56.rgb, alpha: 1) +} diff --git a/wallabag/Lib/Theme/White.swift b/wallabag/Lib/Theme/White.swift new file mode 100644 index 00000000..7ee18532 --- /dev/null +++ b/wallabag/Lib/Theme/White.swift @@ -0,0 +1,19 @@ +// +// White.swift +// wallabag +// +// Created by maxime marinel on 12/07/2017. +// Copyright © 2017 maxime marinel. All rights reserved. +// + +import UIKit + +class White: ThemeProtocol { + var name: String = "white" + var color: UIColor = .black + var tintColor: UIColor = .black + var barStyle: UIBarStyle = .default + var backgroundColor: UIColor = .white + var navigationBarBackground: UIImage? = nil + var backgroundSelectedColor: UIColor = UIColor.init(red: 229.rgb, green: 229.rgb, blue: 229.rgb, alpha: 1) +} diff --git a/wallabag/Lib/ThemeManager.swift b/wallabag/Lib/ThemeManager.swift index 596153ad..2c815faf 100644 --- a/wallabag/Lib/ThemeManager.swift +++ b/wallabag/Lib/ThemeManager.swift @@ -8,131 +8,59 @@ import UIKit -struct ThemeManager { +protocol ThemeProtocol { + var name: String {get} + var color: UIColor {get} + var tintColor: UIColor {get} + var barStyle: UIBarStyle {get} + var backgroundColor: UIColor {get} + var navigationBarBackground: UIImage? {get} + var backgroundSelectedColor: UIColor {get} +} + +class ThemeManager { + static let manager = ThemeManager() + private init() { + currentTheme = White() + } + + private var themes: [ThemeProtocol] = [White(), Light(), Dusk(), Night()] + private var currentTheme: ThemeProtocol // Add theme here and dont forget add in allThemes - enum Theme: String { + /*enum Theme: String { case white case light case dusk case night static let allThemes: [Theme] = [white, light, dusk, night] + }*/ - // color for the menu and listing articles - var color: UIColor { - switch self { - case .light: - // #404040 - return UIColor.init(red: 64.rgb, green: 64.rgb, blue: 64.rgb, alpha: 1) - case .dusk: - // #a0a0a0 - return UIColor.init(red: 160.rgb, green: 160.rgb, blue: 160.rgb, alpha: 1) - case .night: - // #999999 - return UIColor.init(red: 153.rgb, green: 153.rgb, blue: 153.rgb, alpha: 1) - default: - return UIColor.black - } - } - - // color for navigation button (back, delete, star, share) - var tintColor: UIColor { - switch self { - case .light: - return UIColor.init(red: 64.rgb, green: 64.rgb, blue: 64.rgb, alpha: 1) - case .dusk: - return UIColor.init(red: 160.rgb, green: 160.rgb, blue: 160.rgb, alpha: 1) - case .night: - return UIColor.init(red: 153.rgb, green: 153.rgb, blue: 153.rgb, alpha: 1) - default: - return UIColor.black - } - } - - var barStyle: UIBarStyle { - switch self { - case .night: - return .black - default: - return .default - } - } - - // background of the article (outside the webview) & the listing - var backgroundColor: UIColor { - switch self { - case .light: - // #f6efdc - return UIColor.init(red: 246.rgb, green: 239.rgb, blue: 220.rgb, alpha: 1) - case .dusk: - // #3c3c3c - return UIColor.init(red: 60.rgb, green: 60.rgb, blue: 60.rgb, alpha: 1) - case .night: - // #222222 - return UIColor.init(red: 34.rgb, green: 34.rgb, blue: 34.rgb, alpha: 1) - default: - return UIColor.white - } - } - - var navigationBarBackground: UIImage? { - switch self { - case .light: - let image = #imageLiteral(resourceName: "navBackgroundSoft") - return image - case .dusk: - let image = #imageLiteral(resourceName: "navBackgroundDusk") - return image - case .night: - let image = #imageLiteral(resourceName: "navBackgroundNight") - return image - default: - return nil - } - } - - // background color when an element is selected in the list - var backgroundSelectedColor: UIColor { - switch self { - case .light: - // #ddd7c6 - return UIColor.init(red: 221.rgb, green: 215.rgb, blue: 198.rgb, alpha: 1) - case .dusk: - // #4f4f4f - return UIColor.init(red: 79.rgb, green: 79.rgb, blue: 79.rgb, alpha: 1) - case .night: - // #383838 - return UIColor.init(red: 56.rgb, green: 56.rgb, blue: 56.rgb, alpha: 1) - default: - // e5e5e5 - return UIColor.init(red: 229.rgb, green: 229.rgb, blue: 229.rgb, alpha: 1) - } - } - } + func apply(_ themeName: String) { + updateCurrentTheme(themeName) - static func apply(theme: Theme) { let sharedApplication = UIApplication.shared - sharedApplication.delegate?.window??.tintColor = theme.tintColor + sharedApplication.delegate?.window??.tintColor = currentTheme.tintColor let uiButton = UIButton.appearance() - uiButton.tintColor = theme.tintColor + uiButton.tintColor = currentTheme.tintColor let uiBarButton = UIBarButtonItem.appearance() - uiBarButton.tintColor = theme.tintColor + uiBarButton.tintColor = currentTheme.tintColor let uiLabel = UILabel.appearance() - uiLabel.textColor = theme.color + uiLabel.textColor = currentTheme.color let bar = UINavigationBar.appearance() - bar.setBackgroundImage(theme.navigationBarBackground, for: .default) - bar.titleTextAttributes = [NSForegroundColorAttributeName: theme.color] - bar.barStyle = theme.barStyle + bar.setBackgroundImage(currentTheme.navigationBarBackground, for: .default) + bar.titleTextAttributes = [NSForegroundColorAttributeName: currentTheme.color] + bar.barStyle = currentTheme.barStyle let toolbar = UIToolbar.appearance() - toolbar.setBackgroundImage(theme.navigationBarBackground, forToolbarPosition: .any, barMetrics: .default) + toolbar.setBackgroundImage(currentTheme.navigationBarBackground, forToolbarPosition: .any, barMetrics: .default) - UITextView.appearance().backgroundColor = Setting.getTheme().backgroundColor + UITextView.appearance().backgroundColor = currentTheme.backgroundColor UIBarButtonItem.appearance().setTitleTextAttributes([ NSFontAttributeName: UIFont(name: "UbuntuTitling-Bold", size: 15.0)! @@ -140,10 +68,40 @@ struct ThemeManager { NotificationCenter.default.post(name: Notification.Name.themeUpdated, object: nil) } + + private func updateCurrentTheme(_ themeName: String) { + for theme in themes where theme.name == themeName { + currentTheme = theme + } + } + + func getColor() -> UIColor { + return currentTheme.color + } + + func getTintColor() -> UIColor { + return currentTheme.tintColor + } + + func getBackgroundColor() -> UIColor { + return currentTheme.backgroundColor + } + + func getBackgroundSelectedColor() -> UIColor { + return currentTheme.backgroundSelectedColor + } + + func getNavigationBarBackground() -> UIImage? { + return currentTheme.navigationBarBackground + } + + func getThemes() -> [ThemeProtocol] { + return themes + } } extension Notification.Name { static var themeUpdated: Notification.Name { - return Notification.Name(rawValue: "theme.updted") + return Notification.Name(rawValue: "theme.updated") } } From 2ca3d06d7f5917ef1c23e793e4cf522e2ae54a1f Mon Sep 17 00:00:00 2001 From: Maxime Date: Thu, 13 Jul 2017 12:43:35 +0200 Subject: [PATCH 02/20] Clean ThemeManager --- wallabag/Lib/ThemeManager.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/wallabag/Lib/ThemeManager.swift b/wallabag/Lib/ThemeManager.swift index 2c815faf..281f14e4 100644 --- a/wallabag/Lib/ThemeManager.swift +++ b/wallabag/Lib/ThemeManager.swift @@ -27,16 +27,6 @@ class ThemeManager { private var themes: [ThemeProtocol] = [White(), Light(), Dusk(), Night()] private var currentTheme: ThemeProtocol - // Add theme here and dont forget add in allThemes - /*enum Theme: String { - case white - case light - case dusk - case night - - static let allThemes: [Theme] = [white, light, dusk, night] - }*/ - func apply(_ themeName: String) { updateCurrentTheme(themeName) From 20d5b4bb2a0a4304adb671b7bd817651ba0b035b Mon Sep 17 00:00:00 2001 From: Maxime Date: Sun, 30 Jul 2017 22:08:38 +0200 Subject: [PATCH 03/20] Speech article ( fix #113) * Speech synthetizer * Voice setting * Change icon * Use identifier instead language * Clean code --- bagit/Info.plist | 4 +- wallabag.xcodeproj/project.pbxproj | 8 +- .../AppIcon.appiconset/Contents.json | 5 + .../lips.imageset/Contents.json | 15 ++ .../Assets.xcassets/lips.imageset/lips.pdf | Bin 0 -> 4126 bytes .../lipsfilled.imageset/Contents.json | 15 ++ .../lipsfilled.imageset/lipsfill.pdf | Bin 0 -> 4413 bytes .../Controller/ArticleViewController.swift | 18 +++ .../SettingsTableViewController.swift | 10 ++ .../VoiceChoiceTableViewController.swift | 43 ++++++ wallabag/Info.plist | 4 +- wallabag/Lib/Setting.swift | 22 +++ .../Storyboard/Base.lproj/Main.storyboard | 131 +++++++++++++++--- wallabag/Storyboard/de.lproj/Main.strings | 3 + wallabag/Storyboard/fr.lproj/Main.strings | 2 + wallabagScreenshotUITests/Info.plist | 2 +- wallabagTests/Info.plist | 2 +- wallabagUITests/Info.plist | 2 +- 18 files changed, 259 insertions(+), 27 deletions(-) create mode 100644 wallabag/Assets.xcassets/lips.imageset/Contents.json create mode 100644 wallabag/Assets.xcassets/lips.imageset/lips.pdf create mode 100644 wallabag/Assets.xcassets/lipsfilled.imageset/Contents.json create mode 100644 wallabag/Assets.xcassets/lipsfilled.imageset/lipsfill.pdf create mode 100644 wallabag/Controller/VoiceChoiceTableViewController.swift diff --git a/bagit/Info.plist b/bagit/Info.plist index 49e09f76..7cc3574e 100644 --- a/bagit/Info.plist +++ b/bagit/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 2.1.2 + 2.2 CFBundleVersion - 61 + 63 ITSEncryptionExportComplianceCode NSAppTransportSecurity diff --git a/wallabag.xcodeproj/project.pbxproj b/wallabag.xcodeproj/project.pbxproj index a4939ba1..db57f5be 100644 --- a/wallabag.xcodeproj/project.pbxproj +++ b/wallabag.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 9C7B79E41EBF7E37004559B1 /* wallabag2.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 9C7B79E21EBF7E37004559B1 /* wallabag2.xcdatamodeld */; }; 9C7B79E61EBF8070004559B1 /* ArticleSync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C7B79E51EBF8070004559B1 /* ArticleSync.swift */; }; 9C8311861E06F56C004ACD5D /* ThemedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8311851E06F56C004ACD5D /* ThemedTableViewCell.swift */; }; + 9C8B20A21F2612BA0084BBAA /* VoiceChoiceTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C8B20A11F2612BA0084BBAA /* VoiceChoiceTableViewController.swift */; }; 9C9034881E5A26D90074540B /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CCAC4001E0176CF002C6A66 /* ThemeManager.swift */; }; 9C914A4A1EBD1D6000973BA6 /* CoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C914A491EBD1D6000973BA6 /* CoreData.swift */; }; 9C987A471DB79826005515F4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C987A461DB79826005515F4 /* AppDelegate.swift */; }; @@ -158,6 +159,7 @@ 9C7B79E31EBF7E37004559B1 /* wallabag2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = wallabag2.xcdatamodel; sourceTree = ""; }; 9C7B79E51EBF8070004559B1 /* ArticleSync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArticleSync.swift; sourceTree = ""; }; 9C8311851E06F56C004ACD5D /* ThemedTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemedTableViewCell.swift; sourceTree = ""; }; + 9C8B20A11F2612BA0084BBAA /* VoiceChoiceTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceChoiceTableViewController.swift; sourceTree = ""; }; 9C914A491EBD1D6000973BA6 /* CoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreData.swift; sourceTree = ""; }; 9C987A431DB79826005515F4 /* wallabag.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = wallabag.app; sourceTree = BUILT_PRODUCTS_DIR; }; 9C987A461DB79826005515F4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -315,6 +317,7 @@ 9CE369731DE632030005E20E /* SettingsTableViewController.swift */, 9C298B121E0BF8F400B72503 /* ThemeChoiceTableViewController.swift */, 9C25C9B71EEC99D700B97F6D /* TipViewController.swift */, + 9C8B20A11F2612BA0084BBAA /* VoiceChoiceTableViewController.swift */, ); path = Controller; sourceTree = ""; @@ -929,6 +932,7 @@ 9CF1F2211DBE97D80086B98B /* ServerViewController.swift in Sources */, 9CE369761DE63D030005E20E /* Setting.swift in Sources */, 9C7B79E61EBF8070004559B1 /* ArticleSync.swift in Sources */, + 9C8B20A21F2612BA0084BBAA /* VoiceChoiceTableViewController.swift in Sources */, 9C9034881E5A26D90074540B /* ThemeManager.swift in Sources */, 9CE9F2061DE2151B002E06F5 /* AboutViewController.swift in Sources */, 9CF1F21C1DBE943C0086B98B /* HomeViewController.swift in Sources */, @@ -1153,7 +1157,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = wallabag/wallabag.entitlements; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 63; DEVELOPMENT_TEAM = G97URPCGB8; INFOPLIST_FILE = wallabag/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; @@ -1171,7 +1175,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = "$(inherited)"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_ENTITLEMENTS = wallabag/wallabag.entitlements; - CURRENT_PROJECT_VERSION = 61; + CURRENT_PROJECT_VERSION = 63; DEVELOPMENT_TEAM = G97URPCGB8; INFOPLIST_FILE = wallabag/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; diff --git a/wallabag/Assets.xcassets/AppIcon.appiconset/Contents.json b/wallabag/Assets.xcassets/AppIcon.appiconset/Contents.json index a3bac0a2..ebc6de78 100644 --- a/wallabag/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/wallabag/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -101,6 +101,11 @@ "idiom" : "ipad", "filename" : "Icon-iPadPro@2x.png", "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" } ], "info" : { diff --git a/wallabag/Assets.xcassets/lips.imageset/Contents.json b/wallabag/Assets.xcassets/lips.imageset/Contents.json new file mode 100644 index 00000000..5fe8f6f1 --- /dev/null +++ b/wallabag/Assets.xcassets/lips.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "lips.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/wallabag/Assets.xcassets/lips.imageset/lips.pdf b/wallabag/Assets.xcassets/lips.imageset/lips.pdf new file mode 100644 index 0000000000000000000000000000000000000000..a67ec30e71f4e3005999fa1487c2d9c98ddb692c GIT binary patch literal 4126 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 I#~6VB1F`f51poj5 literal 0 HcmV?d00001 diff --git a/wallabag/Assets.xcassets/lipsfilled.imageset/Contents.json b/wallabag/Assets.xcassets/lipsfilled.imageset/Contents.json new file mode 100644 index 00000000..b771fc16 --- /dev/null +++ b/wallabag/Assets.xcassets/lipsfilled.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "lipsfill.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + }, + "properties" : { + "template-rendering-intent" : "template" + } +} \ No newline at end of file diff --git a/wallabag/Assets.xcassets/lipsfilled.imageset/lipsfill.pdf b/wallabag/Assets.xcassets/lipsfilled.imageset/lipsfill.pdf new file mode 100644 index 0000000000000000000000000000000000000000..af8dcd4a9ed8d4c433a10ce2dde8de87f08048bf GIT binary patch literal 4413 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 literal 0 HcmV?d00001 diff --git a/wallabag/Controller/ArticleViewController.swift b/wallabag/Controller/ArticleViewController.swift index 6b3a0c9c..5cdf8af6 100644 --- a/wallabag/Controller/ArticleViewController.swift +++ b/wallabag/Controller/ArticleViewController.swift @@ -9,9 +9,12 @@ import UIKit import TUSafariActivity import WallabagKit +import AVFoundation final class ArticleViewController: UIViewController { + let speechSynthetizer: AVSpeechSynthesizer = AVSpeechSynthesizer() + var lastOffsetY: CGFloat = 0 var update: Bool = true var entry: Entry! { @@ -28,6 +31,7 @@ final class ArticleViewController: UIViewController { @IBOutlet weak var contentWeb: UIWebView! @IBOutlet weak var readButton: UIBarButtonItem! @IBOutlet weak var starButton: UIBarButtonItem! + @IBOutlet weak var speechButton: UIBarButtonItem! @IBAction func add(_ sender: Any) { addHandler?() @@ -43,6 +47,19 @@ final class ArticleViewController: UIViewController { updateUi() } + @IBAction func speech(_ sender: Any) { + if !speechSynthetizer.isSpeaking { + let utterance = AVSpeechUtterance(string: entry.content!.withoutHTML) + utterance.rate = Setting.getSpeechRate() + utterance.voice = Setting.getSpeechVoice() + speechSynthetizer.speak(utterance) + speechButton.image = #imageLiteral(resourceName: "lipsfilled") + } else { + speechSynthetizer.stopSpeaking(at: .word) + speechButton.image = #imageLiteral(resourceName: "lips") + } + } + @IBAction func shareMenu(_ sender: UIBarButtonItem) { let activity = TUSafariActivity() let shareController = UIActivityViewController(activityItems: [URL(string: entry.url!) as Any], applicationActivities: [activity]) @@ -79,6 +96,7 @@ final class ArticleViewController: UIViewController { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.setNavigationBarHidden(false, animated: animated) + speechSynthetizer.stopSpeaking(at: .immediate) } private func loadArticleContent() { diff --git a/wallabag/Controller/SettingsTableViewController.swift b/wallabag/Controller/SettingsTableViewController.swift index 77721690..42a9247c 100644 --- a/wallabag/Controller/SettingsTableViewController.swift +++ b/wallabag/Controller/SettingsTableViewController.swift @@ -8,12 +8,18 @@ import UIKit import WallabagKit +import AVFoundation final class SettingsTableViewController: UITableViewController { @IBOutlet weak var currentThemeLabel: UILabel! @IBOutlet weak var justifySwitch: UISwitch! @IBOutlet weak var badgeSwitch: UISwitch! + @IBOutlet weak var speechRateSlider: UISlider! + + @IBAction func speechRateChanged(_ sender: UISlider) { + Setting.setSpeechRate(value: sender.value) + } @IBAction func justifySwitch(_ sender: UISwitch) { Setting.setJustifyArticle(value: sender.isOn) @@ -31,6 +37,10 @@ final class SettingsTableViewController: UITableViewController { justifySwitch.setOn(Setting.isJustifyArticle(), animated: false) badgeSwitch.setOn(Setting.isBadgeEnable(), animated: false) currentThemeLabel.text = Setting.getTheme().ucFirst + + speechRateSlider.minimumValue = AVSpeechUtteranceMinimumSpeechRate + speechRateSlider.maximumValue = AVSpeechUtteranceMaximumSpeechRate + speechRateSlider.value = Setting.getSpeechRate() } override func didMove(toParentViewController parent: UIViewController?) { diff --git a/wallabag/Controller/VoiceChoiceTableViewController.swift b/wallabag/Controller/VoiceChoiceTableViewController.swift new file mode 100644 index 00000000..f69a80d0 --- /dev/null +++ b/wallabag/Controller/VoiceChoiceTableViewController.swift @@ -0,0 +1,43 @@ +// +// VoiceChoiceTableViewController.swift +// wallabag +// +// Created by maxime marinel on 24/07/2017. +// Copyright © 2017 maxime marinel. All rights reserved. +// + +import UIKit +import AVFoundation + +final class VoiceChoiceTableViewController: UITableViewController { + + let voices: [AVSpeechSynthesisVoice] = AVSpeechSynthesisVoice.speechVoices() + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return voices.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath) + let voice: AVSpeechSynthesisVoice = voices[indexPath.row] + + cell.textLabel?.text = "\(voice.name) (\(voice.language))" + + if voice.identifier == Setting.getSpeechVoice()?.identifier { + cell.accessoryType = .checkmark + } + + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + for row in 0 ... tableView.numberOfRows(inSection: indexPath.section) { + tableView.cellForRow(at: IndexPath(row: row, section: indexPath.section))?.accessoryType = .none + } + tableView.cellForRow(at: indexPath)?.accessoryType = .checkmark + tableView.deselectRow(at: indexPath, animated: true) + + Setting.setSpeechVoice(identifier: voices[indexPath.row].identifier) + _ = navigationController?.popViewController(animated: true) + } +} diff --git a/wallabag/Info.plist b/wallabag/Info.plist index 192535da..157747aa 100644 --- a/wallabag/Info.plist +++ b/wallabag/Info.plist @@ -15,9 +15,9 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.1.2 + 2.2 CFBundleVersion - 61 + 63 ITSAppUsesNonExemptEncryption LSRequiresIPhoneOS diff --git a/wallabag/Lib/Setting.swift b/wallabag/Lib/Setting.swift index d83b0202..9cf9e186 100644 --- a/wallabag/Lib/Setting.swift +++ b/wallabag/Lib/Setting.swift @@ -8,6 +8,7 @@ import Foundation import WallabagKit +import AVFoundation class Setting { @@ -40,6 +41,8 @@ class Setting { case justifyArticle case articleTheme case badge + case speechRate + case speechVoice } static func getDefaultMode() -> RetrieveMode { @@ -73,6 +76,25 @@ class Setting { standard.set(value, forKey: Const.badge.rawValue) } + static func setSpeechRate(value: Float) { + standard.set(value, forKey: Const.speechRate.rawValue) + } + + static func getSpeechRate() -> Float { + guard standard.value(forKey: Const.speechRate.rawValue) != nil else { + return 0.5 + } + return standard.float(forKey: Const.speechRate.rawValue) + } + + static func getSpeechVoice() -> AVSpeechSynthesisVoice? { + return AVSpeechSynthesisVoice(identifier: standard.string(forKey: Const.speechVoice.rawValue) ?? "com.apple.ttsbundle.Daniel-compact") + } + + static func setSpeechVoice(identifier: String) { + standard.set(identifier, forKey: Const.speechVoice.rawValue) + } + static func getTheme() -> String { guard let value = standard.string(forKey: Const.articleTheme.rawValue) else { return "white" diff --git a/wallabag/Storyboard/Base.lproj/Main.storyboard b/wallabag/Storyboard/Base.lproj/Main.storyboard index a8c06f42..13ce56fd 100644 --- a/wallabag/Storyboard/Base.lproj/Main.storyboard +++ b/wallabag/Storyboard/Base.lproj/Main.storyboard @@ -1,10 +1,11 @@ - + - + + @@ -48,7 +49,7 @@