Contribute recording

This commit is contained in:
relikd
2020-08-28 18:36:52 +02:00
parent 52fa2e460e
commit 42aa7cf926
18 changed files with 229 additions and 12 deletions

View File

@@ -70,6 +70,7 @@
545DDDD124436983003B6544 /* QuickUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DDDD024436983003B6544 /* QuickUI.swift */; };
545DDDD424466D37003B6544 /* AutoLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = 545DDDD324466D37003B6544 /* AutoLayout.swift */; };
546063E523FEFAFE008F505A /* DBCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B7562223D7B2DC008F0C41 /* DBCore.swift */; };
54686A7624F8062C0084934D /* NotificationBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54686A7524F8062C0084934D /* NotificationBanner.swift */; };
54751E512423955100168273 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54751E502423955000168273 /* URL.swift */; };
54751E522423955100168273 /* URL.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54751E502423955000168273 /* URL.swift */; };
54953E3323DC752E0054345C /* DBCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54B7562223D7B2DC008F0C41 /* DBCore.swift */; };
@@ -268,6 +269,7 @@
545DDDCE243E6267003B6544 /* TutorialSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TutorialSheet.swift; sourceTree = "<group>"; };
545DDDD024436983003B6544 /* QuickUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickUI.swift; sourceTree = "<group>"; };
545DDDD324466D37003B6544 /* AutoLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoLayout.swift; sourceTree = "<group>"; };
54686A7524F8062C0084934D /* NotificationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationBanner.swift; sourceTree = "<group>"; };
54751E502423955000168273 /* URL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URL.swift; sourceTree = "<group>"; };
548B1F9423D338EC005B047C /* main.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = main.entitlements; sourceTree = "<group>"; };
54953E5E23DEBE840054345C /* TVCDomains.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TVCDomains.swift; sourceTree = "<group>"; };
@@ -565,6 +567,7 @@
541FC47524A12D01009154D8 /* IBViews.swift */,
5404AEEC24A95F3F003B2F54 /* SlideInAnimation.swift */,
541075D024CDBA0000D6F1BF /* ThrottledBatchQueue.swift */,
54686A7524F8062C0084934D /* NotificationBanner.swift */,
);
path = "Common Classes";
sourceTree = "<group>";
@@ -1017,6 +1020,7 @@
54B345A6241BB982004C53CC /* Notifications.swift in Sources */,
54448A2E2486464F00771C96 /* Array.swift in Sources */,
54E67E4B24A8C6370025D261 /* GlassVPN.swift in Sources */,
54686A7624F8062C0084934D /* NotificationBanner.swift in Sources */,
541FC47824A1453F009154D8 /* VCCoOccurrence.swift in Sources */,
54B345AB241BBA5B004C53CC /* AlertSheet.swift in Sources */,
541DCA6124A6B0F6005F1A4B /* Color.swift in Sources */,

Binary file not shown.

View File

@@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "img.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 572 B

View File

@@ -0,0 +1,26 @@
{
"images" : [
{
"filename" : "img.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "img@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "img@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 B

View File

@@ -0,0 +1,63 @@
import UIKit
struct NotificationBanner {
enum Style {
case fail, ok
}
let view: UIView
init(_ msg: String, style: Style) {
let bg, fg: UIColor
let imgName: String
switch style {
case .fail:
bg = .systemRed
fg = UIColor.black.withAlphaComponent(0.80)
imgName = "circle-x"
case .ok:
bg = .systemGreen
fg = UIColor.black.withAlphaComponent(0.65)
imgName = "circle-check"
}
view = UIView()
view.backgroundColor = bg
let lbl = QuickUI.label(msg, style: .callout)
lbl.textColor = fg
lbl.numberOfLines = 0
lbl.font = lbl.font.bold()
let img = QuickUI.image(UIImage(named: imgName))
img.tintColor = fg
view.addSubview(lbl)
view.addSubview(img)
img.anchor([.leading, .centerY], to: view.layoutMarginsGuide)
lbl.anchor([.top, .bottom, .trailing], to: view.layoutMarginsGuide)
img.widthAnchor =&= 25
img.heightAnchor =&= 25
lbl.leadingAnchor =&= img.trailingAnchor + 8
img.bottomAnchor =<= view.bottomAnchor - 8
lbl.bottomAnchor =<= view.bottomAnchor - 8
}
/// Animate header banner from the top of the view. Show for `delay` seconds and then hide again.
/// - Parameter onClose: Run after the close animation finishes.
func present(in vc: UIViewController, hideAfter delay: TimeInterval = 3, onClose: (() -> Void)? = nil) {
vc.view.addSubview(view)
view.anchor([.leading, .trailing], to: vc.view!)
vc.view.layoutIfNeeded() // sets the height
let h = view.frame.height
let constraint = view.topAnchor =&= vc.view.topAnchor - h
vc.view.layoutIfNeeded() // hide view
UIView.animate(withDuration: 0.3, animations: {
constraint.constant = 0
vc.view.layoutIfNeeded() // animate view
UIView.animate(withDuration: 0.3, delay: delay, options: .curveLinear, animations: {
constraint.constant = -h
vc.view.layoutIfNeeded() // hide again
}, completion: { _ in
self.view.removeFromSuperview()
onClose?()
})
})
}
}

View File

@@ -33,7 +33,9 @@ extension FilterOptions {
}
extension Recording {
var fallbackTitle: String { get { "Unnamed Recording #\(id)" } }
var fallbackTitle: String { get {
isLongTerm ? "Background Recording" : "Unnamed Recording #\(id)"
} }
var duration: Timestamp? { get { stop == nil ? nil : stop! - start } }
var isLongTerm: Bool { (duration ?? 0) > Timestamp.hours(1) }
}

View File

@@ -39,9 +39,13 @@ func AskAlert(title: String?, text: String?, buttonText: String = "Continue", bu
/// Show alert hinting the user to go to system settings and re-enable notifications.
func NotificationsDisabledAlert(presentIn viewController: UIViewController) {
Alert(title: "Notifications Disabled",
text: "Go to System Settings > Notifications > AppCheck to re-enable notifications."
).presentIn(viewController)
AskAlert(title: "Notifications Disabled",
text: "Go to System Settings > Notifications > AppCheck to re-enable notifications.",
buttonText: "Open settings") { _ in
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.openURL(url)
}
}.presentIn(viewController)
}
// MARK: Alert with multiple options

View File

@@ -260,7 +260,7 @@
</barButtonItem>
<barButtonItem key="rightBarButtonItem" title="Send" style="done" id="PWY-06-ykI">
<connections>
<action selector="shareRecording" destination="P0a-ZP-uEV" id="lSZ-8V-ANw"/>
<action selector="shareRecording:" destination="P0a-ZP-uEV" id="vCR-9d-91D"/>
</connections>
</barButtonItem>
</navigationItem>
@@ -274,6 +274,9 @@
<fontDescription key="fontDescription" style="UICTFontTextStyleBody"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eWC-xB-CJe">
<rect key="frame" x="292" y="12" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<constraints>
@@ -283,11 +286,14 @@
<constraint firstItem="dN0-a9-t4G" firstAttribute="leading" secondItem="lie-9P-DtH" secondAttribute="leading" id="BWF-aS-ah3"/>
<constraint firstItem="lie-9P-DtH" firstAttribute="trailing" secondItem="fFm-v5-DGy" secondAttribute="trailing" id="GRg-BF-rYE"/>
<constraint firstItem="lie-9P-DtH" firstAttribute="bottom" secondItem="fFm-v5-DGy" secondAttribute="bottom" id="IE3-o5-suZ"/>
<constraint firstItem="eWC-xB-CJe" firstAttribute="trailing" secondItem="dN0-a9-t4G" secondAttribute="trailingMargin" id="NgN-7W-Ecm"/>
<constraint firstItem="lie-9P-DtH" firstAttribute="trailing" secondItem="dN0-a9-t4G" secondAttribute="trailing" id="UuJ-xj-xBK"/>
<constraint firstItem="eWC-xB-CJe" firstAttribute="centerY" secondItem="dN0-a9-t4G" secondAttribute="centerY" id="jvl-OA-Vdr"/>
</constraints>
<viewLayoutGuide key="safeArea" id="lie-9P-DtH"/>
</view>
<connections>
<outlet property="sendActivity" destination="eWC-xB-CJe" id="rx3-Jz-ppT"/>
<outlet property="text" destination="fFm-v5-DGy" id="fwm-RE-gHu"/>
</connections>
</viewController>

View File

@@ -106,7 +106,7 @@ class TVCAppSearch: UITableViewController, UISearchBarDelegate {
let alert = AskAlert(title: "App Name",
text: "Be as descriptive as possible. Preferably use app bundle id if available. Alternatively use app name or a link to a public repository.",
buttonText: "Set") {
self.delegate?.appSearch(didSelect: "un.known", appName: $0.textFields?.first?.text, developer: nil)
self.delegate?.appSearch(didSelect: "_manually", appName: $0.textFields?.first?.text, developer: nil)
self.closeThis()
}
alert.addTextField { $0.placeholder = "com.apple.notes" }

View File

@@ -21,7 +21,7 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
if record.isLongTerm {
appId = nil
appIcon.image = nil
appTitle.text = "Background Recording"
appTitle.text = record.fallbackTitle
appDeveloper.text = nil
chooseAppTap.isEnabled = false
} else {
@@ -158,7 +158,7 @@ class VCEditRecording: UIViewController, UITextFieldDelegate, UITextViewDelegate
private func validateSaveButton() {
let changed = (appId != record.appId
|| (appTitle.text != record.title && appTitle.text != "Tap here to choose app")
|| (appTitle.text != record.title && appTitle.text != "Tap here to choose app" && appTitle.text != record.fallbackTitle)
|| appDeveloper.text != record.subtitle
|| inputNotes.text != record.notes ?? "")
buttonSave.isEnabled = changed || deleteOnCancel // always allow save for new recordings

View File

@@ -6,6 +6,7 @@ class VCShareRecording : UIViewController {
private var jsonData: Data?
@IBOutlet private var text : UITextView!
@IBOutlet private var sendActivity : UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
@@ -59,8 +60,68 @@ class VCShareRecording : UIViewController {
dismiss(animated: true)
}
@IBAction private func shareRecording() {
print("\(String(data: jsonData!, encoding: .utf8)!)")
Alert(title: "Not implemented yet", text: nil).presentIn(self)
@IBAction private func shareRecording(_ sender: UIBarButtonItem) {
sender.isEnabled = false
sendActivity.startAnimating()
let url = URL(string: "http://127.0.0.1/api/v1/contribute/")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
URLSession.shared.dataTask(with: request) { data, response, error in
DispatchQueue.main.async { [weak self] in
sender.isEnabled = true
self?.sendActivity.stopAnimating()
guard error == nil, let data = data,
let response = response as? HTTPURLResponse else {
self?.banner(.fail, "\(error?.localizedDescription ?? "Unkown error occurred")")
return
}
let json = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any]
let status = json?["status"] as? String
let v = json?["v"] as? Int ?? 0
guard v > 0, (200 ... 299) ~= response.statusCode else {
QLog.Warning("Couldn't contribute: \(status ?? "unkown reason")")
self?.banner(.fail, "Server couldn't parse request.\nTry again later.")
return
}
var autoHide = true
if v == 1, let urlStr = json?["url"] as? String {
let nextUpdateIn = json?["when"] as? Int
self?.showOpenResultsAlert(urlStr, when: nextUpdateIn)
autoHide = false
}
self?.banner(.ok, "Thank you for your contribution.",
autoHide ? { [weak self] in self?.closeView() } : nil)
}
}.resume()
}
private func banner(_ style: NotificationBanner.Style, _ msg: String, _ closure: (() -> Void)? = nil) {
NotificationBanner(msg, style: style).present(in: self, onClose: closure)
}
private func showOpenResultsAlert(_ urlStr: String, when: Int?) {
var msg = "Your contribution is being processed and will be available "
if let when = when {
if when < 61 {
msg += "in approx. \(when) sec. "
} else {
let fmt = TimeFormat.from(Timestamp(when))
msg += "in \(fmt) min. "
}
} else {
msg += "shortly. "
}
msg += "Open results webpage now?"
let alert = Alert(title: "Thank you", text: msg, buttonText: "Not now")
alert.addAction(UIAlertAction(title: "Show results", style: .default) { _ in
if let url = URL(string: urlStr) {
UIApplication.shared.openURL(url)
}
})
alert.presentIn(self)
}
}

View File

@@ -84,6 +84,31 @@ br.vet
br.vlog
br.wiki
br.zlg
co.a
co.b
co.com
co.edu
co.g
co.gov
co.inf
co.m
co.mil
co.net
co.ngo
co.nom
co.o
co.org
co.s
co.t
co.x
co.y
er.com
er.edu
er.gov
er.mil
er.net
er.org
er.ind
es.com
es.edu
es.gob
@@ -553,4 +578,4 @@ za.nom
za.org
za.school
za.tm
za.web
za.web