Commit 966233f8 authored by Romain Bertozzi's avatar Romain Bertozzi

project: reorganize structure, begin mvvm

This patch reorganizes the project structure to separate entities in
specific groups.
It also renames some files.

This is done to improve the separation of concerns of the components
of the project.
The renaming operation reflects the MVVM architecture that we will try
to implement.

Tuleap: #1327
Change-Id: I46109e82c87510e134f57d551946499b11334c44
parent e36578b2
This diff is collapsed.
......@@ -2,6 +2,7 @@
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com>
* Author: Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -25,134 +26,98 @@ enum AccountType: String {
case RING = "RING"
}
struct Account {
struct AccountModel {
// MARK: - Keys
fileprivate let accountAliasKey = "Account.alias"
fileprivate let accountVideoEnabledKey = "Account.videoEnabled"
fileprivate let accountUsernameKey = "Account.username"
fileprivate let accountAutoAnswerKey = "Account.autoAnswer"
fileprivate let accountTurnEnabledKey = "TURN.enable"
fileprivate let accountTurnUsernameKey = "TURN.username"
fileprivate let accountTurnServerKey = "TURN.server"
fileprivate let accountTurnPasswordKey = "TURN.password"
fileprivate let accountEnabledKey = "Account.enable"
fileprivate let accountUpnpEnabledKey = "Account.upnpEnabled"
fileprivate let accountHostnameKey = "Account.hostname"
fileprivate let accountTypeKey = "Account.type"
fileprivate let accountDisplayNameKey = "Account.displayName"
// MARK: - Properties
let id: String
// FIXME: This should be private
var details: Dictionary<String, String>
fileprivate var details: Dictionary<String, String>
var alias: String? {
get {
return details["Account.alias"]
}
set {
details["Account.alias"] = newValue
}
get {return details[accountAliasKey]}
set {details[accountAliasKey] = newValue}
}
var videoEnabled: Bool {
get {
return (details["Account.videoEnabled"]?.toBool())!
}
set {
details["Account.videoEnabled"] = newValue.toString()
}
get {return (details[accountVideoEnabledKey]?.toBool())!}
set {details[accountVideoEnabledKey] = newValue.toString()}
}
var username: String? {
get {
return details["Account.username"]
}
set {
details["Account.username"] = newValue
}
get {return details[accountUsernameKey]}
set {details[accountUsernameKey] = newValue}
}
var autoAnswer: Bool {
get {
return (details["Account.autoAnswer"]?.toBool())!
}
set {
details["Account.autoAnswer"] = newValue.toString()
}
get {return (details[accountAutoAnswerKey]?.toBool())!}
set {details[accountAutoAnswerKey] = newValue.toString()}
}
var turnEnabled: Bool {
get {
return (details["TURN.enable"]?.toBool())!
}
set {
details["TURN.enable"] = newValue.toString()
}
get {return (details[accountTurnEnabledKey]?.toBool())!}
set {details[accountTurnEnabledKey] = newValue.toString()}
}
var turnUsername: String? {
get {
return details["TURN.username"]
}
set {
details["TURN.username"] = newValue
}
get {return details[accountTurnUsernameKey]}
set {details[accountTurnUsernameKey] = newValue}
}
var turnServer: String? {
get {
return details["TURN.server"]
}
set {
details["TURN.server"] = newValue
}
get {return details[accountTurnServerKey]}
set {details[accountTurnServerKey] = newValue}
}
var turnPassword: String? {
get {
return details["TURN.password"]
}
set {
details["TURN.password"] = newValue
}
get {return details[accountTurnPasswordKey]}
set {details[accountTurnPasswordKey] = newValue}
}
var isEnabled: Bool {
get {
return (details["Account.enable"]?.toBool())!
}
get {return (details[accountEnabledKey]?.toBool())!}
set {
details["Account.enable"] = newValue.toString()
details[accountEnabledKey] = newValue.toString()
(ConfigurationManagerAdaptator.sharedManager() as AnyObject).setAccountActive(self.id, active: newValue)
}
}
var upnpEnabled: Bool {
get {
return (details["Account.upnpEnabled"]?.toBool())!
}
set {
details["Account.upnpEnabled"] = newValue.toString()
}
get {return (details[accountUpnpEnabledKey]?.toBool())!}
set {details[accountUpnpEnabledKey] = newValue.toString()}
}
var accountHostname: String? {
get {
return details["Account.hostname"]
}
set {
details["Account.hostname"] = newValue
}
get {return details[accountHostnameKey]}
set {details[accountHostnameKey] = newValue}
}
var accountType: AccountType {
get {
return AccountType(rawValue: details["Account.type"]!)!
}
set {
details["Account.type"] = newValue.rawValue
}
get {return AccountType(rawValue: details[accountTypeKey]!)!}
set {details[accountTypeKey] = newValue.rawValue}
}
var displayName: String? {
get {
return details["Account.displayName"]
}
set {
details["Account.displayName"] = newValue
}
get {return details[accountDisplayNameKey]}
set {details[accountDisplayNameKey] = newValue}
}
// MARK: - Init
init(accID: String) {
id = accID
init(accountID: String) {
id = accountID
details = (ConfigurationManagerAdaptator.sharedManager() as AnyObject).getAccountDetails(id) as! Dictionary<String, String>
}
......
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
struct AccountViewModel {
fileprivate var account: AccountModel
init (withAccount account: AccountModel) {
self.account = account
}
}
......@@ -23,16 +23,13 @@ import UIKit
class AccountDetailsViewController: UIViewController {
// MARK: - Properties
var account: Account!
var account: AccountModel!
@IBOutlet weak var detailsLabel: UILabel!
// MARK: - UIViewController
override func viewDidLoad() {
super.viewDidLoad()
// FIXME: This is just a placeholder
detailsLabel.text = account.details.description
}
override func didReceiveMemoryWarning() {
......
......@@ -27,7 +27,7 @@ class AccountTableViewCell: UITableViewCell {
@IBOutlet weak var accountNameLabel: UILabel!
@IBOutlet weak var accountTypeLabel: UILabel!
var account: Account!
var account: AccountModel!
// MARK: - UITableViewCell
override func awakeFromNib() {
......
......@@ -31,7 +31,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if (dRingAdapt.initDaemon() == true) {
if (dRingAdapt.startDaemon() == true) {
Timer.scheduledTimer(timeInterval: 0.05, target: self, selector: #selector(AppDelegate.pollFunction), userInfo: nil, repeats: true)
AccountModel.sharedInstance.reload()
AccountsService.sharedInstance.reload()
}
}
return true
......
......@@ -50,7 +50,7 @@
<rect key="frame" x="0.0" y="28" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="i1O-Yc-WGd" id="Bz1-A3-Z3f">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="bm0-lC-K2F">
......@@ -96,7 +96,7 @@
<rect key="frame" x="0.0" y="72" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="Ok2-8L-eMm" id="m64-AI-t2h">
<rect key="frame" x="0.0" y="0.0" width="320" height="43"/>
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Add Account" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kZv-uf-BsD" userLabel="Account Name Label">
......@@ -251,7 +251,7 @@
<!--Ring-->
<scene sceneID="oqo-zJ-m0o">
<objects>
<tabBarController title="Ring" id="qdG-Sd-QaE" sceneMemberID="viewController">
<tabBarController title="Ring" id="qdG-Sd-QaE" customClass="MainTabBarViewController" sceneMemberID="viewController">
<tabBar key="tabBar" contentMode="scaleToFill" id="zN5-xb-CQh">
<rect key="frame" x="0.0" y="0.0" width="320" height="49"/>
<autoresizingMask key="autoresizingMask"/>
......
......@@ -40,4 +40,4 @@ extension Bool {
}
return "false"
}
}
\ No newline at end of file
}
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
extension Notification.Name {
static let accountsChanged = Notification.Name("AccountsChanged")
}
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import UIKit
class MainTabBarViewController: UITabBarController {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !AccountsService.sharedInstance.hasAccounts() {
self.presentWalkthrough()
}
}
fileprivate func presentWalkthrough() {
let storyboard = UIStoryboard(name: "WalkthroughStoryboard", bundle: nil)
let viewController = storyboard.instantiateInitialViewController()!
self.present(viewController, animated: false, completion: nil)
}
}
......@@ -23,7 +23,7 @@ import UIKit
class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
// MARK: - Properties
let accountModel = AccountModel.sharedInstance
let accountService = AccountsService.sharedInstance
@IBOutlet weak var accountTableView: UITableView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var qrImageView: UIImageView!
......@@ -32,8 +32,8 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo
override func viewDidLoad() {
super.viewDidLoad()
if accountModel.accountList.count > 0 {
let acc = accountModel.accountList[0]
if accountService.accounts.count > 0 {
let acc = accountService.accounts[0]
nameLabel.text = acc.displayName
if let username = acc.username {
createQRFromString(username);
......@@ -68,14 +68,14 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return accountModel.accountList.count + 1
return accountService.accounts.count + 1
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row < accountModel.accountList.count {
if indexPath.row < accountService.accounts.count {
let cell = tableView.dequeueReusableCell(withIdentifier: "accountTableCell", for: indexPath) as! AccountTableViewCell
let account = accountModel.accountList[indexPath.row]
let account = accountService.accounts[indexPath.row]
cell.account = account
cell.accountNameLabel.text = account.alias
......@@ -91,14 +91,14 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.row == accountModel.accountList.count {
accountModel.addAccount()
if indexPath.row == accountService.accounts.count {
accountService.addAccount()
accountTableView.reloadData()
}
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if indexPath.row == accountModel.accountList.count {
if indexPath.row == accountService.accounts.count {
return false
}
return true
......@@ -106,14 +106,14 @@ class MeViewController: UIViewController, UITableViewDelegate, UITableViewDataSo
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
accountModel.removeAccount(indexPath.row)
accountService.removeAccount(indexPath.row)
accountTableView.reloadData()
}
}
// MARK: - Actions
@IBAction func addAccountClicked(_ sender: AnyObject) {
let index = IndexPath(row: accountModel.accountList.count, section: 0)
let index = IndexPath(row: accountService.accounts.count, section: 0)
accountTableView.selectRow(at: index, animated: false, scrollPosition: UITableViewScrollPosition.none)
tableView(accountTableView, didSelectRowAt: index)
}
......
/*
* Copyright (C) 2016 Savoir-faire Linux Inc.
*
* Author: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com>
* Authors: Edric Ladent-Milaret <edric.ladent-milaret@savoirfairelinux.com>
* Romain Bertozzi <romain.bertozzi@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -18,35 +19,60 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
import Foundation
class AccountsService {
// MARK: - Properties
fileprivate let confAdapter = ConfigurationManagerAdaptator.sharedManager() as AnyObject
class AccountModel {
/// Fileprivate Accounts list.
///
/// Can be used for all the operations, but won't be accessed from outside this file.
///
/// - SeeAlso: `accounts`
fileprivate var accountList: Array<AccountModel>
// MARK: - Properties
let confAdapt = ConfigurationManagerAdaptator.sharedManager() as AnyObject
var accountList: Array<Account> = []
/// Accounts list public interface
///
/// Can be used to access by constant the list of accounts.
fileprivate(set) var accounts: Array<AccountModel> {
set {
accountList = newValue
}
get {
let lAccounts = accountList
return lAccounts
}
}
// MARK: - Singleton
static let sharedInstance = AccountModel()
static let sharedInstance = AccountsService()
fileprivate init() {
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: "AccountsChanged"), object: nil, queue: nil, using: { _ in
self.reload()
accountList = []
NotificationCenter.default.addObserver(forName: .accountsChanged,
object: nil,
queue: nil,
using: { _ in
self.reload()
})
}
// MARK: - Methods
func hasAccounts() -> Bool {
return accountList.count > 0
}
func reload() {
accountList.removeAll()
for acc in confAdapt.getAccountList() {
let accID = acc as! String
accountList.append(Account(accID: accID))
for account in confAdapter.getAccountList() {
let accountID = account as! String
accountList.append(AccountModel(accountID: accountID))
}
}
func addAccount() {
// TODO: This need work for all account type
let details:NSMutableDictionary? = confAdapt.getAccountTemplate("RING")
let details:NSMutableDictionary? = confAdapter.getAccountTemplate("RING")
if details == nil {
print("Error retrieving Ring account template, can not continue");
return;
......@@ -54,14 +80,13 @@ class AccountModel {
details!.setValue("iOS", forKey: "Account.alias")
details!.setValue("iOS", forKey: "Account.displayName")
let convertedDetails = details as NSDictionary? as? [AnyHashable: Any] ?? [:]
let addResult:String! = confAdapt.addAccount(convertedDetails)
let addResult:String! = confAdapter.addAccount(convertedDetails)
print(addResult);
}
func removeAccount(_ row: Int) {
if row < accountList.count {
confAdapt.removeAccount(accountList[row].id)
confAdapter.removeAccount(accountList[row].id)
}
}
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="11542" systemVersion="15G1108" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="GnB-zf-djy">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="11524"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Navigation Controller-->
<scene sceneID="azk-5X-z35">
<objects>
<navigationController storyboardIdentifier="WalkthroughStoryboard" navigationBarHidden="YES" id="GnB-zf-djy" sceneMemberID="viewController">
<navigationBar key="navigationBar" contentMode="scaleToFill" id="Pgv-sV-7YL">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<connections>
<segue destination="zOM-us-BHp" kind="relationship" relationship="rootViewController" id="Cs0-7s-oVR"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="c5B-LX-9rY" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-566" y="143"/>
</scene>
<!--View Controller-->
<scene sceneID="BPo-UM-NNL">
<objects>
<viewController id="zOM-us-BHp" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Lps-eE-m7k"/>
<viewControllerLayoutGuide type="bottom" id="OgW-31-HCB"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="GmX-eQ-kCs">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
<navigationItem key="navigationItem" id="LPh-IM-uvN"/>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="PWI-c5-979" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="249" y="143"/>
</scene>
</scenes>
</document>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment