First version with app notifications
This commit is contained in:
97
main/Settings/TVCChooseAlertTone.swift
Normal file
97
main/Settings/TVCChooseAlertTone.swift
Normal file
@@ -0,0 +1,97 @@
|
||||
import UIKit
|
||||
import AudioToolbox
|
||||
|
||||
protocol NotificationSoundChangedDelegate {
|
||||
/// Use `#mute` to disable sounds and `#default` to use default notification sound.
|
||||
func notificationSoundCurrent() -> String
|
||||
/// Called every time the user changes selection
|
||||
func notificationSoundChanged(filename: String, title: String)
|
||||
}
|
||||
|
||||
class TVCChooseAlertTone: UITableViewController {
|
||||
|
||||
var delegate: NotificationSoundChangedDelegate!
|
||||
private lazy var selected: String = delegate.notificationSoundCurrent()
|
||||
|
||||
private func playTone(_ name: String) {
|
||||
switch name {
|
||||
case "#mute": return // No Sound
|
||||
case "#default": AudioServicesPlayAlertSound(1315) // Default sound
|
||||
default:
|
||||
guard let url = Bundle.main.url(forResource: name, withExtension: "caf") else {
|
||||
preconditionFailure("Something went wrong. Sound file \(name).caf does not exist.")
|
||||
}
|
||||
var soundId: SystemSoundID = 0
|
||||
AudioServicesCreateSystemSoundID(url as CFURL, &soundId)
|
||||
AudioServicesAddSystemSoundCompletion(soundId, nil, nil, { id, _ -> Void in
|
||||
AudioServicesDisposeSystemSoundID(id)
|
||||
}, nil)
|
||||
AudioServicesPlayAlertSound(soundId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Table View Delegate
|
||||
|
||||
override func numberOfSections(in _: UITableView) -> Int {
|
||||
AvailableSounds.count
|
||||
}
|
||||
|
||||
override func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
AvailableSounds[section].count
|
||||
}
|
||||
|
||||
override func tableView(_: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
section == 1 ? "AppCheck" : nil
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = tableView.dequeueReusableCell(withIdentifier: "SettingsAlertToneCell")!
|
||||
let src = AvailableSounds[indexPath.section][indexPath.row]
|
||||
cell.textLabel?.text = src.title
|
||||
cell.accessoryType = (src.file == selected) ? .checkmark : .none
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willSelectRowAt indexPath: IndexPath) -> IndexPath? {
|
||||
let src = AvailableSounds[indexPath.section][indexPath.row]
|
||||
selected = src.file
|
||||
tableView.reloadData() // re-apply checkmarks
|
||||
playTone(selected)
|
||||
delegate.notificationSoundChanged(filename: src.file, title: src.title)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sounds Data Source
|
||||
// afconvert input.aiff output.caf -d ima4 -f caff -v
|
||||
|
||||
fileprivate let AvailableSounds: [[(title: String, file: String)]] = [
|
||||
[ // System sounds
|
||||
("None", "#mute"),
|
||||
("Default", "#default")
|
||||
], [ // AppCheck sounds
|
||||
("Clock", "clock"),
|
||||
("Drum 1", "drum1"),
|
||||
("Drum 2", "drum2"),
|
||||
("Plop 1", "plop1"),
|
||||
("Plop 2", "plop2"),
|
||||
("Snap 1", "snap1"),
|
||||
("Snap 2", "snap2"),
|
||||
("Typewriter 1", "typewriter1"),
|
||||
("Typewriter 2", "typewriter2"),
|
||||
("Wood 1", "wood1"),
|
||||
("Wood 2", "wood2")
|
||||
]
|
||||
]
|
||||
|
||||
func AlertSoundTitle(for filename: String) -> String {
|
||||
for section in AvailableSounds {
|
||||
for row in section {
|
||||
if row.file == filename {
|
||||
return row.title
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
135
main/Settings/TVCConnectionAlerts.swift
Normal file
135
main/Settings/TVCConnectionAlerts.swift
Normal file
@@ -0,0 +1,135 @@
|
||||
import UIKit
|
||||
|
||||
class TVCConnectionAlerts: UITableViewController {
|
||||
|
||||
@IBOutlet var showNotifications: UISwitch!
|
||||
@IBOutlet var cellSound: UITableViewCell!
|
||||
|
||||
@IBOutlet var listsCustomA: UITableViewCell!
|
||||
@IBOutlet var listsCustomB: UITableViewCell!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
cascadeEnableConnAlert(PrefsShared.ConnectionAlerts.Enabled)
|
||||
cellSound.detailTextLabel?.text = AlertSoundTitle(for: PrefsShared.ConnectionAlerts.Sound)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
let (_, _, custA, custB) = DomainFilter.counts()
|
||||
listsCustomA.detailTextLabel?.text = "\(custA) Domains"
|
||||
listsCustomB.detailTextLabel?.text = "\(custB) Domains"
|
||||
}
|
||||
|
||||
private func cascadeEnableConnAlert(_ flag: Bool) {
|
||||
showNotifications.isOn = flag
|
||||
// en/disable related controls
|
||||
}
|
||||
|
||||
private func getListSelected(_ index: Int) -> Bool {
|
||||
switch index {
|
||||
case 0: return PrefsShared.ConnectionAlerts.Lists.Blocked
|
||||
case 1: return PrefsShared.ConnectionAlerts.Lists.CustomA
|
||||
case 2: return PrefsShared.ConnectionAlerts.Lists.CustomB
|
||||
case 3: return PrefsShared.ConnectionAlerts.Lists.Else
|
||||
default: preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
private func setListSelected(_ index: Int, _ value: Bool) {
|
||||
switch index {
|
||||
case 0: PrefsShared.ConnectionAlerts.Lists.Blocked = value
|
||||
case 1: PrefsShared.ConnectionAlerts.Lists.CustomA = value
|
||||
case 2: PrefsShared.ConnectionAlerts.Lists.CustomB = value
|
||||
case 3: PrefsShared.ConnectionAlerts.Lists.Else = value
|
||||
default: preconditionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Toggles
|
||||
|
||||
@IBAction private func toggleShowNotifications(_ sender: UISwitch) {
|
||||
PrefsShared.ConnectionAlerts.Enabled = sender.isOn
|
||||
cascadeEnableConnAlert(sender.isOn)
|
||||
GlassVPN.send(.notificationSettingsChanged())
|
||||
if sender.isOn {
|
||||
PushNotification.requestAuthorization { granted in
|
||||
if !granted {
|
||||
NotificationsDisabledAlert(presentIn: self)
|
||||
self.cascadeEnableConnAlert(false)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PushNotification.cancel(.AllConnectionAlertNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Table View Delegate
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let cell = super.tableView(tableView, cellForRowAt: indexPath)
|
||||
let checked: Bool
|
||||
switch indexPath.section {
|
||||
case 1: // mode selection
|
||||
checked = (indexPath.row == (PrefsShared.ConnectionAlerts.ExcludeMode ? 1 : 0))
|
||||
case 2: // include & exclude lists
|
||||
checked = getListSelected(indexPath.row)
|
||||
default: return cell // process only checkmarked cells
|
||||
}
|
||||
cell.accessoryType = checked ? .checkmark : .none
|
||||
return cell
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
switch indexPath.section {
|
||||
case 1: // mode selection
|
||||
PrefsShared.ConnectionAlerts.ExcludeMode = indexPath.row == 1
|
||||
tableView.reloadSections(.init(integer: 2), with: .none)
|
||||
case 2: // include & exclude lists
|
||||
let prev = tableView.cellForRow(at: indexPath)?.accessoryType == .checkmark
|
||||
setListSelected(indexPath.row, !prev)
|
||||
default: return // process only checkmarked cells
|
||||
}
|
||||
tableView.deselectRow(at: indexPath, animated: true)
|
||||
tableView.reloadSections(.init(integer: indexPath.section), with: .none)
|
||||
GlassVPN.send(.notificationSettingsChanged())
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
||||
if section == 2 {
|
||||
return PrefsShared.ConnectionAlerts.ExcludeMode ? "Exclude All" : "Include All"
|
||||
}
|
||||
return super.tableView(tableView, titleForHeaderInSection: section)
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let dest = segue.destination as? TVCFilter {
|
||||
switch segue.identifier {
|
||||
case "segueFilterListCustomA":
|
||||
dest.navigationItem.title = "Custom List A"
|
||||
dest.currentFilter = .customA
|
||||
case "segueFilterListCustomB":
|
||||
dest.navigationItem.title = "Custom List B"
|
||||
dest.currentFilter = .customB
|
||||
default:
|
||||
break
|
||||
}
|
||||
} else if let tvc = segue.destination as? TVCChooseAlertTone {
|
||||
tvc.delegate = self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sound Selection
|
||||
|
||||
extension TVCConnectionAlerts: NotificationSoundChangedDelegate {
|
||||
func notificationSoundCurrent() -> String {
|
||||
PrefsShared.ConnectionAlerts.Sound
|
||||
}
|
||||
|
||||
func notificationSoundChanged(filename: String, title: String) {
|
||||
cellSound.detailTextLabel?.text = title
|
||||
PrefsShared.ConnectionAlerts.Sound = filename
|
||||
GlassVPN.send(.notificationSettingsChanged())
|
||||
}
|
||||
}
|
||||
@@ -25,14 +25,10 @@ class TVCFilter: UITableViewController, EditActionsRemove {
|
||||
}
|
||||
|
||||
@IBAction private func addNewFilter() {
|
||||
let desc: String
|
||||
switch currentFilter {
|
||||
case .blocked: desc = "Enter the domain name you wish to block."
|
||||
case .ignored: desc = "Enter the domain name you wish to ignore."
|
||||
default: return
|
||||
}
|
||||
let alert = AskAlert(title: "Create new filter", text: desc, buttonText: "Add") {
|
||||
guard let dom = $0.textFields?.first?.text else {
|
||||
let alert = AskAlert(title: "Create new filter",
|
||||
text: "Enter the domain name you wish to add.",
|
||||
buttonText: "Add") {
|
||||
guard let dom = $0.textFields?.first?.text?.lowercased() else {
|
||||
return
|
||||
}
|
||||
guard dom.contains("."), !dom.isKnownSLD() else {
|
||||
|
||||
154
main/Settings/TVCReminderAlerts.swift
Normal file
154
main/Settings/TVCReminderAlerts.swift
Normal file
@@ -0,0 +1,154 @@
|
||||
import UIKit
|
||||
|
||||
class TVCReminderAlerts: UITableViewController {
|
||||
|
||||
@IBOutlet var restartAllow: UISwitch!
|
||||
@IBOutlet var restartAllowNotify: UISwitch!
|
||||
@IBOutlet var restartAllowBadge: UISwitch!
|
||||
@IBOutlet var restartSound: UITableViewCell!
|
||||
|
||||
@IBOutlet var recordingAllow: UISwitch!
|
||||
@IBOutlet var recordingSound: UITableViewCell!
|
||||
|
||||
private enum ReminderCellType { case Restart, Recording }
|
||||
private var selectedSound: ReminderCellType = .Restart
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
restartAllowNotify.isOn = PrefsShared.RestartReminder.WithText
|
||||
restartAllowBadge.isOn = PrefsShared.RestartReminder.WithBadge
|
||||
restartSound.detailTextLabel?.text = AlertSoundTitle(for: PrefsShared.RestartReminder.Sound)
|
||||
recordingSound.detailTextLabel?.text = AlertSoundTitle(for: Prefs.RecordingReminder.Sound)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
readNotificationState { (allowStart, allowRecord, isProvisional) in
|
||||
self.cascadeEnableRestart(allowStart && !isProvisional)
|
||||
self.recordingAllow.isOn = (allowRecord && !isProvisional)
|
||||
self.setIndicateProvisional(isProvisional)
|
||||
}
|
||||
}
|
||||
|
||||
private func readNotificationState(_ closure: @escaping (Bool, Bool, Bool) -> Void) {
|
||||
let en1 = PrefsShared.RestartReminder.Enabled
|
||||
let en2 = Prefs.RecordingReminder.Enabled
|
||||
closure(en1, en2, false)
|
||||
guard en1 || en2 else { return }
|
||||
PushNotification.allowed { state in
|
||||
switch state {
|
||||
case .NotDetermined, .Denied: closure(false, false, false)
|
||||
case .Authorized, .Provisional: closure(en1, en2, state == .Provisional)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func cascadeEnableRestart(_ flag: Bool) {
|
||||
restartAllow.isOn = flag
|
||||
restartAllowNotify.isEnabled = flag
|
||||
restartAllowBadge.isEnabled = flag
|
||||
}
|
||||
|
||||
private func setIndicateProvisional(_ flag: Bool) {
|
||||
if flag {
|
||||
restartAllow.thumbTintColor = .systemGreen
|
||||
recordingAllow.thumbTintColor = .systemGreen
|
||||
} else {
|
||||
// thumb tint is only set in provisional mode
|
||||
if restartAllow.thumbTintColor <-? nil { restartAllow.isOn = true }
|
||||
if recordingAllow.thumbTintColor <-? nil { recordingAllow.isOn = true }
|
||||
}
|
||||
}
|
||||
|
||||
private func updateBadge() {
|
||||
let flag = (restartAllow.isOn && restartAllowBadge.isOn && GlassVPN.state != .on)
|
||||
UIApplication.shared.applicationIconBadgeNumber = flag ? 1 : 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Toggles
|
||||
|
||||
extension TVCReminderAlerts {
|
||||
@IBAction private func toggleAllowRestartReminder(_ sender: UISwitch) {
|
||||
PrefsShared.RestartReminder.Enabled = sender.isOn
|
||||
cascadeEnableRestart(sender.isOn)
|
||||
updateBadge()
|
||||
if sender.isOn {
|
||||
askAuthorization {}
|
||||
} else {
|
||||
PushNotification.cancel(.CantStopMeNowReminder)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func toggleAllowRestartNotify(_ sender: UISwitch) {
|
||||
PrefsShared.RestartReminder.WithText = sender.isOn
|
||||
if !sender.isOn {
|
||||
PushNotification.cancel(.CantStopMeNowReminder)
|
||||
}
|
||||
}
|
||||
|
||||
@IBAction private func toggleAllowRestartBadge(_ sender: UISwitch) {
|
||||
PrefsShared.RestartReminder.WithBadge = sender.isOn
|
||||
updateBadge()
|
||||
}
|
||||
|
||||
@IBAction private func toggleAllowRecordingReminder(_ sender: UISwitch) {
|
||||
Prefs.RecordingReminder.Enabled = sender.isOn
|
||||
if sender.isOn {
|
||||
askAuthorization { PushNotification.scheduleRecordingReminder(force: false) }
|
||||
} else {
|
||||
PushNotification.cancel(.YouShallRecordMoreReminder)
|
||||
}
|
||||
}
|
||||
|
||||
private func askAuthorization(_ closure: @escaping () -> Void) {
|
||||
setIndicateProvisional(false)
|
||||
PushNotification.requestAuthorization { granted in
|
||||
if granted {
|
||||
closure()
|
||||
} else {
|
||||
NotificationsDisabledAlert(presentIn: self)
|
||||
self.cascadeEnableRestart(false)
|
||||
self.recordingAllow.isOn = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Sound Selection
|
||||
|
||||
extension TVCReminderAlerts: NotificationSoundChangedDelegate {
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
|
||||
if let tvc = segue.destination as? TVCChooseAlertTone {
|
||||
switch segue.identifier {
|
||||
case "segueSoundRestartReminder": selectedSound = .Restart
|
||||
case "segueSoundRecordingReminder": selectedSound = .Recording
|
||||
default: preconditionFailure()
|
||||
}
|
||||
tvc.delegate = self
|
||||
}
|
||||
}
|
||||
|
||||
func notificationSoundCurrent() -> String {
|
||||
switch selectedSound {
|
||||
case .Restart: return PrefsShared.RestartReminder.Sound
|
||||
case .Recording: return Prefs.RecordingReminder.Sound
|
||||
}
|
||||
}
|
||||
|
||||
func notificationSoundChanged(filename: String, title: String) {
|
||||
switch selectedSound {
|
||||
case .Restart:
|
||||
restartSound.detailTextLabel?.text = title
|
||||
PrefsShared.RestartReminder.Sound = filename
|
||||
case .Recording:
|
||||
recordingSound.detailTextLabel?.text = title
|
||||
Prefs.RecordingReminder.Sound = filename
|
||||
if Prefs.RecordingReminder.Enabled {
|
||||
PushNotification.scheduleRecordingReminder(force: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,36 +6,63 @@ class TVCSettings: UITableViewController {
|
||||
@IBOutlet var cellDomainsIgnored: UITableViewCell!
|
||||
@IBOutlet var cellDomainsBlocked: UITableViewCell!
|
||||
@IBOutlet var cellPrivacyAutoDelete: UITableViewCell!
|
||||
@IBOutlet var cellNotificationReminder: UITableViewCell!
|
||||
@IBOutlet var cellNotificationConnectionAlert: UITableViewCell!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
reloadToggleState()
|
||||
reloadDataSource()
|
||||
NotifyVPNStateChanged.observe(call: #selector(reloadToggleState), on: self)
|
||||
NotifyDNSFilterChanged.observe(call: #selector(reloadDataSource), on: self)
|
||||
reloadVPNState()
|
||||
reloadLoggingFilterUI()
|
||||
reloadPrivacyUI()
|
||||
NotifyVPNStateChanged.observe(call: #selector(reloadVPNState), on: self)
|
||||
NotifyDNSFilterChanged.observe(call: #selector(reloadLoggingFilterUI), on: self)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
reloadNotificationState()
|
||||
}
|
||||
|
||||
// MARK: - VPN Proxy Settings
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
// FIXME: there is a lag between tap and open when run on device
|
||||
if let cell = tableView.cellForRow(at: indexPath), cell === cellPrivacyAutoDelete {
|
||||
openAutoDeletePicker()
|
||||
}
|
||||
}
|
||||
|
||||
func openRestartVPNSettings() { scrollToSection(0, animated: false) }
|
||||
func openNotificationSettings() { scrollToSection(2, animated: false) }
|
||||
private func scrollToSection(_ section: Int, animated: Bool) {
|
||||
tableView.scrollToRow(at: .init(row: 0, section: section), at: .top, animated: animated)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - VPN Proxy Settings
|
||||
|
||||
extension TVCSettings {
|
||||
@objc private func reloadVPNState() {
|
||||
vpnToggle.isOn = (GlassVPN.state != .off)
|
||||
vpnToggle.onTintColor = (GlassVPN.state == .inbetween ? .systemYellow : nil)
|
||||
UIApplication.shared.applicationIconBadgeNumber =
|
||||
!vpnToggle.isOn &&
|
||||
PrefsShared.RestartReminder.Enabled &&
|
||||
PrefsShared.RestartReminder.WithBadge ? 1 : 0
|
||||
}
|
||||
|
||||
@IBAction private func toggleVPNProxy(_ sender: UISwitch) {
|
||||
GlassVPN.setEnabled(sender.isOn)
|
||||
}
|
||||
|
||||
@objc private func reloadToggleState() {
|
||||
vpnToggle.isOn = (GlassVPN.state != .off)
|
||||
vpnToggle.onTintColor = (GlassVPN.state == .inbetween ? .systemYellow : nil)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Logging Filter
|
||||
|
||||
@objc private func reloadDataSource() {
|
||||
let (blocked, ignored) = DomainFilter.counts()
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Logging Filter
|
||||
|
||||
extension TVCSettings {
|
||||
@objc private func reloadLoggingFilterUI() {
|
||||
let (blocked, ignored, _, _) = DomainFilter.counts()
|
||||
cellDomainsIgnored.detailTextLabel?.text = "\(ignored) Domains"
|
||||
cellDomainsBlocked.detailTextLabel?.text = "\(blocked) Domains"
|
||||
let (one, two) = autoDeleteSelection([1, 7, 31])
|
||||
cellPrivacyAutoDelete.detailTextLabel?.text = autoDeleteString(one, unit: two)
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) {
|
||||
@@ -65,38 +92,84 @@ class TVCSettings: UITableViewController {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Privacy
|
||||
|
||||
extension TVCSettings {
|
||||
private func reloadPrivacyUI() {
|
||||
let (num, unit) = getAutoDeleteSelection([1, 7, 31])
|
||||
let str: String
|
||||
switch num {
|
||||
case 0: str = "Never"
|
||||
case 1: str = "1 \(["Day", "Week", "Month"][unit])"
|
||||
default: str = "\(num) \(["Days", "Weeks", "Months"][unit])"
|
||||
}
|
||||
cellPrivacyAutoDelete.detailTextLabel?.text = str
|
||||
}
|
||||
|
||||
private func getAutoDeleteSelection(_ multiplier: [Int]) -> (Int, Int) {
|
||||
let current = PrefsShared.AutoDeleteLogsDays
|
||||
let snd = multiplier.lastIndex { current % $0 == 0 }! // make sure 1 is in list
|
||||
return (current / multiplier[snd], snd)
|
||||
}
|
||||
|
||||
// MARK: - Privacy
|
||||
|
||||
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
// FIXME: there is a lag between tap and open when run on device
|
||||
if let cell = tableView.cellForRow(at: indexPath), cell === cellPrivacyAutoDelete {
|
||||
let multiplier = [1, 7, 31]
|
||||
let (one, two) = autoDeleteSelection(multiplier)
|
||||
|
||||
let picker = DurationPickerAlert(
|
||||
title: "Auto-delete logs",
|
||||
detail: "Warning: Logs older than the selected interval are deleted immediately! " +
|
||||
"Logs are also deleted on each app launch, and periodically in the background as long as the VPN is running.",
|
||||
options: [(0...30).map{"\($0)"}, ["Days", "Weeks", "Months"]],
|
||||
widths: [0.4, 0.6])
|
||||
picker.pickerView.setSelection([min(30, one), two])
|
||||
picker.present(in: self) { _, idx in
|
||||
cell.detailTextLabel?.text = autoDeleteString(idx[0], unit: idx[1])
|
||||
let asDays = idx[0] * multiplier[idx[1]]
|
||||
PrefsShared.AutoDeleteLogsDays = asDays
|
||||
if !GlassVPN.send(.autoDelete(after: asDays)) {
|
||||
// if VPN isn't active, fallback to immediate local delete
|
||||
TheGreatDestroyer.deleteLogs(olderThan: asDays)
|
||||
}
|
||||
private func openAutoDeletePicker() {
|
||||
let multiplier = [1, 7, 31]
|
||||
let (one, two) = getAutoDeleteSelection(multiplier)
|
||||
|
||||
let picker = DurationPickerAlert(
|
||||
title: "Auto-delete logs",
|
||||
detail: "Warning: Logs older than the selected interval are deleted immediately! " +
|
||||
"Logs are also deleted on each app launch, and periodically in the background as long as the VPN is running.",
|
||||
options: [(0...30).map{"\($0)"}, ["Days", "Weeks", "Months"]],
|
||||
widths: [0.4, 0.6])
|
||||
picker.pickerView.setSelection([min(30, one), two])
|
||||
picker.present(in: self) { _, idx in
|
||||
let asDays = idx[0] * multiplier[idx[1]]
|
||||
PrefsShared.AutoDeleteLogsDays = asDays
|
||||
self.reloadPrivacyUI()
|
||||
if !GlassVPN.send(.autoDelete(after: asDays)) {
|
||||
// if VPN isn't active, fallback to immediate local delete
|
||||
TheGreatDestroyer.deleteLogs(olderThan: asDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Notification Settings
|
||||
|
||||
extension TVCSettings {
|
||||
private func reloadNotificationState() {
|
||||
let lbl1 = cellNotificationReminder.detailTextLabel
|
||||
let lbl2 = cellNotificationConnectionAlert.detailTextLabel
|
||||
readNotificationState { (realAllowed, provisional) in
|
||||
lbl1?.text = provisional ? "Enabled" : "Disabled"
|
||||
lbl2?.text = realAllowed ? "Enabled" : "Disabled"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Reset Settings
|
||||
|
||||
private func readNotificationState(_ closure: @escaping (_ all: Bool, _ prov: Bool) -> Void) {
|
||||
let en1 = PrefsShared.ConnectionAlerts.Enabled
|
||||
let en2 = Prefs.RecordingReminder.Enabled || PrefsShared.RestartReminder.Enabled
|
||||
closure(en1, en2)
|
||||
guard en1 || en2 else { return }
|
||||
PushNotification.allowed { state in
|
||||
switch state {
|
||||
case .NotDetermined, .Denied: closure(false, false)
|
||||
case .Authorized: closure(en1, en2)
|
||||
case .Provisional: closure(false, en2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Reset Settings
|
||||
|
||||
extension TVCSettings {
|
||||
@IBAction private func resetTutorialAlerts(_ sender: UIButton) {
|
||||
Prefs.DidShowTutorial.Welcome = false
|
||||
Prefs.DidShowTutorial.Recordings = false
|
||||
@@ -112,10 +185,12 @@ class TVCSettings: UITableViewController {
|
||||
TheGreatDestroyer.deleteAllLogs()
|
||||
}.presentIn(self)
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Advanced
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Advanced
|
||||
|
||||
extension TVCSettings {
|
||||
@IBAction private func exportDB() {
|
||||
AppDB?.vacuum()
|
||||
let sheet = UIActivityViewController(activityItems: [URL.internalDB()], applicationActivities: nil)
|
||||
@@ -130,24 +205,3 @@ class TVCSettings: UITableViewController {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------
|
||||
// |
|
||||
// | MARK: - Helper methods
|
||||
// |
|
||||
// -------------------------------
|
||||
|
||||
private func autoDeleteSelection(_ multiplier: [Int]) -> (Int, Int) {
|
||||
let current = PrefsShared.AutoDeleteLogsDays
|
||||
let snd = multiplier.lastIndex { current % $0 == 0 }! // make sure 1 is in list
|
||||
return (current / multiplier[snd], snd)
|
||||
}
|
||||
|
||||
private func autoDeleteString(_ num: Int, unit: Int) -> String {
|
||||
switch num {
|
||||
case 0: return "Never"
|
||||
case 1: return "1 \(["Day", "Week", "Month"][unit])"
|
||||
default: return "\(num) \(["Days", "Weeks", "Months"][unit])"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user