Skip to content

Commit

Permalink
Merge pull request #7 from BiAffect/syoung/keyboard-schema
Browse files Browse the repository at this point in the history
JSON Schema updates
  • Loading branch information
faraz-hussain authored Jul 25, 2022
2 parents c6f62bc + 97f025e commit abfcb67
Show file tree
Hide file tree
Showing 12 changed files with 5,926 additions and 23 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@ jobs:
- name: Run tests for macOS
run: swift test -v
- name: Build for iOS
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -scheme BiAffectSDK -destination "platform=iOS Simulator,OS=15.2,name=iPhone 12" | xcpretty
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild build-for-testing -scheme BiAffectSDK-Package -destination "platform=iOS Simulator,OS=15.2,name=iPhone 12" | xcpretty
- name: Run tests for iOS
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -scheme BiAffectSDK -destination "platform=iOS Simulator,OS=15.2,name=iPhone 12" | xcpretty
run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild test-without-building -scheme BiAffectSDK-Package -destination "platform=iOS Simulator,OS=15.2,name=iPhone 12" | xcpretty
15 changes: 15 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ let package = Package(
.library(
name: "BiAffectSDK",
targets: ["BiAffectSDK"]),
.library(
name: "KeyLogger",
targets: ["KeyLogger"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
Expand Down Expand Up @@ -48,5 +51,17 @@ let package = Package(
.testTarget(
name: "BiAffectSDKTests",
dependencies: ["BiAffectSDK"]),

.target(
name: "KeyLogger",
dependencies: [
.product(name: "JsonModel", package: "JsonModel"),
]),
.testTarget(
name: "KeyLoggerTests",
dependencies: [
"KeyLogger",
.product(name: "JsonModel", package: "JsonModel"),
]),
]
)
114 changes: 94 additions & 20 deletions Sources/BiAffectSDK/GoNoGo/ShakeMotionSensor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ import JsonModel
import CoreMotion
#endif

fileprivate let recordSchema = DocumentableRootArray(rootDocumentType: MotionRecord.self,
jsonSchema: .init(string: "ShakeSample.json", relativeTo: kBaseJsonSchemaURL)!,
fileprivate let recordSchema = DocumentableRootArray(rootDocumentType: MotionSample.self,
jsonSchema: .init(string: "MotionSample.json", relativeTo: kBaseJsonSchemaURL)!,
documentDescription: "A list of motion sensor records.")

fileprivate let motionRecorderConfig = MotionRecorderConfigurationObject(identifier: "motion",
Expand Down Expand Up @@ -141,23 +141,6 @@ final class ShakeMotionSensor : MotionRecorder {
}

override var schemaDoc: DocumentableRootArray? { recordSchema }

struct ShakeSample : SampleRecord, Codable, Hashable {
private enum CodingKeys : String, OrderedEnumCodingKey {
case stepPath, uptime, timestamp, timestampDate, sensorType, x, y, z, vectorMagnitude
}

let stepPath: String
let uptime: ClockUptime
let timestamp: SecondDuration?
let sensorType: MotionRecorderType?
let x: Double?
let y: Double?
let z: Double?
let vectorMagnitude: Double?

private(set) var timestampDate: Date? = nil
}

#if os(iOS)

Expand All @@ -168,7 +151,7 @@ final class ShakeMotionSensor : MotionRecorder {
await onMotionReceived(vectorMagnitude, timestamp: data.timestamp)
}
return [
ShakeSample(stepPath: stepPath,
MotionSample(stepPath: stepPath,
uptime: uptime,
timestamp: timestamp,
sensorType: .userAcceleration,
Expand All @@ -195,3 +178,94 @@ extension UIWindow {
}
}
#endif

struct MotionSample : SampleRecord, Codable, Hashable {
private enum CodingKeys : String, OrderedEnumCodingKey {
case stepPath, uptime, timestamp, timestampDate, sensorType, x, y, z, vectorMagnitude
}

let stepPath: String
let uptime: ClockUptime
let timestamp: SecondDuration?
let sensorType: MotionRecorderType?
let x: Double?
let y: Double?
let z: Double?
let vectorMagnitude: Double?

private(set) var timestampDate: Date? = nil
}

extension MotionSample : DocumentableStruct {
public static func codingKeys() -> [CodingKey] {
return CodingKeys.allCases
}

public static func isRequired(_ codingKey: CodingKey) -> Bool {
(codingKey as? CodingKeys) == CodingKeys.stepPath
}

public static func documentProperty(for codingKey: CodingKey) throws -> DocumentProperty {
guard let key = codingKey as? CodingKeys else {
throw DocumentableError.invalidCodingKey(codingKey, "\(codingKey) is not recognized for this class")
}
switch key {
case .uptime:
return .init(propertyType: .primitive(.number), propertyDescription: "System clock time.")
case .timestamp:
return .init(propertyType: .primitive(.number), propertyDescription: "Time that the system has been awake since last reboot.")
case .stepPath:
return .init(propertyType: .primitive(.string), propertyDescription: "An identifier marking the current step.")
case .timestampDate:
return .init(propertyType: .format(.dateTime), propertyDescription: "The date timestamp when the measurement was taken (if available).")
case .sensorType:
return .init(propertyType: .reference(MotionRecorderType.documentableType()), propertyDescription: "The sensor type for this record sample.")
case .x:
return .init(propertyType: .primitive(.number), propertyDescription: "The `x` component of the vector measurement for this sensor sample.")
case .y:
return .init(propertyType: .primitive(.number), propertyDescription: "The `y` component of the vector measurement for this sensor sample.")
case .z:
return .init(propertyType: .primitive(.number), propertyDescription: "The `z` component of the vector measurement for this sensor sample.")
case .vectorMagnitude:
return .init(propertyType: .primitive(.number), propertyDescription: "The calculated vector magnitude used to determine whether or not the device was shaken. (`sensorType==userAcceleration`)")
}
}

public static func examples() -> [MotionSample] {
let json = """
[
{
"uptime" : 1350727.0347595001,
"timestamp" : 0.22008016658946872,
"stepPath" : "attempt/1/showing/none",
"sensorType" : "gyro",
"x" : 0.043036416172981262,
"y" : -0.038228876888751984,
"z" : 0.012300875037908554
},
{
"uptime" : 1350727.0347595001,
"timestamp" : 0.22008016658946872,
"stepPath" : "attempt/1/showing/none",
"sensorType" : "accelerometer",
"x" : 0.0980987548828125,
"y" : -0.483734130859375,
"z" : -0.8672332763671875
},
{
"stepPath" : "attempt/1/showing/none",
"uptime" : 1350727.0408015,
"timestamp" : 0.22612216649577022,
"sensorType" : "userAcceleration",
"x" : 0.001909300684928894,
"y" : -0.0058466494083404541,
"z" : 0.0059053897857666016,
"vectorMagnitude" : 0.008526568297466116
}
]
""".data(using: .utf8)! // our data in native (JSON) format
let decoder = BiAffectFactory().createJSONDecoder()
return try! decoder.decode([MotionSample].self, from: json)
}
}

178 changes: 178 additions & 0 deletions Sources/KeyLogger/Models/Keylog.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//
// Keylog.swift
//
// Created by Andrea Piscitello on 17/10/16.
//
// Copyright © 2016, 2022 BiAffect. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation and/or
// other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder(s) nor the names of any contributors
// may be used to endorse or promote products derived from this software without
// specific prior written permission. No license is granted to the trademarks of
// the copyright holders even if such marks are included in this software.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

import Foundation
import SwiftUI

/// Data structure containing all the information linked with a key pression
public class Keylog : Codable {
private enum CodingKeys : String, CodingKey {
case uptime, _timestamp = "timestamp", value, duration, distanceFromPrevious, distanceFromCenter, force, radius
}

/// System clock time
public let uptime: TimeInterval

/// Time instant of the touch up event (when the finger is raised).
public var timestamp: Date {
get { Date(timeIntervalSince1970: _timestamp) }
set { _timestamp = newValue.timeIntervalSince1970 }
}
internal var _timestamp: TimeInterval

/// Description of the key pressed. It can be either a KeyType or a particular key that is recorded.
public var keyType: KeyType { .init(rawValue: value) ?? .other }
private let value: String

/// Residence Time. Time interval the finger is kept on the key.
public var duration: TimeInterval?

/// Distance (in universal size called gridpoint which is independent of screen resolutions) of touch from the previous KeyLog touch position.
public var distanceFromPrevious: Double?

/// Distance (in universal size called gridpoint which is independent of screen resolutions) of touch from key center.
public var distanceFromCenter: Double?

/// Force of touch.
public var force: Force?

/// Radius of touch.
public var radius: Radius?

/**
Coordinates of the touch in the keyboard.
It is used just to compute distances between two consecutive touch events.
** It should never be serialized and sent to not compromise text privacy **
*/
public var coordinates: CGPoint? = nil

public convenience init(key: String, timestamp: Date = Date()) {
self.init(value: .init(key: key), timestamp: timestamp)
}

public init(value: KeyType, timestamp: Date = Date()) {
let now = Date()
let nowUptime = ProcessInfo.processInfo.systemUptime
self.uptime = nowUptime - now.timeIntervalSince(timestamp)
self._timestamp = timestamp.timeIntervalSince1970
self.value = value.rawValue
}

/// Compute distance between this keylog touch coordinates and the previous one passed as parameter.
/// - parameter from: Touch position of the previous keylog.
public func setDistance(from point: CGPoint?) {
guard let coordinates = coordinates, let point = point else {
return
}
self.distanceFromPrevious = Utils.distance(coordinates, b: point)
}

/// General types of key which is pressed
public enum KeyType : String {
/// It represents a character or number.
case alphanum

/// It can be a point, a comma or another special character.
case punctuation

/// Emoji
case emoji

/// Backspace
case backspace

/// Suggestion
case suggestion

/// Autocorrection
case autocorrection

/// Special-case characters
case at = "@", hashtag = "#"

/// Default base case; it should never occur.
case other

public init(key: String) {
guard let char = key.unicodeScalars.first else {
self = .other
return
}

if CharacterSet.alphanumerics.contains(char) {
self = .alphanum
}
else if key == "@" {
self = .at
}
else if key == "#" {
self = .hashtag
}
else if CharacterSet.symbols.contains(char) {
self = .punctuation
}
else {
self = .other
}
}
}

/// Touch force sensed by 3DTouch sensor.
public struct Force : Codable {
public let value: Double
public let max: Double

/// - Parameters:
/// - value: Actual force sensed as conventional value.
/// - max: Maximum possible force that can be sensed by the sensor.
public init(value: Double, max: Double) {
self.value = value
self.max = max
}
}

/// Radius of the touch surface.
public struct Radius : Codable {
public private(set) var value: Double
public private(set) var tolerance: Double

/// - Parameters:
/// - value: Actual radius of the touch surface (in universal size called gridpoint which is independent of screen resolutions).
/// - tolerance: Srandard deviation.
public init(value: Double, tolerance: Double) {
self.value = value
self.tolerance = tolerance
}
}
}
Loading

0 comments on commit abfcb67

Please sign in to comment.